diff --git a/CMakeLists.txt b/CMakeLists.txt index 48822ec5..6a055d03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,7 @@ set( src/net/quic/quic_socket_address_coder.cc src/net/quic/quic_utils.cc src/net/quic/quic_frame_list.cc + src/net/quic/spdy_utils.cc src/net/quic/crypto/quic_crypto_server_config.cc src/net/quic/crypto/crypto_handshake_message.cc src/net/quic/crypto/p256_key_exchange_openssl.cc @@ -122,21 +123,23 @@ set( src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc src/net/quic/crypto/quic_encrypter.cc src/net/quic/crypto/quic_crypto_client_config.cc + src/net/quic/crypto/proof_source.cc src/net/quic/quic_clock.cc src/net/quic/quic_alarm.cc src/net/quic/quic_sent_entropy_manager.cc src/net/quic/congestion_control/pacing_sender.cc - src/net/quic/congestion_control/time_loss_algorithm.cc src/net/quic/congestion_control/loss_detection_interface.cc src/net/quic/congestion_control/general_loss_algorithm.cc src/net/quic/congestion_control/send_algorithm_interface.cc src/net/quic/congestion_control/cubic.cc src/net/quic/congestion_control/cubic_bytes.cc - src/net/quic/congestion_control/tcp_loss_algorithm.cc src/net/quic/congestion_control/prr_sender.cc - src/net/quic/congestion_control/tcp_cubic_sender.cc src/net/quic/congestion_control/hybrid_slow_start.cc src/net/quic/congestion_control/rtt_stats.cc + src/net/quic/congestion_control/tcp_cubic_sender.cc + src/net/quic/congestion_control/tcp_cubic_sender_base.cc + src/net/quic/congestion_control/tcp_cubic_sender_bytes.cc + src/net/quic/congestion_control/tcp_cubic_sender_packets.cc src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc src/net/quic/quic_config.cc src/net/quic/quic_crypto_server_stream.cc @@ -147,6 +150,8 @@ set( src/net/quic/quic_packet_creator.cc src/net/quic/quic_sustained_bandwidth_recorder.cc src/net/quic/quic_client_session_base.cc + src/net/quic/quic_client_promised_info.cc + src/net/quic/quic_client_push_promise_index.cc src/net/quic/quic_crypto_client_stream.cc src/net/quic/quic_server_id.cc src/net/quic/quic_simple_buffer_allocator.cc @@ -155,13 +160,16 @@ set( src/net/quic/stream_sequencer_buffer.cc src/net/base/int128.cc + src/net/base/escape.cc src/net/base/io_buffer.cc - src/net/base/net_util.cc src/net/base/net_errors.cc + src/net/base/ip_address.cc + src/net/base/ip_address_number.cc src/net/base/ip_endpoint.cc src/net/base/host_port_pair.cc - src/net/base/port_util.cc src/net/base/address_family.cc + src/net/base/port_util.cc + src/net/base/url_util.cc src/net/spdy/spdy_frame_reader.cc src/net/spdy/spdy_frame_builder.cc @@ -175,6 +183,7 @@ set( src/net/spdy/hpack/hpack_entry.cc src/net/spdy/hpack/hpack_header_table.cc src/net/spdy/hpack/hpack_huffman_table.cc + src/net/spdy/hpack/hpack_huffman_decoder.cc src/net/spdy/hpack/hpack_input_stream.cc src/net/spdy/hpack/hpack_output_stream.cc src/net/spdy/hpack/hpack_static_table.cc @@ -256,6 +265,7 @@ set( src/base/strings/string16.cc src/base/strings/utf_string_conversion_utils.cc src/base/strings/utf_string_conversions.cc + src/base/strings/utf_offset_string_conversions.cc src/base/pickle.cc src/base/debug/alias.cc src/base/debug/stack_trace.cc @@ -269,7 +279,6 @@ set( src/base/lazy_instance.cc src/base/callback_internal.cc src/base/base_switches.cc - src/base/os_compat_nacl.cc src/base/values.cc src/base/at_exit.cc src/base/process/process_handle_posix.cc @@ -279,14 +288,16 @@ set( src/base/base64.cc src/base/md5.cc src/base/metrics/statistics_recorder.cc - src/base/metrics/histogram_base.cc src/base/metrics/sample_map.cc - src/base/metrics/histogram.cc src/base/metrics/sample_vector.cc + src/base/metrics/histogram_base.cc + src/base/metrics/histogram.cc + src/base/metrics/histogram_persistence.cc src/base/metrics/histogram_samples.cc - src/base/metrics/bucket_ranges.cc src/base/metrics/sparse_histogram.cc + src/base/metrics/bucket_ranges.cc src/base/metrics/metrics_hashes.cc + src/base/metrics/persistent_memory_allocator.cc src/base/rand_util.cc src/base/files/file_path_constants.cc src/base/files/file_path.cc @@ -318,6 +329,23 @@ set( URL_SOURCES src/url/url_constants.cc + src/url/url_canon_etc.cc + src/url/url_canon_filesystemurl.cc + src/url/url_canon_fileurl.cc + src/url/url_canon_host.cc + src/url/url_canon_internal.cc + src/url/url_canon_ip.cc + src/url/url_canon_mailtourl.cc + src/url/url_canon_path.cc + src/url/url_canon_pathurl.cc + src/url/url_canon_query.cc + src/url/url_canon_relative.cc + src/url/url_canon_stdstring.cc + src/url/url_canon_stdurl.cc + src/url/url_util.cc + src/url/url_parse_file.cc + src/url/gurl.cc + src/url/third_party/mozilla/url_parse.cc ) add_library( @@ -329,8 +357,6 @@ add_library( ${CRYPTO_SOURCES} ${URL_SOURCES} - src/stubs.cc - src/third_party/modp_b64/modp_b64.cc src/third_party/zlib/gzwrite.c src/third_party/zlib/crc32.c diff --git a/DEPS b/DEPS index 9348a112..e462b4a6 100644 --- a/DEPS +++ b/DEPS @@ -3,16 +3,18 @@ # START # { - "chromium_revision": "b25d2d936da7620ff44ce4a2952f38402efad685", + "chromium_revision": "6e3a05d6080ba6ae25af2835573ff9e2406b90c0", "automatic_dependency": [ { "from": "net/quic/quic_connection.cc", "exclude": [ "cached_network_parameters.pb.h", - "net/base/net_util.h", "base/debug/debugger.h", "base/sequence_checker.h", - "base/files/file.h" + "base/files/file.h", + "base/feature_list.h", + "net/base/registry_controlled_domains/registry_controlled_domain.h", + "base/metrics/field_trial.h" ] }, { @@ -45,19 +47,20 @@ "from": "net/quic/quic_session.cc", "exclude": [ "cached_network_parameters.pb.h", - "net/base/net_util.h", "base/debug/debugger.h", "base/sequence_checker.h", "base/files/file.h", "net/ssl/ssl_info.h", - "net/spdy/spdy_header_block.h" + "net/spdy/spdy_header_block.h", + "base/feature_list.h", + "net/base/registry_controlled_domains/registry_controlled_domain.h", + "base/metrics/field_trial.h" ] }, { "from": "net/quic/quic_client_session_base.cc", "exclude": [ "cached_network_parameters.pb.h", - "net/base/net_util.h", "base/debug/debugger.h", "base/sequence_checker.h", "base/files/file.h", @@ -66,14 +69,16 @@ "net/ssl/ssl_info.h", "net/spdy/spdy_header_block.h", "base/cpu.h", - "net/base/host_port_pair.h" + "net/base/host_port_pair.h", + "base/feature_list.h", + "net/base/registry_controlled_domains/registry_controlled_domain.h", + "base/metrics/field_trial.h" ] }, { "from": "net/quic/quic_crypto_client_stream.cc", "exclude": [ "cached_network_parameters.pb.h", - "net/base/net_util.h", "base/debug/debugger.h", "base/sequence_checker.h", "base/files/file.h", @@ -82,7 +87,10 @@ "net/ssl/ssl_info.h", "net/spdy/spdy_header_block.h", "base/cpu.h", - "net/base/host_port_pair.h" + "net/base/host_port_pair.h", + "base/feature_list.h", + "net/base/registry_controlled_domains/registry_controlled_domain.h", + "base/metrics/field_trial.h" ] }, { @@ -90,7 +98,6 @@ "exclude": [ "cached_network_parameters.pb.h", "source_address_token.pb.h", - "net/base/net_util.h", "base/debug/debugger.h", "base/sequence_checker.h", "base/files/file.h", @@ -99,7 +106,10 @@ "net/spdy/spdy_header_block.h", "modp_b64.h", "modp_b64_data.h", - "base/cpu.h" + "base/cpu.h", + "base/feature_list.h", + "net/base/registry_controlled_domains/registry_controlled_domain.h", + "base/metrics/field_trial.h" ] }, { @@ -159,12 +169,11 @@ "base/synchronization/condition_variable.h", "base/synchronization/condition_variable_posix.cc", "base/files/file_path_constants.cc", + "base/files/file_util_posix.cc", "base/process/process_handle_posix.cc", "net/base/io_buffer.h", "net/base/io_buffer.cc", "net/base/completion_callback.h", - "net/base/net_util.h", - "net/base/net_util.cc", "net/base/host_port_pair.h", "net/base/host_port_pair.cc", "net/quic/quic_simple_buffer_allocator.h", @@ -211,8 +220,6 @@ "crypto/secure_hash_default.cc", "crypto/curve25519-donna.c", "crypto/curve25519_openssl.cc", - "crypto/ghash.cc", - "crypto/ghash.h", "base/memory/scoped_vector.h", "third_party/modp_b64/modp_b64_data.h", "base/mac/mach_logging.cc", @@ -221,11 +228,14 @@ "base/mac/scoped_mach_port.h", "base/scoped_generic.h", "base/time/time_mac.cc", + "base/feature_list.h", "third_party/zlib/*.c", "third_party/zlib/*.h", "crypto/third_party/nss/*.c", "crypto/third_party/nss/*.cc", - "crypto/third_party/nss/*.h" + "crypto/third_party/nss/*.h", + "url/url_canon_*.cc", + "url/url_parse_file.cc" ] }, { @@ -245,12 +255,19 @@ } ], "patches": [ - "patch/basepatch.patch", - "patch/freebsd.patch" + "patch/disable_sequence_checker.patch", + "patch/net_errors_remove_file_error.patch", + "patch/quic_session_remove_unused_include.patch", + "patch/pickle_remove_unused_include.patch", + "patch/platform_thread_remove_tracked_objects.patch", + "patch/rand_util_posix.patch", + "patch/persistent_memory_allocator_disable_sharing.patch", + "patch/spdy_header_block_remove_unused_callback.patch", + "patch/url_canon_host_disable_idn_host.patch", + "patch/url_util_remove_unused_function.patch", + "patch/disable_stack_trace.patch" ], "custom_files": [ - {"from": "custom/net_util.h", "to": "net/base/net_util.h"}, - {"from": "custom/net_util.cc", "to": "net/base/net_util.cc"}, {"from": "custom/debugger.h", "to": "base/debug/debugger.h"}, {"from": "custom/debugger.cc", "to": "base/debug/debugger.cc"}, {"from": "custom/stubs.cc", "to": "stubs.cc"} diff --git a/Makefile b/Makefile index a52d1841..0758e722 100644 --- a/Makefile +++ b/Makefile @@ -1,251 +1,7752 @@ -CC=g++ -AR=ar -C=gcc -CFLAGS=-Wall -Isrc -Isrc/third_party/modp_b64 -DUSE_OPENSSL=1 -Iboringssl/include -g -gdwarf-4 -CPPFLAGS=--std=gnu++11 -LDFLAGS=-pthread -Lboringssl/build/crypto -Lboringssl/build/ssl -LDLIBS=-lssl -lcrypto -CHROMIUM=/home/hodduc/repos/chromium/src -SRCROOT=$(CURDIR)/src -CPP_FILES:=$(wildcard src/*/*.cc) $(wildcard src/*/*/*.cc) $(wildcard src/*/*/*/*.cc) -CPP_BASE_FILES:=$(CPP_FILES:src/%=%) -C_FILES:=$(wildcard src/*/*.c) $(wildcard src/*/*/*.c) $(wildcard src/*/*/*/*.c) -C_BASE_FILES:=$(C_FILES:src/%=%) -OBJ_FILES:=$(addprefix obj/,$(CPP_BASE_FILES:.cc=.o)) $(addprefix obj/,$(C_BASE_FILES:.c=.o)) obj/base/third_party/superfasthash/superfasthash.o obj/crypto/curve25519-donna.o -EXE_FILE=build/exe -LIB_FILE=build/libquic.a - -all: init $(OBJ_FILES) $(EXE_FILE) $(LIB_FILE) - -$(EXE_FILE): obj/main.o $(LIB_FILE) boringssl/build/.builded - $(CC) $(LDFLAGS) -o $@ $< $(LIB_FILE) $(LDLIBS) - -obj/main.o: custom/main.cc - $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< - -$(LIB_FILE): $(OBJ_FILES) - $(AR) rvs $@ $(OBJ_FILES) - -boringssl/build/.builded: - mkdir -p boringssl/build - cd boringssl/build && cmake -GNinja .. && ninja - touch boringssl/build/.builded - -obj/net/base/net_util_linux.o obj/url/url_util.o obj/base/win/scoped_handle.o obj/net/quic/crypto/common_cert_set_0.o obj/net/quic/crypto/common_cert_set_1.o: custom/empty.cc - mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< - -obj/base/third_party/superfasthash/superfasthash.o: src/base/third_party/superfasthash/superfasthash.c - mkdir -p $(dir $@) - $(C) -Wall -Isrc -c -o $@ $< - -obj/crypto/curve25519-donna.o: src/crypto/curve25519-donna.c - mkdir -p $(dir $@) - $(C) -Wall -Isrc -c -o $@ $< - -obj/%.o: src/%.c - mkdir -p $(dir $@) - $(C) $(CFLAGS) -c -o $@ $< - -obj/%.o: src/%.cc - mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< - -init: - mkdir -p obj build - -sync: - rm -rf src/* obj/* boringssl/build - python getdep.py /home/hodduc/repos/chromium/src net/quic/quic_connection.cc \ - --exclude net/base/net_util.h \ - --exclude base/debug/debugger.h \ - --exclude base/sequence_checker.h \ - --exclude base/files/file.h\ - --cmd | bash - # - python getdep.py /home/hodduc/repos/chromium/src base/threading/platform_thread_posix.cc \ - --exclude net/base/net_util.h \ - --exclude base/debug/debugger.h \ - --exclude base/sequence_checker.h \ - --exclude base/files/file.h\ - --exclude base/tracked_objects.h \ - --cmd | bash - - python getdep.py /home/hodduc/repos/chromium/src base/threading/platform_thread_linux.cc \ - --exclude net/base/net_util.h \ - --exclude base/debug/debugger.h \ - --exclude base/sequence_checker.h \ - --exclude base/files/file.h \ - --exclude base/tracked_objects.h \ - --cmd | bash - - python getdep.py /home/hodduc/repos/chromium/src net/quic/quic_session.cc \ - --exclude net/base/net_util.h \ - --exclude base/debug/debugger.h \ - --exclude base/sequence_checker.h \ - --exclude base/files/file.h \ - --exclude base/tracked_objects.h \ - --exclude base/third_party/valgrind/memcheck.h \ - --exclude zconf.h \ - --exclude net/ssl/ssl_info.h \ - --exclude base/metrics/stats_counters.h \ - --exclude url/url_canon.h \ - --exclude net/spdy/spdy_header_block.h \ - --cmd | bash - - python getdep.py /home/hodduc/repos/chromium/src net/quic/quic_client_session_base.cc \ - --exclude net/base/net_util.h \ - --exclude base/debug/debugger.h \ - --exclude base/sequence_checker.h \ - --exclude base/files/file.h \ - --exclude base/tracked_objects.h \ - --exclude base/third_party/valgrind/memcheck.h \ - --exclude zconf.h \ - --exclude net/ssl/ssl_info.h \ - --exclude base/metrics/stats_counters.h \ - --exclude url/url_canon.h \ - --exclude net/spdy/spdy_header_block.h \ - --exclude modp_b64.h \ - --exclude modp_b64_data.h \ - --exclude base/cpu.h \ - --exclude net/base/host_port_pair.h \ - --exclude base/profiler/scoped_tracker.h \ - --cmd | bash - - python getdep.py /home/hodduc/repos/chromium/src net/quic/quic_crypto_client_stream.cc \ - --exclude net/base/net_util.h \ - --exclude base/debug/debugger.h \ - --exclude base/sequence_checker.h \ - --exclude base/files/file.h \ - --exclude base/tracked_objects.h \ - --exclude base/third_party/valgrind/memcheck.h \ - --exclude zconf.h \ - --exclude net/ssl/ssl_info.h \ - --exclude base/metrics/stats_counters.h \ - --exclude url/url_canon.h \ - --exclude net/spdy/spdy_header_block.h \ - --exclude modp_b64.h \ - --exclude modp_b64_data.h \ - --exclude base/cpu.h \ - --exclude net/base/host_port_pair.h \ - --exclude base/profiler/scoped_tracker.h \ - --cmd | bash - - python getdep.py /home/hodduc/repos/chromium/src net/quic/quic_crypto_server_stream.cc \ - --exclude net/base/net_util.h \ - --exclude base/debug/debugger.h \ - --exclude base/sequence_checker.h \ - --exclude base/files/file.h \ - --exclude base/tracked_objects.h \ - --exclude base/third_party/valgrind/memcheck.h \ - --exclude zconf.h \ - --exclude net/ssl/ssl_info.h \ - --exclude base/metrics/stats_counters.h \ - --exclude url/url_canon.h \ - --exclude net/spdy/spdy_header_block.h \ - --exclude modp_b64.h \ - --exclude modp_b64_data.h \ - --exclude base/cpu.h \ - --cmd | bash - # "third_party/modp_b64.cc" uses relative import. so we should exclude this and add -I option to CFLAGS - - rm -f src/base/os_compat_android.* - - cp $(CHROMIUM)/base/atomicops_internals_portable.* $(SRCROOT)/base/ - cp $(CHROMIUM)/base/atomicops_internals_x86_gcc.* $(SRCROOT)/base/ - cp $(CHROMIUM)/base/callback_helpers.h $(SRCROOT)/base/ - cp $(CHROMIUM)/base/callback_helpers.cc $(SRCROOT)/base/ - cp $(CHROMIUM)/base/cancelable_callback.h $(SRCROOT)/base/ - cp $(CHROMIUM)/base/rand_util_posix.cc $(SRCROOT)/base/ - cp $(CHROMIUM)/base/sequence_checker.h $(SRCROOT)/base/ - cp $(CHROMIUM)/base/debug/debugger.h $(SRCROOT)/base/debug/ - cp $(CHROMIUM)/base/debug/debugger.cc $(SRCROOT)/base/debug/ - cp $(CHROMIUM)/base/time/time_posix.cc $(SRCROOT)/base/time/ - cp $(CHROMIUM)/base/third_party/dmg_fp/dtoa.cc $(SRCROOT)/base/third_party/dmg_fp/ - cp $(CHROMIUM)/base/third_party/dmg_fp/g_fmt.cc $(SRCROOT)/base/third_party/dmg_fp/ - mkdir -p $(SRCROOT)/base/third_party/superfasthash/ - cp $(CHROMIUM)/base/third_party/superfasthash/superfasthash.c $(SRCROOT)/base/third_party/superfasthash/ - cp $(CHROMIUM)/base/strings/sys_string_conversions_posix.cc $(SRCROOT)/base/strings/ - cp $(CHROMIUM)/base/strings/string_util_constants.cc $(SRCROOT)/base/strings/ - cp $(CHROMIUM)/base/threading/thread_local_storage_posix.cc $(SRCROOT)/base/threading/ - cp $(CHROMIUM)/base/threading/thread_local_posix.cc $(SRCROOT)/base/threading/ - cp $(CHROMIUM)/base/threading/platform_thread_mac.mm $(SRCROOT)/base/threading/ - cp $(CHROMIUM)/base/synchronization/lock_impl_posix.cc $(SRCROOT)/base/synchronization/ - cp $(CHROMIUM)/base/synchronization/waitable_event_posix.cc $(SRCROOT)/base/synchronization/ - cp $(CHROMIUM)/base/synchronization/condition_variable.h $(SRCROOT)/base/synchronization/ - cp $(CHROMIUM)/base/synchronization/condition_variable_posix.cc $(SRCROOT)/base/synchronization/ - cp $(CHROMIUM)/base/files/file_path_constants.cc $(SRCROOT)/base/files/ - cp $(CHROMIUM)/base/process/process_handle_posix.cc $(SRCROOT)/base/process/ - cp $(CHROMIUM)/net/base/io_buffer.h $(SRCROOT)/net/base/ - cp $(CHROMIUM)/net/base/io_buffer.cc $(SRCROOT)/net/base/ - cp $(CHROMIUM)/net/base/completion_callback.h $(SRCROOT)/net/base/ - cp $(CHROMIUM)/net/base/net_util.h $(SRCROOT)/net/base/ - cp $(CHROMIUM)/net/base/net_util.cc $(SRCROOT)/net/base/ - cp $(CHROMIUM)/net/base/host_port_pair.h $(SRCROOT)/net/base/ - cp $(CHROMIUM)/net/base/host_port_pair.cc $(SRCROOT)/net/base/ - cp $(CHROMIUM)/net/quic/crypto/chacha20_poly1305_decrypter.h $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/chacha20_poly1305_encrypter.h $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/aes_128_gcm_12_decrypter.h $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/aes_128_gcm_12_encrypter.h $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/aead_base_decrypter.h $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/aead_base_decrypter_openssl.cc $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/aead_base_encrypter.h $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/aead_base_encrypter_openssl.cc $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/p256_key_exchange_openssl.cc $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/net/quic/crypto/channel_id_openssl.cc $(SRCROOT)/net/quic/crypto/ - cp $(CHROMIUM)/crypto/hmac_openssl.cc $(SRCROOT)/crypto/ - cp $(CHROMIUM)/crypto/symmetric_key_openssl.cc $(SRCROOT)/crypto/ - cp $(CHROMIUM)/crypto/openssl_util.h $(SRCROOT)/crypto/ - cp $(CHROMIUM)/crypto/openssl_util.cc $(SRCROOT)/crypto/ - cp $(CHROMIUM)/crypto/secure_hash_openssl.cc $(SRCROOT)/crypto/ - cp $(CHROMIUM)/crypto/curve25519-donna.c $(SRCROOT)/crypto/ - cp $(CHROMIUM)/base/memory/scoped_vector.h $(SRCROOT)/base/memory/ - cp $(CHROMIUM)/third_party/modp_b64/modp_b64_data.h $(SRCROOT)/third_party/modp_b64/ - - # MAC support - cp $(CHROMIUM)/base/mac/mach_logging.cc $(SRCROOT)/base/mac/ - cp $(CHROMIUM)/base/mac/mach_logging.h $(SRCROOT)/base/mac/ - cp $(CHROMIUM)/base/mac/scoped_mach_port.cc $(SRCROOT)/base/mac/ - cp $(CHROMIUM)/base/mac/scoped_mach_port.h $(SRCROOT)/base/mac/ - cp $(CHROMIUM)/base/scoped_generic.h $(SRCROOT)/base/ - cp $(CHROMIUM)/base/time/time_mac.cc $(SRCROOT)/base/time/ - - cp -r $(CHROMIUM)/third_party/zlib/*.c $(SRCROOT)/third_party/zlib - cp -r $(CHROMIUM)/third_party/zlib/*.h $(SRCROOT)/third_party/zlib - rm $(SRCROOT)/third_party/zlib/crc_folding.c - rm $(SRCROOT)/third_party/zlib/fill_window_sse.c - rm $(SRCROOT)/third_party/zlib/x86.c - - #cp custom/net_util.h $(SRCROOT)/net/base/net_util.h - #cp custom/net_util.cc $(SRCROOT)/net/base/net_util.cc - #cp custom/debugger.h $(SRCROOT)/base/debug/debugger.h - #cp custom/debugger.cc $(SRCROOT)/base/debug/debugger.cc - #cp custom/sequence_checker.h $(SRCROOT)/base/sequence_checker.h - #cp custom/net_errors.h $(SRCROOT)/net/base/net_errors.h - #cp custom/net_errors.cc $(SRCROOT)/net/base/net_errors.cc - #cp custom/rand_util_posix.cc $(SRCROOT)/base/rand_util_posix.cc - #cp custom/stack_trace.cc $(SRCROOT)/base/debug/stack_trace.cc - #cp custom/platform_thread_posix.cc $(SRCROOT)/base/threading/platform_thread_posix.cc - #cp custom/platform_thread_linux.cc $(SRCROOT)/base/threading/platform_thread_linux.cc - #cp custom/platform_thread_mac.mm $(SRCROOT)/base/threading/platform_thread_mac.mm - #cp custom/quic_session.h $(SRCROOT)/net/quic/quic_session.h - #cp custom/quic_session.cc $(SRCROOT)/net/quic/quic_session.cc - #cp custom/quic_data_stream.cc $(SRCROOT)/net/quic/quic_data_stream.cc - #cp custom/spdy_framer.h $(SRCROOT)/net/spdy/spdy_framer.h - #cp custom/spdy_framer.cc $(SRCROOT)/net/spdy/spdy_framer.cc - #cp custom/crypto_utils.h $(SRCROOT)/net/quic/crypto/crypto_utils.h - #cp custom/crypto_utils.cc $(SRCROOT)/net/quic/crypto/crypto_utils.cc - #cp custom/host_port_pair.h $(SRCROOT)/net/base/host_port_pair.h - #cp custom/host_port_pair.cc $(SRCROOT)/net/base/host_port_pair.cc - #cp custom/quic_crypto_client_stream.cc $(SRCROOT)/net/quic/quic_crypto_client_stream.cc - - patch -p1 < basepatch.patch - patch -p1 < patch_remove_scoped_tracker.patch - cp custom/net_util.h $(SRCROOT)/net/base/net_util.h - cp custom/net_util.cc $(SRCROOT)/net/base/net_util.cc - cp custom/debugger.h $(SRCROOT)/base/debug/debugger.h - cp custom/debugger.cc $(SRCROOT)/base/debug/debugger.cc +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.4 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/local/Cellar/cmake/3.4.1/bin/cmake + +# The command to remove a file. +RM = /usr/local/Cellar/cmake/3.4.1/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /Users/hodduc/go/src/github.com/devsisters/goquic/libquic + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /Users/hodduc/go/src/github.com/devsisters/goquic/libquic + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake cache editor..." + /usr/local/Cellar/cmake/3.4.1/bin/ccmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/local/Cellar/cmake/3.4.1/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# The main all target +all: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /Users/hodduc/go/src/github.com/devsisters/goquic/libquic/CMakeFiles /Users/hodduc/go/src/github.com/devsisters/goquic/libquic/CMakeFiles/progress.marks + $(MAKE) -f CMakeFiles/Makefile2 all + $(CMAKE_COMMAND) -E cmake_progress_start /Users/hodduc/go/src/github.com/devsisters/goquic/libquic/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + $(MAKE) -f CMakeFiles/Makefile2 clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +#============================================================================= +# Target rules for targets named quic + +# Build rule for target. +quic: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 quic +.PHONY : quic + +# fast build rule for target. +quic/fast: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/build +.PHONY : quic/fast + +#============================================================================= +# Target rules for targets named all_tests + +# Build rule for target. +all_tests: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 all_tests +.PHONY : all_tests + +# fast build rule for target. +all_tests/fast: + $(MAKE) -f boringssl/CMakeFiles/all_tests.dir/build.make boringssl/CMakeFiles/all_tests.dir/build +.PHONY : all_tests/fast + +#============================================================================= +# Target rules for targets named run_tests + +# Build rule for target. +run_tests: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 run_tests +.PHONY : run_tests + +# fast build rule for target. +run_tests/fast: + $(MAKE) -f boringssl/CMakeFiles/run_tests.dir/build.make boringssl/CMakeFiles/run_tests.dir/build +.PHONY : run_tests/fast + +#============================================================================= +# Target rules for targets named constant_time_test + +# Build rule for target. +constant_time_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 constant_time_test +.PHONY : constant_time_test + +# fast build rule for target. +constant_time_test/fast: + $(MAKE) -f boringssl/crypto/CMakeFiles/constant_time_test.dir/build.make boringssl/crypto/CMakeFiles/constant_time_test.dir/build +.PHONY : constant_time_test/fast + +#============================================================================= +# Target rules for targets named crypto + +# Build rule for target. +crypto: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 crypto +.PHONY : crypto + +# fast build rule for target. +crypto/fast: + $(MAKE) -f boringssl/crypto/CMakeFiles/crypto.dir/build.make boringssl/crypto/CMakeFiles/crypto.dir/build +.PHONY : crypto/fast + +#============================================================================= +# Target rules for targets named refcount_test + +# Build rule for target. +refcount_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 refcount_test +.PHONY : refcount_test + +# fast build rule for target. +refcount_test/fast: + $(MAKE) -f boringssl/crypto/CMakeFiles/refcount_test.dir/build.make boringssl/crypto/CMakeFiles/refcount_test.dir/build +.PHONY : refcount_test/fast + +#============================================================================= +# Target rules for targets named thread_test + +# Build rule for target. +thread_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 thread_test +.PHONY : thread_test + +# fast build rule for target. +thread_test/fast: + $(MAKE) -f boringssl/crypto/CMakeFiles/thread_test.dir/build.make boringssl/crypto/CMakeFiles/thread_test.dir/build +.PHONY : thread_test/fast + +#============================================================================= +# Target rules for targets named stack + +# Build rule for target. +stack: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 stack +.PHONY : stack + +# fast build rule for target. +stack/fast: + $(MAKE) -f boringssl/crypto/stack/CMakeFiles/stack.dir/build.make boringssl/crypto/stack/CMakeFiles/stack.dir/build +.PHONY : stack/fast + +#============================================================================= +# Target rules for targets named lhash + +# Build rule for target. +lhash: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 lhash +.PHONY : lhash + +# fast build rule for target. +lhash/fast: + $(MAKE) -f boringssl/crypto/lhash/CMakeFiles/lhash.dir/build.make boringssl/crypto/lhash/CMakeFiles/lhash.dir/build +.PHONY : lhash/fast + +#============================================================================= +# Target rules for targets named lhash_test + +# Build rule for target. +lhash_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 lhash_test +.PHONY : lhash_test + +# fast build rule for target. +lhash_test/fast: + $(MAKE) -f boringssl/crypto/lhash/CMakeFiles/lhash_test.dir/build.make boringssl/crypto/lhash/CMakeFiles/lhash_test.dir/build +.PHONY : lhash_test/fast + +#============================================================================= +# Target rules for targets named err + +# Build rule for target. +err: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 err +.PHONY : err + +# fast build rule for target. +err/fast: + $(MAKE) -f boringssl/crypto/err/CMakeFiles/err.dir/build.make boringssl/crypto/err/CMakeFiles/err.dir/build +.PHONY : err/fast + +#============================================================================= +# Target rules for targets named err_test + +# Build rule for target. +err_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 err_test +.PHONY : err_test + +# fast build rule for target. +err_test/fast: + $(MAKE) -f boringssl/crypto/err/CMakeFiles/err_test.dir/build.make boringssl/crypto/err/CMakeFiles/err_test.dir/build +.PHONY : err_test/fast + +#============================================================================= +# Target rules for targets named buf + +# Build rule for target. +buf: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 buf +.PHONY : buf + +# fast build rule for target. +buf/fast: + $(MAKE) -f boringssl/crypto/buf/CMakeFiles/buf.dir/build.make boringssl/crypto/buf/CMakeFiles/buf.dir/build +.PHONY : buf/fast + +#============================================================================= +# Target rules for targets named base64 + +# Build rule for target. +base64: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 base64 +.PHONY : base64 + +# fast build rule for target. +base64/fast: + $(MAKE) -f boringssl/crypto/base64/CMakeFiles/base64.dir/build.make boringssl/crypto/base64/CMakeFiles/base64.dir/build +.PHONY : base64/fast + +#============================================================================= +# Target rules for targets named base64_test + +# Build rule for target. +base64_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 base64_test +.PHONY : base64_test + +# fast build rule for target. +base64_test/fast: + $(MAKE) -f boringssl/crypto/base64/CMakeFiles/base64_test.dir/build.make boringssl/crypto/base64/CMakeFiles/base64_test.dir/build +.PHONY : base64_test/fast + +#============================================================================= +# Target rules for targets named bytestring + +# Build rule for target. +bytestring: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 bytestring +.PHONY : bytestring + +# fast build rule for target. +bytestring/fast: + $(MAKE) -f boringssl/crypto/bytestring/CMakeFiles/bytestring.dir/build.make boringssl/crypto/bytestring/CMakeFiles/bytestring.dir/build +.PHONY : bytestring/fast + +#============================================================================= +# Target rules for targets named bytestring_test + +# Build rule for target. +bytestring_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 bytestring_test +.PHONY : bytestring_test + +# fast build rule for target. +bytestring_test/fast: + $(MAKE) -f boringssl/crypto/bytestring/CMakeFiles/bytestring_test.dir/build.make boringssl/crypto/bytestring/CMakeFiles/bytestring_test.dir/build +.PHONY : bytestring_test/fast + +#============================================================================= +# Target rules for targets named sha + +# Build rule for target. +sha: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 sha +.PHONY : sha + +# fast build rule for target. +sha/fast: + $(MAKE) -f boringssl/crypto/sha/CMakeFiles/sha.dir/build.make boringssl/crypto/sha/CMakeFiles/sha.dir/build +.PHONY : sha/fast + +#============================================================================= +# Target rules for targets named md4 + +# Build rule for target. +md4: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 md4 +.PHONY : md4 + +# fast build rule for target. +md4/fast: + $(MAKE) -f boringssl/crypto/md4/CMakeFiles/md4.dir/build.make boringssl/crypto/md4/CMakeFiles/md4.dir/build +.PHONY : md4/fast + +#============================================================================= +# Target rules for targets named md5 + +# Build rule for target. +md5: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 md5 +.PHONY : md5 + +# fast build rule for target. +md5/fast: + $(MAKE) -f boringssl/crypto/md5/CMakeFiles/md5.dir/build.make boringssl/crypto/md5/CMakeFiles/md5.dir/build +.PHONY : md5/fast + +#============================================================================= +# Target rules for targets named gcm_test + +# Build rule for target. +gcm_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 gcm_test +.PHONY : gcm_test + +# fast build rule for target. +gcm_test/fast: + $(MAKE) -f boringssl/crypto/modes/CMakeFiles/gcm_test.dir/build.make boringssl/crypto/modes/CMakeFiles/gcm_test.dir/build +.PHONY : gcm_test/fast + +#============================================================================= +# Target rules for targets named modes + +# Build rule for target. +modes: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 modes +.PHONY : modes + +# fast build rule for target. +modes/fast: + $(MAKE) -f boringssl/crypto/modes/CMakeFiles/modes.dir/build.make boringssl/crypto/modes/CMakeFiles/modes.dir/build +.PHONY : modes/fast + +#============================================================================= +# Target rules for targets named aes + +# Build rule for target. +aes: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 aes +.PHONY : aes + +# fast build rule for target. +aes/fast: + $(MAKE) -f boringssl/crypto/aes/CMakeFiles/aes.dir/build.make boringssl/crypto/aes/CMakeFiles/aes.dir/build +.PHONY : aes/fast + +#============================================================================= +# Target rules for targets named aes_test + +# Build rule for target. +aes_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 aes_test +.PHONY : aes_test + +# fast build rule for target. +aes_test/fast: + $(MAKE) -f boringssl/crypto/aes/CMakeFiles/aes_test.dir/build.make boringssl/crypto/aes/CMakeFiles/aes_test.dir/build +.PHONY : aes_test/fast + +#============================================================================= +# Target rules for targets named des + +# Build rule for target. +des: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 des +.PHONY : des + +# fast build rule for target. +des/fast: + $(MAKE) -f boringssl/crypto/des/CMakeFiles/des.dir/build.make boringssl/crypto/des/CMakeFiles/des.dir/build +.PHONY : des/fast + +#============================================================================= +# Target rules for targets named rc4 + +# Build rule for target. +rc4: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 rc4 +.PHONY : rc4 + +# fast build rule for target. +rc4/fast: + $(MAKE) -f boringssl/crypto/rc4/CMakeFiles/rc4.dir/build.make boringssl/crypto/rc4/CMakeFiles/rc4.dir/build +.PHONY : rc4/fast + +#============================================================================= +# Target rules for targets named conf + +# Build rule for target. +conf: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 conf +.PHONY : conf + +# fast build rule for target. +conf/fast: + $(MAKE) -f boringssl/crypto/conf/CMakeFiles/conf.dir/build.make boringssl/crypto/conf/CMakeFiles/conf.dir/build +.PHONY : conf/fast + +#============================================================================= +# Target rules for targets named chacha + +# Build rule for target. +chacha: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 chacha +.PHONY : chacha + +# fast build rule for target. +chacha/fast: + $(MAKE) -f boringssl/crypto/chacha/CMakeFiles/chacha.dir/build.make boringssl/crypto/chacha/CMakeFiles/chacha.dir/build +.PHONY : chacha/fast + +#============================================================================= +# Target rules for targets named poly1305 + +# Build rule for target. +poly1305: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 poly1305 +.PHONY : poly1305 + +# fast build rule for target. +poly1305/fast: + $(MAKE) -f boringssl/crypto/poly1305/CMakeFiles/poly1305.dir/build.make boringssl/crypto/poly1305/CMakeFiles/poly1305.dir/build +.PHONY : poly1305/fast + +#============================================================================= +# Target rules for targets named poly1305_test + +# Build rule for target. +poly1305_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 poly1305_test +.PHONY : poly1305_test + +# fast build rule for target. +poly1305_test/fast: + $(MAKE) -f boringssl/crypto/poly1305/CMakeFiles/poly1305_test.dir/build.make boringssl/crypto/poly1305/CMakeFiles/poly1305_test.dir/build +.PHONY : poly1305_test/fast + +#============================================================================= +# Target rules for targets named curve25519 + +# Build rule for target. +curve25519: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 curve25519 +.PHONY : curve25519 + +# fast build rule for target. +curve25519/fast: + $(MAKE) -f boringssl/crypto/curve25519/CMakeFiles/curve25519.dir/build.make boringssl/crypto/curve25519/CMakeFiles/curve25519.dir/build +.PHONY : curve25519/fast + +#============================================================================= +# Target rules for targets named ed25519_test + +# Build rule for target. +ed25519_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 ed25519_test +.PHONY : ed25519_test + +# fast build rule for target. +ed25519_test/fast: + $(MAKE) -f boringssl/crypto/curve25519/CMakeFiles/ed25519_test.dir/build.make boringssl/crypto/curve25519/CMakeFiles/ed25519_test.dir/build +.PHONY : ed25519_test/fast + +#============================================================================= +# Target rules for targets named x25519_test + +# Build rule for target. +x25519_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 x25519_test +.PHONY : x25519_test + +# fast build rule for target. +x25519_test/fast: + $(MAKE) -f boringssl/crypto/curve25519/CMakeFiles/x25519_test.dir/build.make boringssl/crypto/curve25519/CMakeFiles/x25519_test.dir/build +.PHONY : x25519_test/fast + +#============================================================================= +# Target rules for targets named digest + +# Build rule for target. +digest: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 digest +.PHONY : digest + +# fast build rule for target. +digest/fast: + $(MAKE) -f boringssl/crypto/digest/CMakeFiles/digest.dir/build.make boringssl/crypto/digest/CMakeFiles/digest.dir/build +.PHONY : digest/fast + +#============================================================================= +# Target rules for targets named digest_test + +# Build rule for target. +digest_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 digest_test +.PHONY : digest_test + +# fast build rule for target. +digest_test/fast: + $(MAKE) -f boringssl/crypto/digest/CMakeFiles/digest_test.dir/build.make boringssl/crypto/digest/CMakeFiles/digest_test.dir/build +.PHONY : digest_test/fast + +#============================================================================= +# Target rules for targets named aead_test + +# Build rule for target. +aead_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 aead_test +.PHONY : aead_test + +# fast build rule for target. +aead_test/fast: + $(MAKE) -f boringssl/crypto/cipher/CMakeFiles/aead_test.dir/build.make boringssl/crypto/cipher/CMakeFiles/aead_test.dir/build +.PHONY : aead_test/fast + +#============================================================================= +# Target rules for targets named cipher + +# Build rule for target. +cipher: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 cipher +.PHONY : cipher + +# fast build rule for target. +cipher/fast: + $(MAKE) -f boringssl/crypto/cipher/CMakeFiles/cipher.dir/build.make boringssl/crypto/cipher/CMakeFiles/cipher.dir/build +.PHONY : cipher/fast + +#============================================================================= +# Target rules for targets named cipher_test + +# Build rule for target. +cipher_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 cipher_test +.PHONY : cipher_test + +# fast build rule for target. +cipher_test/fast: + $(MAKE) -f boringssl/crypto/cipher/CMakeFiles/cipher_test.dir/build.make boringssl/crypto/cipher/CMakeFiles/cipher_test.dir/build +.PHONY : cipher_test/fast + +#============================================================================= +# Target rules for targets named rand + +# Build rule for target. +rand: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 rand +.PHONY : rand + +# fast build rule for target. +rand/fast: + $(MAKE) -f boringssl/crypto/rand/CMakeFiles/rand.dir/build.make boringssl/crypto/rand/CMakeFiles/rand.dir/build +.PHONY : rand/fast + +#============================================================================= +# Target rules for targets named bio + +# Build rule for target. +bio: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 bio +.PHONY : bio + +# fast build rule for target. +bio/fast: + $(MAKE) -f boringssl/crypto/bio/CMakeFiles/bio.dir/build.make boringssl/crypto/bio/CMakeFiles/bio.dir/build +.PHONY : bio/fast + +#============================================================================= +# Target rules for targets named bio_test + +# Build rule for target. +bio_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 bio_test +.PHONY : bio_test + +# fast build rule for target. +bio_test/fast: + $(MAKE) -f boringssl/crypto/bio/CMakeFiles/bio_test.dir/build.make boringssl/crypto/bio/CMakeFiles/bio_test.dir/build +.PHONY : bio_test/fast + +#============================================================================= +# Target rules for targets named bn + +# Build rule for target. +bn: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 bn +.PHONY : bn + +# fast build rule for target. +bn/fast: + $(MAKE) -f boringssl/crypto/bn/CMakeFiles/bn.dir/build.make boringssl/crypto/bn/CMakeFiles/bn.dir/build +.PHONY : bn/fast + +#============================================================================= +# Target rules for targets named bn_test + +# Build rule for target. +bn_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 bn_test +.PHONY : bn_test + +# fast build rule for target. +bn_test/fast: + $(MAKE) -f boringssl/crypto/bn/CMakeFiles/bn_test.dir/build.make boringssl/crypto/bn/CMakeFiles/bn_test.dir/build +.PHONY : bn_test/fast + +#============================================================================= +# Target rules for targets named obj + +# Build rule for target. +obj: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 obj +.PHONY : obj + +# fast build rule for target. +obj/fast: + $(MAKE) -f boringssl/crypto/obj/CMakeFiles/obj.dir/build.make boringssl/crypto/obj/CMakeFiles/obj.dir/build +.PHONY : obj/fast + +#============================================================================= +# Target rules for targets named asn1 + +# Build rule for target. +asn1: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 asn1 +.PHONY : asn1 + +# fast build rule for target. +asn1/fast: + $(MAKE) -f boringssl/crypto/asn1/CMakeFiles/asn1.dir/build.make boringssl/crypto/asn1/CMakeFiles/asn1.dir/build +.PHONY : asn1/fast + +#============================================================================= +# Target rules for targets named engine + +# Build rule for target. +engine: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 engine +.PHONY : engine + +# fast build rule for target. +engine/fast: + $(MAKE) -f boringssl/crypto/engine/CMakeFiles/engine.dir/build.make boringssl/crypto/engine/CMakeFiles/engine.dir/build +.PHONY : engine/fast + +#============================================================================= +# Target rules for targets named dh + +# Build rule for target. +dh: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 dh +.PHONY : dh + +# fast build rule for target. +dh/fast: + $(MAKE) -f boringssl/crypto/dh/CMakeFiles/dh.dir/build.make boringssl/crypto/dh/CMakeFiles/dh.dir/build +.PHONY : dh/fast + +#============================================================================= +# Target rules for targets named dh_test + +# Build rule for target. +dh_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 dh_test +.PHONY : dh_test + +# fast build rule for target. +dh_test/fast: + $(MAKE) -f boringssl/crypto/dh/CMakeFiles/dh_test.dir/build.make boringssl/crypto/dh/CMakeFiles/dh_test.dir/build +.PHONY : dh_test/fast + +#============================================================================= +# Target rules for targets named dsa + +# Build rule for target. +dsa: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 dsa +.PHONY : dsa + +# fast build rule for target. +dsa/fast: + $(MAKE) -f boringssl/crypto/dsa/CMakeFiles/dsa.dir/build.make boringssl/crypto/dsa/CMakeFiles/dsa.dir/build +.PHONY : dsa/fast + +#============================================================================= +# Target rules for targets named dsa_test + +# Build rule for target. +dsa_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 dsa_test +.PHONY : dsa_test + +# fast build rule for target. +dsa_test/fast: + $(MAKE) -f boringssl/crypto/dsa/CMakeFiles/dsa_test.dir/build.make boringssl/crypto/dsa/CMakeFiles/dsa_test.dir/build +.PHONY : dsa_test/fast + +#============================================================================= +# Target rules for targets named rsa + +# Build rule for target. +rsa: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 rsa +.PHONY : rsa + +# fast build rule for target. +rsa/fast: + $(MAKE) -f boringssl/crypto/rsa/CMakeFiles/rsa.dir/build.make boringssl/crypto/rsa/CMakeFiles/rsa.dir/build +.PHONY : rsa/fast + +#============================================================================= +# Target rules for targets named rsa_test + +# Build rule for target. +rsa_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 rsa_test +.PHONY : rsa_test + +# fast build rule for target. +rsa_test/fast: + $(MAKE) -f boringssl/crypto/rsa/CMakeFiles/rsa_test.dir/build.make boringssl/crypto/rsa/CMakeFiles/rsa_test.dir/build +.PHONY : rsa_test/fast + +#============================================================================= +# Target rules for targets named ec + +# Build rule for target. +ec: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 ec +.PHONY : ec + +# fast build rule for target. +ec/fast: + $(MAKE) -f boringssl/crypto/ec/CMakeFiles/ec.dir/build.make boringssl/crypto/ec/CMakeFiles/ec.dir/build +.PHONY : ec/fast + +#============================================================================= +# Target rules for targets named ec_test + +# Build rule for target. +ec_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 ec_test +.PHONY : ec_test + +# fast build rule for target. +ec_test/fast: + $(MAKE) -f boringssl/crypto/ec/CMakeFiles/ec_test.dir/build.make boringssl/crypto/ec/CMakeFiles/ec_test.dir/build +.PHONY : ec_test/fast + +#============================================================================= +# Target rules for targets named example_mul + +# Build rule for target. +example_mul: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 example_mul +.PHONY : example_mul + +# fast build rule for target. +example_mul/fast: + $(MAKE) -f boringssl/crypto/ec/CMakeFiles/example_mul.dir/build.make boringssl/crypto/ec/CMakeFiles/example_mul.dir/build +.PHONY : example_mul/fast + +#============================================================================= +# Target rules for targets named ecdh + +# Build rule for target. +ecdh: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 ecdh +.PHONY : ecdh + +# fast build rule for target. +ecdh/fast: + $(MAKE) -f boringssl/crypto/ecdh/CMakeFiles/ecdh.dir/build.make boringssl/crypto/ecdh/CMakeFiles/ecdh.dir/build +.PHONY : ecdh/fast + +#============================================================================= +# Target rules for targets named ecdsa + +# Build rule for target. +ecdsa: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 ecdsa +.PHONY : ecdsa + +# fast build rule for target. +ecdsa/fast: + $(MAKE) -f boringssl/crypto/ecdsa/CMakeFiles/ecdsa.dir/build.make boringssl/crypto/ecdsa/CMakeFiles/ecdsa.dir/build +.PHONY : ecdsa/fast + +#============================================================================= +# Target rules for targets named ecdsa_test + +# Build rule for target. +ecdsa_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 ecdsa_test +.PHONY : ecdsa_test + +# fast build rule for target. +ecdsa_test/fast: + $(MAKE) -f boringssl/crypto/ecdsa/CMakeFiles/ecdsa_test.dir/build.make boringssl/crypto/ecdsa/CMakeFiles/ecdsa_test.dir/build +.PHONY : ecdsa_test/fast + +#============================================================================= +# Target rules for targets named hmac + +# Build rule for target. +hmac: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 hmac +.PHONY : hmac + +# fast build rule for target. +hmac/fast: + $(MAKE) -f boringssl/crypto/hmac/CMakeFiles/hmac.dir/build.make boringssl/crypto/hmac/CMakeFiles/hmac.dir/build +.PHONY : hmac/fast + +#============================================================================= +# Target rules for targets named hmac_test + +# Build rule for target. +hmac_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 hmac_test +.PHONY : hmac_test + +# fast build rule for target. +hmac_test/fast: + $(MAKE) -f boringssl/crypto/hmac/CMakeFiles/hmac_test.dir/build.make boringssl/crypto/hmac/CMakeFiles/hmac_test.dir/build +.PHONY : hmac_test/fast + +#============================================================================= +# Target rules for targets named cmac + +# Build rule for target. +cmac: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 cmac +.PHONY : cmac + +# fast build rule for target. +cmac/fast: + $(MAKE) -f boringssl/crypto/cmac/CMakeFiles/cmac.dir/build.make boringssl/crypto/cmac/CMakeFiles/cmac.dir/build +.PHONY : cmac/fast + +#============================================================================= +# Target rules for targets named cmac_test + +# Build rule for target. +cmac_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 cmac_test +.PHONY : cmac_test + +# fast build rule for target. +cmac_test/fast: + $(MAKE) -f boringssl/crypto/cmac/CMakeFiles/cmac_test.dir/build.make boringssl/crypto/cmac/CMakeFiles/cmac_test.dir/build +.PHONY : cmac_test/fast + +#============================================================================= +# Target rules for targets named evp + +# Build rule for target. +evp: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 evp +.PHONY : evp + +# fast build rule for target. +evp/fast: + $(MAKE) -f boringssl/crypto/evp/CMakeFiles/evp.dir/build.make boringssl/crypto/evp/CMakeFiles/evp.dir/build +.PHONY : evp/fast + +#============================================================================= +# Target rules for targets named evp_extra_test + +# Build rule for target. +evp_extra_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 evp_extra_test +.PHONY : evp_extra_test + +# fast build rule for target. +evp_extra_test/fast: + $(MAKE) -f boringssl/crypto/evp/CMakeFiles/evp_extra_test.dir/build.make boringssl/crypto/evp/CMakeFiles/evp_extra_test.dir/build +.PHONY : evp_extra_test/fast + +#============================================================================= +# Target rules for targets named evp_test + +# Build rule for target. +evp_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 evp_test +.PHONY : evp_test + +# fast build rule for target. +evp_test/fast: + $(MAKE) -f boringssl/crypto/evp/CMakeFiles/evp_test.dir/build.make boringssl/crypto/evp/CMakeFiles/evp_test.dir/build +.PHONY : evp_test/fast + +#============================================================================= +# Target rules for targets named pbkdf_test + +# Build rule for target. +pbkdf_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 pbkdf_test +.PHONY : pbkdf_test + +# fast build rule for target. +pbkdf_test/fast: + $(MAKE) -f boringssl/crypto/evp/CMakeFiles/pbkdf_test.dir/build.make boringssl/crypto/evp/CMakeFiles/pbkdf_test.dir/build +.PHONY : pbkdf_test/fast + +#============================================================================= +# Target rules for targets named hkdf + +# Build rule for target. +hkdf: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 hkdf +.PHONY : hkdf + +# fast build rule for target. +hkdf/fast: + $(MAKE) -f boringssl/crypto/hkdf/CMakeFiles/hkdf.dir/build.make boringssl/crypto/hkdf/CMakeFiles/hkdf.dir/build +.PHONY : hkdf/fast + +#============================================================================= +# Target rules for targets named hkdf_test + +# Build rule for target. +hkdf_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 hkdf_test +.PHONY : hkdf_test + +# fast build rule for target. +hkdf_test/fast: + $(MAKE) -f boringssl/crypto/hkdf/CMakeFiles/hkdf_test.dir/build.make boringssl/crypto/hkdf/CMakeFiles/hkdf_test.dir/build +.PHONY : hkdf_test/fast + +#============================================================================= +# Target rules for targets named pem + +# Build rule for target. +pem: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 pem +.PHONY : pem + +# fast build rule for target. +pem/fast: + $(MAKE) -f boringssl/crypto/pem/CMakeFiles/pem.dir/build.make boringssl/crypto/pem/CMakeFiles/pem.dir/build +.PHONY : pem/fast + +#============================================================================= +# Target rules for targets named pkcs7_test + +# Build rule for target. +pkcs7_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 pkcs7_test +.PHONY : pkcs7_test + +# fast build rule for target. +pkcs7_test/fast: + $(MAKE) -f boringssl/crypto/x509/CMakeFiles/pkcs7_test.dir/build.make boringssl/crypto/x509/CMakeFiles/pkcs7_test.dir/build +.PHONY : pkcs7_test/fast + +#============================================================================= +# Target rules for targets named x509 + +# Build rule for target. +x509: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 x509 +.PHONY : x509 + +# fast build rule for target. +x509/fast: + $(MAKE) -f boringssl/crypto/x509/CMakeFiles/x509.dir/build.make boringssl/crypto/x509/CMakeFiles/x509.dir/build +.PHONY : x509/fast + +#============================================================================= +# Target rules for targets named tab_test + +# Build rule for target. +tab_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 tab_test +.PHONY : tab_test + +# fast build rule for target. +tab_test/fast: + $(MAKE) -f boringssl/crypto/x509v3/CMakeFiles/tab_test.dir/build.make boringssl/crypto/x509v3/CMakeFiles/tab_test.dir/build +.PHONY : tab_test/fast + +#============================================================================= +# Target rules for targets named v3name_test + +# Build rule for target. +v3name_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 v3name_test +.PHONY : v3name_test + +# fast build rule for target. +v3name_test/fast: + $(MAKE) -f boringssl/crypto/x509v3/CMakeFiles/v3name_test.dir/build.make boringssl/crypto/x509v3/CMakeFiles/v3name_test.dir/build +.PHONY : v3name_test/fast + +#============================================================================= +# Target rules for targets named x509v3 + +# Build rule for target. +x509v3: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 x509v3 +.PHONY : x509v3 + +# fast build rule for target. +x509v3/fast: + $(MAKE) -f boringssl/crypto/x509v3/CMakeFiles/x509v3.dir/build.make boringssl/crypto/x509v3/CMakeFiles/x509v3.dir/build +.PHONY : x509v3/fast + +#============================================================================= +# Target rules for targets named pkcs12_test + +# Build rule for target. +pkcs12_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 pkcs12_test +.PHONY : pkcs12_test + +# fast build rule for target. +pkcs12_test/fast: + $(MAKE) -f boringssl/crypto/pkcs8/CMakeFiles/pkcs12_test.dir/build.make boringssl/crypto/pkcs8/CMakeFiles/pkcs12_test.dir/build +.PHONY : pkcs12_test/fast + +#============================================================================= +# Target rules for targets named pkcs8 + +# Build rule for target. +pkcs8: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 pkcs8 +.PHONY : pkcs8 + +# fast build rule for target. +pkcs8/fast: + $(MAKE) -f boringssl/crypto/pkcs8/CMakeFiles/pkcs8.dir/build.make boringssl/crypto/pkcs8/CMakeFiles/pkcs8.dir/build +.PHONY : pkcs8/fast + +#============================================================================= +# Target rules for targets named pkcs8_test + +# Build rule for target. +pkcs8_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 pkcs8_test +.PHONY : pkcs8_test + +# fast build rule for target. +pkcs8_test/fast: + $(MAKE) -f boringssl/crypto/pkcs8/CMakeFiles/pkcs8_test.dir/build.make boringssl/crypto/pkcs8/CMakeFiles/pkcs8_test.dir/build +.PHONY : pkcs8_test/fast + +#============================================================================= +# Target rules for targets named test_support + +# Build rule for target. +test_support: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 test_support +.PHONY : test_support + +# fast build rule for target. +test_support/fast: + $(MAKE) -f boringssl/crypto/test/CMakeFiles/test_support.dir/build.make boringssl/crypto/test/CMakeFiles/test_support.dir/build +.PHONY : test_support/fast + +#============================================================================= +# Target rules for targets named ssl + +# Build rule for target. +ssl: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 ssl +.PHONY : ssl + +# fast build rule for target. +ssl/fast: + $(MAKE) -f boringssl/ssl/CMakeFiles/ssl.dir/build.make boringssl/ssl/CMakeFiles/ssl.dir/build +.PHONY : ssl/fast + +#============================================================================= +# Target rules for targets named ssl_test + +# Build rule for target. +ssl_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 ssl_test +.PHONY : ssl_test + +# fast build rule for target. +ssl_test/fast: + $(MAKE) -f boringssl/ssl/CMakeFiles/ssl_test.dir/build.make boringssl/ssl/CMakeFiles/ssl_test.dir/build +.PHONY : ssl_test/fast + +#============================================================================= +# Target rules for targets named pqueue + +# Build rule for target. +pqueue: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 pqueue +.PHONY : pqueue + +# fast build rule for target. +pqueue/fast: + $(MAKE) -f boringssl/ssl/pqueue/CMakeFiles/pqueue.dir/build.make boringssl/ssl/pqueue/CMakeFiles/pqueue.dir/build +.PHONY : pqueue/fast + +#============================================================================= +# Target rules for targets named pqueue_test + +# Build rule for target. +pqueue_test: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 pqueue_test +.PHONY : pqueue_test + +# fast build rule for target. +pqueue_test/fast: + $(MAKE) -f boringssl/ssl/pqueue/CMakeFiles/pqueue_test.dir/build.make boringssl/ssl/pqueue/CMakeFiles/pqueue_test.dir/build +.PHONY : pqueue_test/fast + +#============================================================================= +# Target rules for targets named bssl_shim + +# Build rule for target. +bssl_shim: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 bssl_shim +.PHONY : bssl_shim + +# fast build rule for target. +bssl_shim/fast: + $(MAKE) -f boringssl/ssl/test/CMakeFiles/bssl_shim.dir/build.make boringssl/ssl/test/CMakeFiles/bssl_shim.dir/build +.PHONY : bssl_shim/fast + +#============================================================================= +# Target rules for targets named bssl + +# Build rule for target. +bssl: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 bssl +.PHONY : bssl + +# fast build rule for target. +bssl/fast: + $(MAKE) -f boringssl/tool/CMakeFiles/bssl.dir/build.make boringssl/tool/CMakeFiles/bssl.dir/build +.PHONY : bssl/fast + +#============================================================================= +# Target rules for targets named decrepit + +# Build rule for target. +decrepit: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 decrepit +.PHONY : decrepit + +# fast build rule for target. +decrepit/fast: + $(MAKE) -f boringssl/decrepit/CMakeFiles/decrepit.dir/build.make boringssl/decrepit/CMakeFiles/decrepit.dir/build +.PHONY : decrepit/fast + +#============================================================================= +# Target rules for targets named bio_decrepit + +# Build rule for target. +bio_decrepit: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 bio_decrepit +.PHONY : bio_decrepit + +# fast build rule for target. +bio_decrepit/fast: + $(MAKE) -f boringssl/decrepit/bio/CMakeFiles/bio_decrepit.dir/build.make boringssl/decrepit/bio/CMakeFiles/bio_decrepit.dir/build +.PHONY : bio_decrepit/fast + +#============================================================================= +# Target rules for targets named blowfish + +# Build rule for target. +blowfish: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 blowfish +.PHONY : blowfish + +# fast build rule for target. +blowfish/fast: + $(MAKE) -f boringssl/decrepit/blowfish/CMakeFiles/blowfish.dir/build.make boringssl/decrepit/blowfish/CMakeFiles/blowfish.dir/build +.PHONY : blowfish/fast + +#============================================================================= +# Target rules for targets named cast + +# Build rule for target. +cast: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 cast +.PHONY : cast + +# fast build rule for target. +cast/fast: + $(MAKE) -f boringssl/decrepit/cast/CMakeFiles/cast.dir/build.make boringssl/decrepit/cast/CMakeFiles/cast.dir/build +.PHONY : cast/fast + +#============================================================================= +# Target rules for targets named des_decrepit + +# Build rule for target. +des_decrepit: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 des_decrepit +.PHONY : des_decrepit + +# fast build rule for target. +des_decrepit/fast: + $(MAKE) -f boringssl/decrepit/des/CMakeFiles/des_decrepit.dir/build.make boringssl/decrepit/des/CMakeFiles/des_decrepit.dir/build +.PHONY : des_decrepit/fast + +#============================================================================= +# Target rules for targets named rsa_decrepit + +# Build rule for target. +rsa_decrepit: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 rsa_decrepit +.PHONY : rsa_decrepit + +# fast build rule for target. +rsa_decrepit/fast: + $(MAKE) -f boringssl/decrepit/rsa/CMakeFiles/rsa_decrepit.dir/build.make boringssl/decrepit/rsa/CMakeFiles/rsa_decrepit.dir/build +.PHONY : rsa_decrepit/fast + +#============================================================================= +# Target rules for targets named xts + +# Build rule for target. +xts: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 xts +.PHONY : xts + +# fast build rule for target. +xts/fast: + $(MAKE) -f boringssl/decrepit/xts/CMakeFiles/xts.dir/build.make boringssl/decrepit/xts/CMakeFiles/xts.dir/build +.PHONY : xts/fast + +#============================================================================= +# Target rules for targets named protobuf + +# Build rule for target. +protobuf: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 protobuf +.PHONY : protobuf + +# fast build rule for target. +protobuf/fast: + $(MAKE) -f protobuf/CMakeFiles/protobuf.dir/build.make protobuf/CMakeFiles/protobuf.dir/build +.PHONY : protobuf/fast + +src/base/at_exit.o: src/base/at_exit.cc.o + +.PHONY : src/base/at_exit.o + +# target to build an object file +src/base/at_exit.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/at_exit.cc.o +.PHONY : src/base/at_exit.cc.o + +src/base/at_exit.i: src/base/at_exit.cc.i + +.PHONY : src/base/at_exit.i + +# target to preprocess a source file +src/base/at_exit.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/at_exit.cc.i +.PHONY : src/base/at_exit.cc.i + +src/base/at_exit.s: src/base/at_exit.cc.s + +.PHONY : src/base/at_exit.s + +# target to generate assembly for a file +src/base/at_exit.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/at_exit.cc.s +.PHONY : src/base/at_exit.cc.s + +src/base/base64.o: src/base/base64.cc.o + +.PHONY : src/base/base64.o + +# target to build an object file +src/base/base64.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/base64.cc.o +.PHONY : src/base/base64.cc.o + +src/base/base64.i: src/base/base64.cc.i + +.PHONY : src/base/base64.i + +# target to preprocess a source file +src/base/base64.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/base64.cc.i +.PHONY : src/base/base64.cc.i + +src/base/base64.s: src/base/base64.cc.s + +.PHONY : src/base/base64.s + +# target to generate assembly for a file +src/base/base64.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/base64.cc.s +.PHONY : src/base/base64.cc.s + +src/base/base_switches.o: src/base/base_switches.cc.o + +.PHONY : src/base/base_switches.o + +# target to build an object file +src/base/base_switches.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/base_switches.cc.o +.PHONY : src/base/base_switches.cc.o + +src/base/base_switches.i: src/base/base_switches.cc.i + +.PHONY : src/base/base_switches.i + +# target to preprocess a source file +src/base/base_switches.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/base_switches.cc.i +.PHONY : src/base/base_switches.cc.i + +src/base/base_switches.s: src/base/base_switches.cc.s + +.PHONY : src/base/base_switches.s + +# target to generate assembly for a file +src/base/base_switches.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/base_switches.cc.s +.PHONY : src/base/base_switches.cc.s + +src/base/bind_helpers.o: src/base/bind_helpers.cc.o + +.PHONY : src/base/bind_helpers.o + +# target to build an object file +src/base/bind_helpers.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/bind_helpers.cc.o +.PHONY : src/base/bind_helpers.cc.o + +src/base/bind_helpers.i: src/base/bind_helpers.cc.i + +.PHONY : src/base/bind_helpers.i + +# target to preprocess a source file +src/base/bind_helpers.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/bind_helpers.cc.i +.PHONY : src/base/bind_helpers.cc.i + +src/base/bind_helpers.s: src/base/bind_helpers.cc.s + +.PHONY : src/base/bind_helpers.s + +# target to generate assembly for a file +src/base/bind_helpers.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/bind_helpers.cc.s +.PHONY : src/base/bind_helpers.cc.s + +src/base/callback_helpers.o: src/base/callback_helpers.cc.o + +.PHONY : src/base/callback_helpers.o + +# target to build an object file +src/base/callback_helpers.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/callback_helpers.cc.o +.PHONY : src/base/callback_helpers.cc.o + +src/base/callback_helpers.i: src/base/callback_helpers.cc.i + +.PHONY : src/base/callback_helpers.i + +# target to preprocess a source file +src/base/callback_helpers.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/callback_helpers.cc.i +.PHONY : src/base/callback_helpers.cc.i + +src/base/callback_helpers.s: src/base/callback_helpers.cc.s + +.PHONY : src/base/callback_helpers.s + +# target to generate assembly for a file +src/base/callback_helpers.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/callback_helpers.cc.s +.PHONY : src/base/callback_helpers.cc.s + +src/base/callback_internal.o: src/base/callback_internal.cc.o + +.PHONY : src/base/callback_internal.o + +# target to build an object file +src/base/callback_internal.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/callback_internal.cc.o +.PHONY : src/base/callback_internal.cc.o + +src/base/callback_internal.i: src/base/callback_internal.cc.i + +.PHONY : src/base/callback_internal.i + +# target to preprocess a source file +src/base/callback_internal.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/callback_internal.cc.i +.PHONY : src/base/callback_internal.cc.i + +src/base/callback_internal.s: src/base/callback_internal.cc.s + +.PHONY : src/base/callback_internal.s + +# target to generate assembly for a file +src/base/callback_internal.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/callback_internal.cc.s +.PHONY : src/base/callback_internal.cc.s + +src/base/command_line.o: src/base/command_line.cc.o + +.PHONY : src/base/command_line.o + +# target to build an object file +src/base/command_line.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/command_line.cc.o +.PHONY : src/base/command_line.cc.o + +src/base/command_line.i: src/base/command_line.cc.i + +.PHONY : src/base/command_line.i + +# target to preprocess a source file +src/base/command_line.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/command_line.cc.i +.PHONY : src/base/command_line.cc.i + +src/base/command_line.s: src/base/command_line.cc.s + +.PHONY : src/base/command_line.s + +# target to generate assembly for a file +src/base/command_line.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/command_line.cc.s +.PHONY : src/base/command_line.cc.s + +src/base/debug/alias.o: src/base/debug/alias.cc.o + +.PHONY : src/base/debug/alias.o + +# target to build an object file +src/base/debug/alias.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/debug/alias.cc.o +.PHONY : src/base/debug/alias.cc.o + +src/base/debug/alias.i: src/base/debug/alias.cc.i + +.PHONY : src/base/debug/alias.i + +# target to preprocess a source file +src/base/debug/alias.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/debug/alias.cc.i +.PHONY : src/base/debug/alias.cc.i + +src/base/debug/alias.s: src/base/debug/alias.cc.s + +.PHONY : src/base/debug/alias.s + +# target to generate assembly for a file +src/base/debug/alias.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/debug/alias.cc.s +.PHONY : src/base/debug/alias.cc.s + +src/base/debug/debugger.o: src/base/debug/debugger.cc.o + +.PHONY : src/base/debug/debugger.o + +# target to build an object file +src/base/debug/debugger.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/debug/debugger.cc.o +.PHONY : src/base/debug/debugger.cc.o + +src/base/debug/debugger.i: src/base/debug/debugger.cc.i + +.PHONY : src/base/debug/debugger.i + +# target to preprocess a source file +src/base/debug/debugger.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/debug/debugger.cc.i +.PHONY : src/base/debug/debugger.cc.i + +src/base/debug/debugger.s: src/base/debug/debugger.cc.s + +.PHONY : src/base/debug/debugger.s + +# target to generate assembly for a file +src/base/debug/debugger.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/debug/debugger.cc.s +.PHONY : src/base/debug/debugger.cc.s + +src/base/debug/stack_trace.o: src/base/debug/stack_trace.cc.o + +.PHONY : src/base/debug/stack_trace.o + +# target to build an object file +src/base/debug/stack_trace.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/debug/stack_trace.cc.o +.PHONY : src/base/debug/stack_trace.cc.o + +src/base/debug/stack_trace.i: src/base/debug/stack_trace.cc.i + +.PHONY : src/base/debug/stack_trace.i + +# target to preprocess a source file +src/base/debug/stack_trace.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/debug/stack_trace.cc.i +.PHONY : src/base/debug/stack_trace.cc.i + +src/base/debug/stack_trace.s: src/base/debug/stack_trace.cc.s + +.PHONY : src/base/debug/stack_trace.s + +# target to generate assembly for a file +src/base/debug/stack_trace.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/debug/stack_trace.cc.s +.PHONY : src/base/debug/stack_trace.cc.s + +src/base/files/file_path.o: src/base/files/file_path.cc.o + +.PHONY : src/base/files/file_path.o + +# target to build an object file +src/base/files/file_path.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/files/file_path.cc.o +.PHONY : src/base/files/file_path.cc.o + +src/base/files/file_path.i: src/base/files/file_path.cc.i + +.PHONY : src/base/files/file_path.i + +# target to preprocess a source file +src/base/files/file_path.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/files/file_path.cc.i +.PHONY : src/base/files/file_path.cc.i + +src/base/files/file_path.s: src/base/files/file_path.cc.s + +.PHONY : src/base/files/file_path.s + +# target to generate assembly for a file +src/base/files/file_path.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/files/file_path.cc.s +.PHONY : src/base/files/file_path.cc.s + +src/base/files/file_path_constants.o: src/base/files/file_path_constants.cc.o + +.PHONY : src/base/files/file_path_constants.o + +# target to build an object file +src/base/files/file_path_constants.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/files/file_path_constants.cc.o +.PHONY : src/base/files/file_path_constants.cc.o + +src/base/files/file_path_constants.i: src/base/files/file_path_constants.cc.i + +.PHONY : src/base/files/file_path_constants.i + +# target to preprocess a source file +src/base/files/file_path_constants.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/files/file_path_constants.cc.i +.PHONY : src/base/files/file_path_constants.cc.i + +src/base/files/file_path_constants.s: src/base/files/file_path_constants.cc.s + +.PHONY : src/base/files/file_path_constants.s + +# target to generate assembly for a file +src/base/files/file_path_constants.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/files/file_path_constants.cc.s +.PHONY : src/base/files/file_path_constants.cc.s + +src/base/json/json_parser.o: src/base/json/json_parser.cc.o + +.PHONY : src/base/json/json_parser.o + +# target to build an object file +src/base/json/json_parser.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_parser.cc.o +.PHONY : src/base/json/json_parser.cc.o + +src/base/json/json_parser.i: src/base/json/json_parser.cc.i + +.PHONY : src/base/json/json_parser.i + +# target to preprocess a source file +src/base/json/json_parser.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_parser.cc.i +.PHONY : src/base/json/json_parser.cc.i + +src/base/json/json_parser.s: src/base/json/json_parser.cc.s + +.PHONY : src/base/json/json_parser.s + +# target to generate assembly for a file +src/base/json/json_parser.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_parser.cc.s +.PHONY : src/base/json/json_parser.cc.s + +src/base/json/json_reader.o: src/base/json/json_reader.cc.o + +.PHONY : src/base/json/json_reader.o + +# target to build an object file +src/base/json/json_reader.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_reader.cc.o +.PHONY : src/base/json/json_reader.cc.o + +src/base/json/json_reader.i: src/base/json/json_reader.cc.i + +.PHONY : src/base/json/json_reader.i + +# target to preprocess a source file +src/base/json/json_reader.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_reader.cc.i +.PHONY : src/base/json/json_reader.cc.i + +src/base/json/json_reader.s: src/base/json/json_reader.cc.s + +.PHONY : src/base/json/json_reader.s + +# target to generate assembly for a file +src/base/json/json_reader.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_reader.cc.s +.PHONY : src/base/json/json_reader.cc.s + +src/base/json/json_string_value_serializer.o: src/base/json/json_string_value_serializer.cc.o + +.PHONY : src/base/json/json_string_value_serializer.o + +# target to build an object file +src/base/json/json_string_value_serializer.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_string_value_serializer.cc.o +.PHONY : src/base/json/json_string_value_serializer.cc.o + +src/base/json/json_string_value_serializer.i: src/base/json/json_string_value_serializer.cc.i + +.PHONY : src/base/json/json_string_value_serializer.i + +# target to preprocess a source file +src/base/json/json_string_value_serializer.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_string_value_serializer.cc.i +.PHONY : src/base/json/json_string_value_serializer.cc.i + +src/base/json/json_string_value_serializer.s: src/base/json/json_string_value_serializer.cc.s + +.PHONY : src/base/json/json_string_value_serializer.s + +# target to generate assembly for a file +src/base/json/json_string_value_serializer.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_string_value_serializer.cc.s +.PHONY : src/base/json/json_string_value_serializer.cc.s + +src/base/json/json_writer.o: src/base/json/json_writer.cc.o + +.PHONY : src/base/json/json_writer.o + +# target to build an object file +src/base/json/json_writer.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_writer.cc.o +.PHONY : src/base/json/json_writer.cc.o + +src/base/json/json_writer.i: src/base/json/json_writer.cc.i + +.PHONY : src/base/json/json_writer.i + +# target to preprocess a source file +src/base/json/json_writer.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_writer.cc.i +.PHONY : src/base/json/json_writer.cc.i + +src/base/json/json_writer.s: src/base/json/json_writer.cc.s + +.PHONY : src/base/json/json_writer.s + +# target to generate assembly for a file +src/base/json/json_writer.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/json_writer.cc.s +.PHONY : src/base/json/json_writer.cc.s + +src/base/json/string_escape.o: src/base/json/string_escape.cc.o + +.PHONY : src/base/json/string_escape.o + +# target to build an object file +src/base/json/string_escape.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/string_escape.cc.o +.PHONY : src/base/json/string_escape.cc.o + +src/base/json/string_escape.i: src/base/json/string_escape.cc.i + +.PHONY : src/base/json/string_escape.i + +# target to preprocess a source file +src/base/json/string_escape.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/string_escape.cc.i +.PHONY : src/base/json/string_escape.cc.i + +src/base/json/string_escape.s: src/base/json/string_escape.cc.s + +.PHONY : src/base/json/string_escape.s + +# target to generate assembly for a file +src/base/json/string_escape.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/json/string_escape.cc.s +.PHONY : src/base/json/string_escape.cc.s + +src/base/lazy_instance.o: src/base/lazy_instance.cc.o + +.PHONY : src/base/lazy_instance.o + +# target to build an object file +src/base/lazy_instance.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/lazy_instance.cc.o +.PHONY : src/base/lazy_instance.cc.o + +src/base/lazy_instance.i: src/base/lazy_instance.cc.i + +.PHONY : src/base/lazy_instance.i + +# target to preprocess a source file +src/base/lazy_instance.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/lazy_instance.cc.i +.PHONY : src/base/lazy_instance.cc.i + +src/base/lazy_instance.s: src/base/lazy_instance.cc.s + +.PHONY : src/base/lazy_instance.s + +# target to generate assembly for a file +src/base/lazy_instance.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/lazy_instance.cc.s +.PHONY : src/base/lazy_instance.cc.s + +src/base/location.o: src/base/location.cc.o + +.PHONY : src/base/location.o + +# target to build an object file +src/base/location.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/location.cc.o +.PHONY : src/base/location.cc.o + +src/base/location.i: src/base/location.cc.i + +.PHONY : src/base/location.i + +# target to preprocess a source file +src/base/location.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/location.cc.i +.PHONY : src/base/location.cc.i + +src/base/location.s: src/base/location.cc.s + +.PHONY : src/base/location.s + +# target to generate assembly for a file +src/base/location.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/location.cc.s +.PHONY : src/base/location.cc.s + +src/base/logging.o: src/base/logging.cc.o + +.PHONY : src/base/logging.o + +# target to build an object file +src/base/logging.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/logging.cc.o +.PHONY : src/base/logging.cc.o + +src/base/logging.i: src/base/logging.cc.i + +.PHONY : src/base/logging.i + +# target to preprocess a source file +src/base/logging.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/logging.cc.i +.PHONY : src/base/logging.cc.i + +src/base/logging.s: src/base/logging.cc.s + +.PHONY : src/base/logging.s + +# target to generate assembly for a file +src/base/logging.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/logging.cc.s +.PHONY : src/base/logging.cc.s + +src/base/mac/bundle_locations.o: src/base/mac/bundle_locations.mm.o + +.PHONY : src/base/mac/bundle_locations.o + +# target to build an object file +src/base/mac/bundle_locations.mm.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/bundle_locations.mm.o +.PHONY : src/base/mac/bundle_locations.mm.o + +src/base/mac/bundle_locations.i: src/base/mac/bundle_locations.mm.i + +.PHONY : src/base/mac/bundle_locations.i + +# target to preprocess a source file +src/base/mac/bundle_locations.mm.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/bundle_locations.mm.i +.PHONY : src/base/mac/bundle_locations.mm.i + +src/base/mac/bundle_locations.s: src/base/mac/bundle_locations.mm.s + +.PHONY : src/base/mac/bundle_locations.s + +# target to generate assembly for a file +src/base/mac/bundle_locations.mm.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/bundle_locations.mm.s +.PHONY : src/base/mac/bundle_locations.mm.s + +src/base/mac/foundation_util.o: src/base/mac/foundation_util.mm.o + +.PHONY : src/base/mac/foundation_util.o + +# target to build an object file +src/base/mac/foundation_util.mm.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/foundation_util.mm.o +.PHONY : src/base/mac/foundation_util.mm.o + +src/base/mac/foundation_util.i: src/base/mac/foundation_util.mm.i + +.PHONY : src/base/mac/foundation_util.i + +# target to preprocess a source file +src/base/mac/foundation_util.mm.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/foundation_util.mm.i +.PHONY : src/base/mac/foundation_util.mm.i + +src/base/mac/foundation_util.s: src/base/mac/foundation_util.mm.s + +.PHONY : src/base/mac/foundation_util.s + +# target to generate assembly for a file +src/base/mac/foundation_util.mm.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/foundation_util.mm.s +.PHONY : src/base/mac/foundation_util.mm.s + +src/base/mac/mach_logging.o: src/base/mac/mach_logging.cc.o + +.PHONY : src/base/mac/mach_logging.o + +# target to build an object file +src/base/mac/mach_logging.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/mach_logging.cc.o +.PHONY : src/base/mac/mach_logging.cc.o + +src/base/mac/mach_logging.i: src/base/mac/mach_logging.cc.i + +.PHONY : src/base/mac/mach_logging.i + +# target to preprocess a source file +src/base/mac/mach_logging.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/mach_logging.cc.i +.PHONY : src/base/mac/mach_logging.cc.i + +src/base/mac/mach_logging.s: src/base/mac/mach_logging.cc.s + +.PHONY : src/base/mac/mach_logging.s + +# target to generate assembly for a file +src/base/mac/mach_logging.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/mach_logging.cc.s +.PHONY : src/base/mac/mach_logging.cc.s + +src/base/mac/scoped_mach_port.o: src/base/mac/scoped_mach_port.cc.o + +.PHONY : src/base/mac/scoped_mach_port.o + +# target to build an object file +src/base/mac/scoped_mach_port.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/scoped_mach_port.cc.o +.PHONY : src/base/mac/scoped_mach_port.cc.o + +src/base/mac/scoped_mach_port.i: src/base/mac/scoped_mach_port.cc.i + +.PHONY : src/base/mac/scoped_mach_port.i + +# target to preprocess a source file +src/base/mac/scoped_mach_port.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/scoped_mach_port.cc.i +.PHONY : src/base/mac/scoped_mach_port.cc.i + +src/base/mac/scoped_mach_port.s: src/base/mac/scoped_mach_port.cc.s + +.PHONY : src/base/mac/scoped_mach_port.s + +# target to generate assembly for a file +src/base/mac/scoped_mach_port.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/mac/scoped_mach_port.cc.s +.PHONY : src/base/mac/scoped_mach_port.cc.s + +src/base/md5.o: src/base/md5.cc.o + +.PHONY : src/base/md5.o + +# target to build an object file +src/base/md5.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/md5.cc.o +.PHONY : src/base/md5.cc.o + +src/base/md5.i: src/base/md5.cc.i + +.PHONY : src/base/md5.i + +# target to preprocess a source file +src/base/md5.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/md5.cc.i +.PHONY : src/base/md5.cc.i + +src/base/md5.s: src/base/md5.cc.s + +.PHONY : src/base/md5.s + +# target to generate assembly for a file +src/base/md5.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/md5.cc.s +.PHONY : src/base/md5.cc.s + +src/base/memory/aligned_memory.o: src/base/memory/aligned_memory.cc.o + +.PHONY : src/base/memory/aligned_memory.o + +# target to build an object file +src/base/memory/aligned_memory.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/aligned_memory.cc.o +.PHONY : src/base/memory/aligned_memory.cc.o + +src/base/memory/aligned_memory.i: src/base/memory/aligned_memory.cc.i + +.PHONY : src/base/memory/aligned_memory.i + +# target to preprocess a source file +src/base/memory/aligned_memory.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/aligned_memory.cc.i +.PHONY : src/base/memory/aligned_memory.cc.i + +src/base/memory/aligned_memory.s: src/base/memory/aligned_memory.cc.s + +.PHONY : src/base/memory/aligned_memory.s + +# target to generate assembly for a file +src/base/memory/aligned_memory.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/aligned_memory.cc.s +.PHONY : src/base/memory/aligned_memory.cc.s + +src/base/memory/ref_counted.o: src/base/memory/ref_counted.cc.o + +.PHONY : src/base/memory/ref_counted.o + +# target to build an object file +src/base/memory/ref_counted.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/ref_counted.cc.o +.PHONY : src/base/memory/ref_counted.cc.o + +src/base/memory/ref_counted.i: src/base/memory/ref_counted.cc.i + +.PHONY : src/base/memory/ref_counted.i + +# target to preprocess a source file +src/base/memory/ref_counted.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/ref_counted.cc.i +.PHONY : src/base/memory/ref_counted.cc.i + +src/base/memory/ref_counted.s: src/base/memory/ref_counted.cc.s + +.PHONY : src/base/memory/ref_counted.s + +# target to generate assembly for a file +src/base/memory/ref_counted.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/ref_counted.cc.s +.PHONY : src/base/memory/ref_counted.cc.s + +src/base/memory/singleton.o: src/base/memory/singleton.cc.o + +.PHONY : src/base/memory/singleton.o + +# target to build an object file +src/base/memory/singleton.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/singleton.cc.o +.PHONY : src/base/memory/singleton.cc.o + +src/base/memory/singleton.i: src/base/memory/singleton.cc.i + +.PHONY : src/base/memory/singleton.i + +# target to preprocess a source file +src/base/memory/singleton.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/singleton.cc.i +.PHONY : src/base/memory/singleton.cc.i + +src/base/memory/singleton.s: src/base/memory/singleton.cc.s + +.PHONY : src/base/memory/singleton.s + +# target to generate assembly for a file +src/base/memory/singleton.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/singleton.cc.s +.PHONY : src/base/memory/singleton.cc.s + +src/base/memory/weak_ptr.o: src/base/memory/weak_ptr.cc.o + +.PHONY : src/base/memory/weak_ptr.o + +# target to build an object file +src/base/memory/weak_ptr.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/weak_ptr.cc.o +.PHONY : src/base/memory/weak_ptr.cc.o + +src/base/memory/weak_ptr.i: src/base/memory/weak_ptr.cc.i + +.PHONY : src/base/memory/weak_ptr.i + +# target to preprocess a source file +src/base/memory/weak_ptr.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/weak_ptr.cc.i +.PHONY : src/base/memory/weak_ptr.cc.i + +src/base/memory/weak_ptr.s: src/base/memory/weak_ptr.cc.s + +.PHONY : src/base/memory/weak_ptr.s + +# target to generate assembly for a file +src/base/memory/weak_ptr.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/memory/weak_ptr.cc.s +.PHONY : src/base/memory/weak_ptr.cc.s + +src/base/metrics/bucket_ranges.o: src/base/metrics/bucket_ranges.cc.o + +.PHONY : src/base/metrics/bucket_ranges.o + +# target to build an object file +src/base/metrics/bucket_ranges.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/bucket_ranges.cc.o +.PHONY : src/base/metrics/bucket_ranges.cc.o + +src/base/metrics/bucket_ranges.i: src/base/metrics/bucket_ranges.cc.i + +.PHONY : src/base/metrics/bucket_ranges.i + +# target to preprocess a source file +src/base/metrics/bucket_ranges.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/bucket_ranges.cc.i +.PHONY : src/base/metrics/bucket_ranges.cc.i + +src/base/metrics/bucket_ranges.s: src/base/metrics/bucket_ranges.cc.s + +.PHONY : src/base/metrics/bucket_ranges.s + +# target to generate assembly for a file +src/base/metrics/bucket_ranges.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/bucket_ranges.cc.s +.PHONY : src/base/metrics/bucket_ranges.cc.s + +src/base/metrics/histogram.o: src/base/metrics/histogram.cc.o + +.PHONY : src/base/metrics/histogram.o + +# target to build an object file +src/base/metrics/histogram.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/histogram.cc.o +.PHONY : src/base/metrics/histogram.cc.o + +src/base/metrics/histogram.i: src/base/metrics/histogram.cc.i + +.PHONY : src/base/metrics/histogram.i + +# target to preprocess a source file +src/base/metrics/histogram.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/histogram.cc.i +.PHONY : src/base/metrics/histogram.cc.i + +src/base/metrics/histogram.s: src/base/metrics/histogram.cc.s + +.PHONY : src/base/metrics/histogram.s + +# target to generate assembly for a file +src/base/metrics/histogram.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/histogram.cc.s +.PHONY : src/base/metrics/histogram.cc.s + +src/base/metrics/histogram_base.o: src/base/metrics/histogram_base.cc.o + +.PHONY : src/base/metrics/histogram_base.o + +# target to build an object file +src/base/metrics/histogram_base.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/histogram_base.cc.o +.PHONY : src/base/metrics/histogram_base.cc.o + +src/base/metrics/histogram_base.i: src/base/metrics/histogram_base.cc.i + +.PHONY : src/base/metrics/histogram_base.i + +# target to preprocess a source file +src/base/metrics/histogram_base.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/histogram_base.cc.i +.PHONY : src/base/metrics/histogram_base.cc.i + +src/base/metrics/histogram_base.s: src/base/metrics/histogram_base.cc.s + +.PHONY : src/base/metrics/histogram_base.s + +# target to generate assembly for a file +src/base/metrics/histogram_base.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/histogram_base.cc.s +.PHONY : src/base/metrics/histogram_base.cc.s + +src/base/metrics/histogram_samples.o: src/base/metrics/histogram_samples.cc.o + +.PHONY : src/base/metrics/histogram_samples.o + +# target to build an object file +src/base/metrics/histogram_samples.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/histogram_samples.cc.o +.PHONY : src/base/metrics/histogram_samples.cc.o + +src/base/metrics/histogram_samples.i: src/base/metrics/histogram_samples.cc.i + +.PHONY : src/base/metrics/histogram_samples.i + +# target to preprocess a source file +src/base/metrics/histogram_samples.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/histogram_samples.cc.i +.PHONY : src/base/metrics/histogram_samples.cc.i + +src/base/metrics/histogram_samples.s: src/base/metrics/histogram_samples.cc.s + +.PHONY : src/base/metrics/histogram_samples.s + +# target to generate assembly for a file +src/base/metrics/histogram_samples.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/histogram_samples.cc.s +.PHONY : src/base/metrics/histogram_samples.cc.s + +src/base/metrics/metrics_hashes.o: src/base/metrics/metrics_hashes.cc.o + +.PHONY : src/base/metrics/metrics_hashes.o + +# target to build an object file +src/base/metrics/metrics_hashes.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/metrics_hashes.cc.o +.PHONY : src/base/metrics/metrics_hashes.cc.o + +src/base/metrics/metrics_hashes.i: src/base/metrics/metrics_hashes.cc.i + +.PHONY : src/base/metrics/metrics_hashes.i + +# target to preprocess a source file +src/base/metrics/metrics_hashes.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/metrics_hashes.cc.i +.PHONY : src/base/metrics/metrics_hashes.cc.i + +src/base/metrics/metrics_hashes.s: src/base/metrics/metrics_hashes.cc.s + +.PHONY : src/base/metrics/metrics_hashes.s + +# target to generate assembly for a file +src/base/metrics/metrics_hashes.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/metrics_hashes.cc.s +.PHONY : src/base/metrics/metrics_hashes.cc.s + +src/base/metrics/sample_map.o: src/base/metrics/sample_map.cc.o + +.PHONY : src/base/metrics/sample_map.o + +# target to build an object file +src/base/metrics/sample_map.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/sample_map.cc.o +.PHONY : src/base/metrics/sample_map.cc.o + +src/base/metrics/sample_map.i: src/base/metrics/sample_map.cc.i + +.PHONY : src/base/metrics/sample_map.i + +# target to preprocess a source file +src/base/metrics/sample_map.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/sample_map.cc.i +.PHONY : src/base/metrics/sample_map.cc.i + +src/base/metrics/sample_map.s: src/base/metrics/sample_map.cc.s + +.PHONY : src/base/metrics/sample_map.s + +# target to generate assembly for a file +src/base/metrics/sample_map.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/sample_map.cc.s +.PHONY : src/base/metrics/sample_map.cc.s + +src/base/metrics/sample_vector.o: src/base/metrics/sample_vector.cc.o + +.PHONY : src/base/metrics/sample_vector.o + +# target to build an object file +src/base/metrics/sample_vector.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/sample_vector.cc.o +.PHONY : src/base/metrics/sample_vector.cc.o + +src/base/metrics/sample_vector.i: src/base/metrics/sample_vector.cc.i + +.PHONY : src/base/metrics/sample_vector.i + +# target to preprocess a source file +src/base/metrics/sample_vector.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/sample_vector.cc.i +.PHONY : src/base/metrics/sample_vector.cc.i + +src/base/metrics/sample_vector.s: src/base/metrics/sample_vector.cc.s + +.PHONY : src/base/metrics/sample_vector.s + +# target to generate assembly for a file +src/base/metrics/sample_vector.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/sample_vector.cc.s +.PHONY : src/base/metrics/sample_vector.cc.s + +src/base/metrics/sparse_histogram.o: src/base/metrics/sparse_histogram.cc.o + +.PHONY : src/base/metrics/sparse_histogram.o + +# target to build an object file +src/base/metrics/sparse_histogram.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/sparse_histogram.cc.o +.PHONY : src/base/metrics/sparse_histogram.cc.o + +src/base/metrics/sparse_histogram.i: src/base/metrics/sparse_histogram.cc.i + +.PHONY : src/base/metrics/sparse_histogram.i + +# target to preprocess a source file +src/base/metrics/sparse_histogram.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/sparse_histogram.cc.i +.PHONY : src/base/metrics/sparse_histogram.cc.i + +src/base/metrics/sparse_histogram.s: src/base/metrics/sparse_histogram.cc.s + +.PHONY : src/base/metrics/sparse_histogram.s + +# target to generate assembly for a file +src/base/metrics/sparse_histogram.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/sparse_histogram.cc.s +.PHONY : src/base/metrics/sparse_histogram.cc.s + +src/base/metrics/statistics_recorder.o: src/base/metrics/statistics_recorder.cc.o + +.PHONY : src/base/metrics/statistics_recorder.o + +# target to build an object file +src/base/metrics/statistics_recorder.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/statistics_recorder.cc.o +.PHONY : src/base/metrics/statistics_recorder.cc.o + +src/base/metrics/statistics_recorder.i: src/base/metrics/statistics_recorder.cc.i + +.PHONY : src/base/metrics/statistics_recorder.i + +# target to preprocess a source file +src/base/metrics/statistics_recorder.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/statistics_recorder.cc.i +.PHONY : src/base/metrics/statistics_recorder.cc.i + +src/base/metrics/statistics_recorder.s: src/base/metrics/statistics_recorder.cc.s + +.PHONY : src/base/metrics/statistics_recorder.s + +# target to generate assembly for a file +src/base/metrics/statistics_recorder.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/metrics/statistics_recorder.cc.s +.PHONY : src/base/metrics/statistics_recorder.cc.s + +src/base/pickle.o: src/base/pickle.cc.o + +.PHONY : src/base/pickle.o + +# target to build an object file +src/base/pickle.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/pickle.cc.o +.PHONY : src/base/pickle.cc.o + +src/base/pickle.i: src/base/pickle.cc.i + +.PHONY : src/base/pickle.i + +# target to preprocess a source file +src/base/pickle.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/pickle.cc.i +.PHONY : src/base/pickle.cc.i + +src/base/pickle.s: src/base/pickle.cc.s + +.PHONY : src/base/pickle.s + +# target to generate assembly for a file +src/base/pickle.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/pickle.cc.s +.PHONY : src/base/pickle.cc.s + +src/base/posix/safe_strerror.o: src/base/posix/safe_strerror.cc.o + +.PHONY : src/base/posix/safe_strerror.o + +# target to build an object file +src/base/posix/safe_strerror.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/posix/safe_strerror.cc.o +.PHONY : src/base/posix/safe_strerror.cc.o + +src/base/posix/safe_strerror.i: src/base/posix/safe_strerror.cc.i + +.PHONY : src/base/posix/safe_strerror.i + +# target to preprocess a source file +src/base/posix/safe_strerror.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/posix/safe_strerror.cc.i +.PHONY : src/base/posix/safe_strerror.cc.i + +src/base/posix/safe_strerror.s: src/base/posix/safe_strerror.cc.s + +.PHONY : src/base/posix/safe_strerror.s + +# target to generate assembly for a file +src/base/posix/safe_strerror.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/posix/safe_strerror.cc.s +.PHONY : src/base/posix/safe_strerror.cc.s + +src/base/process/process_handle_posix.o: src/base/process/process_handle_posix.cc.o + +.PHONY : src/base/process/process_handle_posix.o + +# target to build an object file +src/base/process/process_handle_posix.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/process/process_handle_posix.cc.o +.PHONY : src/base/process/process_handle_posix.cc.o + +src/base/process/process_handle_posix.i: src/base/process/process_handle_posix.cc.i + +.PHONY : src/base/process/process_handle_posix.i + +# target to preprocess a source file +src/base/process/process_handle_posix.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/process/process_handle_posix.cc.i +.PHONY : src/base/process/process_handle_posix.cc.i + +src/base/process/process_handle_posix.s: src/base/process/process_handle_posix.cc.s + +.PHONY : src/base/process/process_handle_posix.s + +# target to generate assembly for a file +src/base/process/process_handle_posix.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/process/process_handle_posix.cc.s +.PHONY : src/base/process/process_handle_posix.cc.s + +src/base/rand_util.o: src/base/rand_util.cc.o + +.PHONY : src/base/rand_util.o + +# target to build an object file +src/base/rand_util.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/rand_util.cc.o +.PHONY : src/base/rand_util.cc.o + +src/base/rand_util.i: src/base/rand_util.cc.i + +.PHONY : src/base/rand_util.i + +# target to preprocess a source file +src/base/rand_util.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/rand_util.cc.i +.PHONY : src/base/rand_util.cc.i + +src/base/rand_util.s: src/base/rand_util.cc.s + +.PHONY : src/base/rand_util.s + +# target to generate assembly for a file +src/base/rand_util.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/rand_util.cc.s +.PHONY : src/base/rand_util.cc.s + +src/base/rand_util_posix.o: src/base/rand_util_posix.cc.o + +.PHONY : src/base/rand_util_posix.o + +# target to build an object file +src/base/rand_util_posix.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/rand_util_posix.cc.o +.PHONY : src/base/rand_util_posix.cc.o + +src/base/rand_util_posix.i: src/base/rand_util_posix.cc.i + +.PHONY : src/base/rand_util_posix.i + +# target to preprocess a source file +src/base/rand_util_posix.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/rand_util_posix.cc.i +.PHONY : src/base/rand_util_posix.cc.i + +src/base/rand_util_posix.s: src/base/rand_util_posix.cc.s + +.PHONY : src/base/rand_util_posix.s + +# target to generate assembly for a file +src/base/rand_util_posix.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/rand_util_posix.cc.s +.PHONY : src/base/rand_util_posix.cc.s + +src/base/strings/string16.o: src/base/strings/string16.cc.o + +.PHONY : src/base/strings/string16.o + +# target to build an object file +src/base/strings/string16.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string16.cc.o +.PHONY : src/base/strings/string16.cc.o + +src/base/strings/string16.i: src/base/strings/string16.cc.i + +.PHONY : src/base/strings/string16.i + +# target to preprocess a source file +src/base/strings/string16.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string16.cc.i +.PHONY : src/base/strings/string16.cc.i + +src/base/strings/string16.s: src/base/strings/string16.cc.s + +.PHONY : src/base/strings/string16.s + +# target to generate assembly for a file +src/base/strings/string16.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string16.cc.s +.PHONY : src/base/strings/string16.cc.s + +src/base/strings/string_number_conversions.o: src/base/strings/string_number_conversions.cc.o + +.PHONY : src/base/strings/string_number_conversions.o + +# target to build an object file +src/base/strings/string_number_conversions.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_number_conversions.cc.o +.PHONY : src/base/strings/string_number_conversions.cc.o + +src/base/strings/string_number_conversions.i: src/base/strings/string_number_conversions.cc.i + +.PHONY : src/base/strings/string_number_conversions.i + +# target to preprocess a source file +src/base/strings/string_number_conversions.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_number_conversions.cc.i +.PHONY : src/base/strings/string_number_conversions.cc.i + +src/base/strings/string_number_conversions.s: src/base/strings/string_number_conversions.cc.s + +.PHONY : src/base/strings/string_number_conversions.s + +# target to generate assembly for a file +src/base/strings/string_number_conversions.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_number_conversions.cc.s +.PHONY : src/base/strings/string_number_conversions.cc.s + +src/base/strings/string_piece.o: src/base/strings/string_piece.cc.o + +.PHONY : src/base/strings/string_piece.o + +# target to build an object file +src/base/strings/string_piece.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_piece.cc.o +.PHONY : src/base/strings/string_piece.cc.o + +src/base/strings/string_piece.i: src/base/strings/string_piece.cc.i + +.PHONY : src/base/strings/string_piece.i + +# target to preprocess a source file +src/base/strings/string_piece.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_piece.cc.i +.PHONY : src/base/strings/string_piece.cc.i + +src/base/strings/string_piece.s: src/base/strings/string_piece.cc.s + +.PHONY : src/base/strings/string_piece.s + +# target to generate assembly for a file +src/base/strings/string_piece.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_piece.cc.s +.PHONY : src/base/strings/string_piece.cc.s + +src/base/strings/string_split.o: src/base/strings/string_split.cc.o + +.PHONY : src/base/strings/string_split.o + +# target to build an object file +src/base/strings/string_split.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_split.cc.o +.PHONY : src/base/strings/string_split.cc.o + +src/base/strings/string_split.i: src/base/strings/string_split.cc.i + +.PHONY : src/base/strings/string_split.i + +# target to preprocess a source file +src/base/strings/string_split.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_split.cc.i +.PHONY : src/base/strings/string_split.cc.i + +src/base/strings/string_split.s: src/base/strings/string_split.cc.s + +.PHONY : src/base/strings/string_split.s + +# target to generate assembly for a file +src/base/strings/string_split.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_split.cc.s +.PHONY : src/base/strings/string_split.cc.s + +src/base/strings/string_util.o: src/base/strings/string_util.cc.o + +.PHONY : src/base/strings/string_util.o + +# target to build an object file +src/base/strings/string_util.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_util.cc.o +.PHONY : src/base/strings/string_util.cc.o + +src/base/strings/string_util.i: src/base/strings/string_util.cc.i + +.PHONY : src/base/strings/string_util.i + +# target to preprocess a source file +src/base/strings/string_util.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_util.cc.i +.PHONY : src/base/strings/string_util.cc.i + +src/base/strings/string_util.s: src/base/strings/string_util.cc.s + +.PHONY : src/base/strings/string_util.s + +# target to generate assembly for a file +src/base/strings/string_util.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_util.cc.s +.PHONY : src/base/strings/string_util.cc.s + +src/base/strings/string_util_constants.o: src/base/strings/string_util_constants.cc.o + +.PHONY : src/base/strings/string_util_constants.o + +# target to build an object file +src/base/strings/string_util_constants.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_util_constants.cc.o +.PHONY : src/base/strings/string_util_constants.cc.o + +src/base/strings/string_util_constants.i: src/base/strings/string_util_constants.cc.i + +.PHONY : src/base/strings/string_util_constants.i + +# target to preprocess a source file +src/base/strings/string_util_constants.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_util_constants.cc.i +.PHONY : src/base/strings/string_util_constants.cc.i + +src/base/strings/string_util_constants.s: src/base/strings/string_util_constants.cc.s + +.PHONY : src/base/strings/string_util_constants.s + +# target to generate assembly for a file +src/base/strings/string_util_constants.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/string_util_constants.cc.s +.PHONY : src/base/strings/string_util_constants.cc.s + +src/base/strings/stringprintf.o: src/base/strings/stringprintf.cc.o + +.PHONY : src/base/strings/stringprintf.o + +# target to build an object file +src/base/strings/stringprintf.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/stringprintf.cc.o +.PHONY : src/base/strings/stringprintf.cc.o + +src/base/strings/stringprintf.i: src/base/strings/stringprintf.cc.i + +.PHONY : src/base/strings/stringprintf.i + +# target to preprocess a source file +src/base/strings/stringprintf.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/stringprintf.cc.i +.PHONY : src/base/strings/stringprintf.cc.i + +src/base/strings/stringprintf.s: src/base/strings/stringprintf.cc.s + +.PHONY : src/base/strings/stringprintf.s + +# target to generate assembly for a file +src/base/strings/stringprintf.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/stringprintf.cc.s +.PHONY : src/base/strings/stringprintf.cc.s + +src/base/strings/sys_string_conversions_mac.o: src/base/strings/sys_string_conversions_mac.mm.o + +.PHONY : src/base/strings/sys_string_conversions_mac.o + +# target to build an object file +src/base/strings/sys_string_conversions_mac.mm.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/sys_string_conversions_mac.mm.o +.PHONY : src/base/strings/sys_string_conversions_mac.mm.o + +src/base/strings/sys_string_conversions_mac.i: src/base/strings/sys_string_conversions_mac.mm.i + +.PHONY : src/base/strings/sys_string_conversions_mac.i + +# target to preprocess a source file +src/base/strings/sys_string_conversions_mac.mm.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/sys_string_conversions_mac.mm.i +.PHONY : src/base/strings/sys_string_conversions_mac.mm.i + +src/base/strings/sys_string_conversions_mac.s: src/base/strings/sys_string_conversions_mac.mm.s + +.PHONY : src/base/strings/sys_string_conversions_mac.s + +# target to generate assembly for a file +src/base/strings/sys_string_conversions_mac.mm.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/sys_string_conversions_mac.mm.s +.PHONY : src/base/strings/sys_string_conversions_mac.mm.s + +src/base/strings/sys_string_conversions_posix.o: src/base/strings/sys_string_conversions_posix.cc.o + +.PHONY : src/base/strings/sys_string_conversions_posix.o + +# target to build an object file +src/base/strings/sys_string_conversions_posix.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/sys_string_conversions_posix.cc.o +.PHONY : src/base/strings/sys_string_conversions_posix.cc.o + +src/base/strings/sys_string_conversions_posix.i: src/base/strings/sys_string_conversions_posix.cc.i + +.PHONY : src/base/strings/sys_string_conversions_posix.i + +# target to preprocess a source file +src/base/strings/sys_string_conversions_posix.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/sys_string_conversions_posix.cc.i +.PHONY : src/base/strings/sys_string_conversions_posix.cc.i + +src/base/strings/sys_string_conversions_posix.s: src/base/strings/sys_string_conversions_posix.cc.s + +.PHONY : src/base/strings/sys_string_conversions_posix.s + +# target to generate assembly for a file +src/base/strings/sys_string_conversions_posix.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/sys_string_conversions_posix.cc.s +.PHONY : src/base/strings/sys_string_conversions_posix.cc.s + +src/base/strings/utf_string_conversion_utils.o: src/base/strings/utf_string_conversion_utils.cc.o + +.PHONY : src/base/strings/utf_string_conversion_utils.o + +# target to build an object file +src/base/strings/utf_string_conversion_utils.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/utf_string_conversion_utils.cc.o +.PHONY : src/base/strings/utf_string_conversion_utils.cc.o + +src/base/strings/utf_string_conversion_utils.i: src/base/strings/utf_string_conversion_utils.cc.i + +.PHONY : src/base/strings/utf_string_conversion_utils.i + +# target to preprocess a source file +src/base/strings/utf_string_conversion_utils.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/utf_string_conversion_utils.cc.i +.PHONY : src/base/strings/utf_string_conversion_utils.cc.i + +src/base/strings/utf_string_conversion_utils.s: src/base/strings/utf_string_conversion_utils.cc.s + +.PHONY : src/base/strings/utf_string_conversion_utils.s + +# target to generate assembly for a file +src/base/strings/utf_string_conversion_utils.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/utf_string_conversion_utils.cc.s +.PHONY : src/base/strings/utf_string_conversion_utils.cc.s + +src/base/strings/utf_string_conversions.o: src/base/strings/utf_string_conversions.cc.o + +.PHONY : src/base/strings/utf_string_conversions.o + +# target to build an object file +src/base/strings/utf_string_conversions.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/utf_string_conversions.cc.o +.PHONY : src/base/strings/utf_string_conversions.cc.o + +src/base/strings/utf_string_conversions.i: src/base/strings/utf_string_conversions.cc.i + +.PHONY : src/base/strings/utf_string_conversions.i + +# target to preprocess a source file +src/base/strings/utf_string_conversions.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/utf_string_conversions.cc.i +.PHONY : src/base/strings/utf_string_conversions.cc.i + +src/base/strings/utf_string_conversions.s: src/base/strings/utf_string_conversions.cc.s + +.PHONY : src/base/strings/utf_string_conversions.s + +# target to generate assembly for a file +src/base/strings/utf_string_conversions.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/strings/utf_string_conversions.cc.s +.PHONY : src/base/strings/utf_string_conversions.cc.s + +src/base/synchronization/condition_variable_posix.o: src/base/synchronization/condition_variable_posix.cc.o + +.PHONY : src/base/synchronization/condition_variable_posix.o + +# target to build an object file +src/base/synchronization/condition_variable_posix.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/synchronization/condition_variable_posix.cc.o +.PHONY : src/base/synchronization/condition_variable_posix.cc.o + +src/base/synchronization/condition_variable_posix.i: src/base/synchronization/condition_variable_posix.cc.i + +.PHONY : src/base/synchronization/condition_variable_posix.i + +# target to preprocess a source file +src/base/synchronization/condition_variable_posix.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/synchronization/condition_variable_posix.cc.i +.PHONY : src/base/synchronization/condition_variable_posix.cc.i + +src/base/synchronization/condition_variable_posix.s: src/base/synchronization/condition_variable_posix.cc.s + +.PHONY : src/base/synchronization/condition_variable_posix.s + +# target to generate assembly for a file +src/base/synchronization/condition_variable_posix.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/synchronization/condition_variable_posix.cc.s +.PHONY : src/base/synchronization/condition_variable_posix.cc.s + +src/base/synchronization/lock.o: src/base/synchronization/lock.cc.o + +.PHONY : src/base/synchronization/lock.o + +# target to build an object file +src/base/synchronization/lock.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/synchronization/lock.cc.o +.PHONY : src/base/synchronization/lock.cc.o + +src/base/synchronization/lock.i: src/base/synchronization/lock.cc.i + +.PHONY : src/base/synchronization/lock.i + +# target to preprocess a source file +src/base/synchronization/lock.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/synchronization/lock.cc.i +.PHONY : src/base/synchronization/lock.cc.i + +src/base/synchronization/lock.s: src/base/synchronization/lock.cc.s + +.PHONY : src/base/synchronization/lock.s + +# target to generate assembly for a file +src/base/synchronization/lock.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/synchronization/lock.cc.s +.PHONY : src/base/synchronization/lock.cc.s + +src/base/synchronization/lock_impl_posix.o: src/base/synchronization/lock_impl_posix.cc.o + +.PHONY : src/base/synchronization/lock_impl_posix.o + +# target to build an object file +src/base/synchronization/lock_impl_posix.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/synchronization/lock_impl_posix.cc.o +.PHONY : src/base/synchronization/lock_impl_posix.cc.o + +src/base/synchronization/lock_impl_posix.i: src/base/synchronization/lock_impl_posix.cc.i + +.PHONY : src/base/synchronization/lock_impl_posix.i + +# target to preprocess a source file +src/base/synchronization/lock_impl_posix.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/synchronization/lock_impl_posix.cc.i +.PHONY : src/base/synchronization/lock_impl_posix.cc.i + +src/base/synchronization/lock_impl_posix.s: src/base/synchronization/lock_impl_posix.cc.s + +.PHONY : src/base/synchronization/lock_impl_posix.s + +# target to generate assembly for a file +src/base/synchronization/lock_impl_posix.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/synchronization/lock_impl_posix.cc.s +.PHONY : src/base/synchronization/lock_impl_posix.cc.s + +src/base/third_party/dmg_fp/dtoa.o: src/base/third_party/dmg_fp/dtoa.cc.o + +.PHONY : src/base/third_party/dmg_fp/dtoa.o + +# target to build an object file +src/base/third_party/dmg_fp/dtoa.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/dmg_fp/dtoa.cc.o +.PHONY : src/base/third_party/dmg_fp/dtoa.cc.o + +src/base/third_party/dmg_fp/dtoa.i: src/base/third_party/dmg_fp/dtoa.cc.i + +.PHONY : src/base/third_party/dmg_fp/dtoa.i + +# target to preprocess a source file +src/base/third_party/dmg_fp/dtoa.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/dmg_fp/dtoa.cc.i +.PHONY : src/base/third_party/dmg_fp/dtoa.cc.i + +src/base/third_party/dmg_fp/dtoa.s: src/base/third_party/dmg_fp/dtoa.cc.s + +.PHONY : src/base/third_party/dmg_fp/dtoa.s + +# target to generate assembly for a file +src/base/third_party/dmg_fp/dtoa.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/dmg_fp/dtoa.cc.s +.PHONY : src/base/third_party/dmg_fp/dtoa.cc.s + +src/base/third_party/dmg_fp/g_fmt.o: src/base/third_party/dmg_fp/g_fmt.cc.o + +.PHONY : src/base/third_party/dmg_fp/g_fmt.o + +# target to build an object file +src/base/third_party/dmg_fp/g_fmt.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/dmg_fp/g_fmt.cc.o +.PHONY : src/base/third_party/dmg_fp/g_fmt.cc.o + +src/base/third_party/dmg_fp/g_fmt.i: src/base/third_party/dmg_fp/g_fmt.cc.i + +.PHONY : src/base/third_party/dmg_fp/g_fmt.i + +# target to preprocess a source file +src/base/third_party/dmg_fp/g_fmt.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/dmg_fp/g_fmt.cc.i +.PHONY : src/base/third_party/dmg_fp/g_fmt.cc.i + +src/base/third_party/dmg_fp/g_fmt.s: src/base/third_party/dmg_fp/g_fmt.cc.s + +.PHONY : src/base/third_party/dmg_fp/g_fmt.s + +# target to generate assembly for a file +src/base/third_party/dmg_fp/g_fmt.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/dmg_fp/g_fmt.cc.s +.PHONY : src/base/third_party/dmg_fp/g_fmt.cc.s + +src/base/third_party/icu/icu_utf.o: src/base/third_party/icu/icu_utf.cc.o + +.PHONY : src/base/third_party/icu/icu_utf.o + +# target to build an object file +src/base/third_party/icu/icu_utf.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/icu/icu_utf.cc.o +.PHONY : src/base/third_party/icu/icu_utf.cc.o + +src/base/third_party/icu/icu_utf.i: src/base/third_party/icu/icu_utf.cc.i + +.PHONY : src/base/third_party/icu/icu_utf.i + +# target to preprocess a source file +src/base/third_party/icu/icu_utf.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/icu/icu_utf.cc.i +.PHONY : src/base/third_party/icu/icu_utf.cc.i + +src/base/third_party/icu/icu_utf.s: src/base/third_party/icu/icu_utf.cc.s + +.PHONY : src/base/third_party/icu/icu_utf.s + +# target to generate assembly for a file +src/base/third_party/icu/icu_utf.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/icu/icu_utf.cc.s +.PHONY : src/base/third_party/icu/icu_utf.cc.s + +src/base/third_party/nspr/prtime.o: src/base/third_party/nspr/prtime.cc.o + +.PHONY : src/base/third_party/nspr/prtime.o + +# target to build an object file +src/base/third_party/nspr/prtime.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/nspr/prtime.cc.o +.PHONY : src/base/third_party/nspr/prtime.cc.o + +src/base/third_party/nspr/prtime.i: src/base/third_party/nspr/prtime.cc.i + +.PHONY : src/base/third_party/nspr/prtime.i + +# target to preprocess a source file +src/base/third_party/nspr/prtime.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/nspr/prtime.cc.i +.PHONY : src/base/third_party/nspr/prtime.cc.i + +src/base/third_party/nspr/prtime.s: src/base/third_party/nspr/prtime.cc.s + +.PHONY : src/base/third_party/nspr/prtime.s + +# target to generate assembly for a file +src/base/third_party/nspr/prtime.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/nspr/prtime.cc.s +.PHONY : src/base/third_party/nspr/prtime.cc.s + +src/base/third_party/superfasthash/superfasthash.o: src/base/third_party/superfasthash/superfasthash.c.o + +.PHONY : src/base/third_party/superfasthash/superfasthash.o + +# target to build an object file +src/base/third_party/superfasthash/superfasthash.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/superfasthash/superfasthash.c.o +.PHONY : src/base/third_party/superfasthash/superfasthash.c.o + +src/base/third_party/superfasthash/superfasthash.i: src/base/third_party/superfasthash/superfasthash.c.i + +.PHONY : src/base/third_party/superfasthash/superfasthash.i + +# target to preprocess a source file +src/base/third_party/superfasthash/superfasthash.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/superfasthash/superfasthash.c.i +.PHONY : src/base/third_party/superfasthash/superfasthash.c.i + +src/base/third_party/superfasthash/superfasthash.s: src/base/third_party/superfasthash/superfasthash.c.s + +.PHONY : src/base/third_party/superfasthash/superfasthash.s + +# target to generate assembly for a file +src/base/third_party/superfasthash/superfasthash.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/third_party/superfasthash/superfasthash.c.s +.PHONY : src/base/third_party/superfasthash/superfasthash.c.s + +src/base/threading/platform_thread_internal_posix.o: src/base/threading/platform_thread_internal_posix.cc.o + +.PHONY : src/base/threading/platform_thread_internal_posix.o + +# target to build an object file +src/base/threading/platform_thread_internal_posix.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/platform_thread_internal_posix.cc.o +.PHONY : src/base/threading/platform_thread_internal_posix.cc.o + +src/base/threading/platform_thread_internal_posix.i: src/base/threading/platform_thread_internal_posix.cc.i + +.PHONY : src/base/threading/platform_thread_internal_posix.i + +# target to preprocess a source file +src/base/threading/platform_thread_internal_posix.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/platform_thread_internal_posix.cc.i +.PHONY : src/base/threading/platform_thread_internal_posix.cc.i + +src/base/threading/platform_thread_internal_posix.s: src/base/threading/platform_thread_internal_posix.cc.s + +.PHONY : src/base/threading/platform_thread_internal_posix.s + +# target to generate assembly for a file +src/base/threading/platform_thread_internal_posix.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/platform_thread_internal_posix.cc.s +.PHONY : src/base/threading/platform_thread_internal_posix.cc.s + +src/base/threading/platform_thread_mac.o: src/base/threading/platform_thread_mac.mm.o + +.PHONY : src/base/threading/platform_thread_mac.o + +# target to build an object file +src/base/threading/platform_thread_mac.mm.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/platform_thread_mac.mm.o +.PHONY : src/base/threading/platform_thread_mac.mm.o + +src/base/threading/platform_thread_mac.i: src/base/threading/platform_thread_mac.mm.i + +.PHONY : src/base/threading/platform_thread_mac.i + +# target to preprocess a source file +src/base/threading/platform_thread_mac.mm.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/platform_thread_mac.mm.i +.PHONY : src/base/threading/platform_thread_mac.mm.i + +src/base/threading/platform_thread_mac.s: src/base/threading/platform_thread_mac.mm.s + +.PHONY : src/base/threading/platform_thread_mac.s + +# target to generate assembly for a file +src/base/threading/platform_thread_mac.mm.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/platform_thread_mac.mm.s +.PHONY : src/base/threading/platform_thread_mac.mm.s + +src/base/threading/platform_thread_posix.o: src/base/threading/platform_thread_posix.cc.o + +.PHONY : src/base/threading/platform_thread_posix.o + +# target to build an object file +src/base/threading/platform_thread_posix.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/platform_thread_posix.cc.o +.PHONY : src/base/threading/platform_thread_posix.cc.o + +src/base/threading/platform_thread_posix.i: src/base/threading/platform_thread_posix.cc.i + +.PHONY : src/base/threading/platform_thread_posix.i + +# target to preprocess a source file +src/base/threading/platform_thread_posix.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/platform_thread_posix.cc.i +.PHONY : src/base/threading/platform_thread_posix.cc.i + +src/base/threading/platform_thread_posix.s: src/base/threading/platform_thread_posix.cc.s + +.PHONY : src/base/threading/platform_thread_posix.s + +# target to generate assembly for a file +src/base/threading/platform_thread_posix.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/platform_thread_posix.cc.s +.PHONY : src/base/threading/platform_thread_posix.cc.s + +src/base/threading/thread_collision_warner.o: src/base/threading/thread_collision_warner.cc.o + +.PHONY : src/base/threading/thread_collision_warner.o + +# target to build an object file +src/base/threading/thread_collision_warner.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_collision_warner.cc.o +.PHONY : src/base/threading/thread_collision_warner.cc.o + +src/base/threading/thread_collision_warner.i: src/base/threading/thread_collision_warner.cc.i + +.PHONY : src/base/threading/thread_collision_warner.i + +# target to preprocess a source file +src/base/threading/thread_collision_warner.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_collision_warner.cc.i +.PHONY : src/base/threading/thread_collision_warner.cc.i + +src/base/threading/thread_collision_warner.s: src/base/threading/thread_collision_warner.cc.s + +.PHONY : src/base/threading/thread_collision_warner.s + +# target to generate assembly for a file +src/base/threading/thread_collision_warner.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_collision_warner.cc.s +.PHONY : src/base/threading/thread_collision_warner.cc.s + +src/base/threading/thread_id_name_manager.o: src/base/threading/thread_id_name_manager.cc.o + +.PHONY : src/base/threading/thread_id_name_manager.o + +# target to build an object file +src/base/threading/thread_id_name_manager.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_id_name_manager.cc.o +.PHONY : src/base/threading/thread_id_name_manager.cc.o + +src/base/threading/thread_id_name_manager.i: src/base/threading/thread_id_name_manager.cc.i + +.PHONY : src/base/threading/thread_id_name_manager.i + +# target to preprocess a source file +src/base/threading/thread_id_name_manager.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_id_name_manager.cc.i +.PHONY : src/base/threading/thread_id_name_manager.cc.i + +src/base/threading/thread_id_name_manager.s: src/base/threading/thread_id_name_manager.cc.s + +.PHONY : src/base/threading/thread_id_name_manager.s + +# target to generate assembly for a file +src/base/threading/thread_id_name_manager.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_id_name_manager.cc.s +.PHONY : src/base/threading/thread_id_name_manager.cc.s + +src/base/threading/thread_local_posix.o: src/base/threading/thread_local_posix.cc.o + +.PHONY : src/base/threading/thread_local_posix.o + +# target to build an object file +src/base/threading/thread_local_posix.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_local_posix.cc.o +.PHONY : src/base/threading/thread_local_posix.cc.o + +src/base/threading/thread_local_posix.i: src/base/threading/thread_local_posix.cc.i + +.PHONY : src/base/threading/thread_local_posix.i + +# target to preprocess a source file +src/base/threading/thread_local_posix.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_local_posix.cc.i +.PHONY : src/base/threading/thread_local_posix.cc.i + +src/base/threading/thread_local_posix.s: src/base/threading/thread_local_posix.cc.s + +.PHONY : src/base/threading/thread_local_posix.s + +# target to generate assembly for a file +src/base/threading/thread_local_posix.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_local_posix.cc.s +.PHONY : src/base/threading/thread_local_posix.cc.s + +src/base/threading/thread_local_storage.o: src/base/threading/thread_local_storage.cc.o + +.PHONY : src/base/threading/thread_local_storage.o + +# target to build an object file +src/base/threading/thread_local_storage.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_local_storage.cc.o +.PHONY : src/base/threading/thread_local_storage.cc.o + +src/base/threading/thread_local_storage.i: src/base/threading/thread_local_storage.cc.i + +.PHONY : src/base/threading/thread_local_storage.i + +# target to preprocess a source file +src/base/threading/thread_local_storage.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_local_storage.cc.i +.PHONY : src/base/threading/thread_local_storage.cc.i + +src/base/threading/thread_local_storage.s: src/base/threading/thread_local_storage.cc.s + +.PHONY : src/base/threading/thread_local_storage.s + +# target to generate assembly for a file +src/base/threading/thread_local_storage.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_local_storage.cc.s +.PHONY : src/base/threading/thread_local_storage.cc.s + +src/base/threading/thread_local_storage_posix.o: src/base/threading/thread_local_storage_posix.cc.o + +.PHONY : src/base/threading/thread_local_storage_posix.o + +# target to build an object file +src/base/threading/thread_local_storage_posix.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_local_storage_posix.cc.o +.PHONY : src/base/threading/thread_local_storage_posix.cc.o + +src/base/threading/thread_local_storage_posix.i: src/base/threading/thread_local_storage_posix.cc.i + +.PHONY : src/base/threading/thread_local_storage_posix.i + +# target to preprocess a source file +src/base/threading/thread_local_storage_posix.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_local_storage_posix.cc.i +.PHONY : src/base/threading/thread_local_storage_posix.cc.i + +src/base/threading/thread_local_storage_posix.s: src/base/threading/thread_local_storage_posix.cc.s + +.PHONY : src/base/threading/thread_local_storage_posix.s + +# target to generate assembly for a file +src/base/threading/thread_local_storage_posix.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_local_storage_posix.cc.s +.PHONY : src/base/threading/thread_local_storage_posix.cc.s + +src/base/threading/thread_restrictions.o: src/base/threading/thread_restrictions.cc.o + +.PHONY : src/base/threading/thread_restrictions.o + +# target to build an object file +src/base/threading/thread_restrictions.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_restrictions.cc.o +.PHONY : src/base/threading/thread_restrictions.cc.o + +src/base/threading/thread_restrictions.i: src/base/threading/thread_restrictions.cc.i + +.PHONY : src/base/threading/thread_restrictions.i + +# target to preprocess a source file +src/base/threading/thread_restrictions.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_restrictions.cc.i +.PHONY : src/base/threading/thread_restrictions.cc.i + +src/base/threading/thread_restrictions.s: src/base/threading/thread_restrictions.cc.s + +.PHONY : src/base/threading/thread_restrictions.s + +# target to generate assembly for a file +src/base/threading/thread_restrictions.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/threading/thread_restrictions.cc.s +.PHONY : src/base/threading/thread_restrictions.cc.s + +src/base/time/time.o: src/base/time/time.cc.o + +.PHONY : src/base/time/time.o + +# target to build an object file +src/base/time/time.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/time/time.cc.o +.PHONY : src/base/time/time.cc.o + +src/base/time/time.i: src/base/time/time.cc.i + +.PHONY : src/base/time/time.i + +# target to preprocess a source file +src/base/time/time.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/time/time.cc.i +.PHONY : src/base/time/time.cc.i + +src/base/time/time.s: src/base/time/time.cc.s + +.PHONY : src/base/time/time.s + +# target to generate assembly for a file +src/base/time/time.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/time/time.cc.s +.PHONY : src/base/time/time.cc.s + +src/base/time/time_mac.o: src/base/time/time_mac.cc.o + +.PHONY : src/base/time/time_mac.o + +# target to build an object file +src/base/time/time_mac.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/time/time_mac.cc.o +.PHONY : src/base/time/time_mac.cc.o + +src/base/time/time_mac.i: src/base/time/time_mac.cc.i + +.PHONY : src/base/time/time_mac.i + +# target to preprocess a source file +src/base/time/time_mac.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/time/time_mac.cc.i +.PHONY : src/base/time/time_mac.cc.i + +src/base/time/time_mac.s: src/base/time/time_mac.cc.s + +.PHONY : src/base/time/time_mac.s + +# target to generate assembly for a file +src/base/time/time_mac.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/time/time_mac.cc.s +.PHONY : src/base/time/time_mac.cc.s + +src/base/time/time_posix.o: src/base/time/time_posix.cc.o + +.PHONY : src/base/time/time_posix.o + +# target to build an object file +src/base/time/time_posix.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/time/time_posix.cc.o +.PHONY : src/base/time/time_posix.cc.o + +src/base/time/time_posix.i: src/base/time/time_posix.cc.i + +.PHONY : src/base/time/time_posix.i + +# target to preprocess a source file +src/base/time/time_posix.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/time/time_posix.cc.i +.PHONY : src/base/time/time_posix.cc.i + +src/base/time/time_posix.s: src/base/time/time_posix.cc.s + +.PHONY : src/base/time/time_posix.s + +# target to generate assembly for a file +src/base/time/time_posix.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/time/time_posix.cc.s +.PHONY : src/base/time/time_posix.cc.s + +src/base/values.o: src/base/values.cc.o + +.PHONY : src/base/values.o + +# target to build an object file +src/base/values.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/values.cc.o +.PHONY : src/base/values.cc.o + +src/base/values.i: src/base/values.cc.i + +.PHONY : src/base/values.i + +# target to preprocess a source file +src/base/values.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/values.cc.i +.PHONY : src/base/values.cc.i + +src/base/values.s: src/base/values.cc.s + +.PHONY : src/base/values.s + +# target to generate assembly for a file +src/base/values.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/values.cc.s +.PHONY : src/base/values.cc.s + +src/base/vlog.o: src/base/vlog.cc.o + +.PHONY : src/base/vlog.o + +# target to build an object file +src/base/vlog.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/vlog.cc.o +.PHONY : src/base/vlog.cc.o + +src/base/vlog.i: src/base/vlog.cc.i + +.PHONY : src/base/vlog.i + +# target to preprocess a source file +src/base/vlog.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/vlog.cc.i +.PHONY : src/base/vlog.cc.i + +src/base/vlog.s: src/base/vlog.cc.s + +.PHONY : src/base/vlog.s + +# target to generate assembly for a file +src/base/vlog.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/base/vlog.cc.s +.PHONY : src/base/vlog.cc.s + +src/crypto/curve25519-donna.o: src/crypto/curve25519-donna.c.o + +.PHONY : src/crypto/curve25519-donna.o + +# target to build an object file +src/crypto/curve25519-donna.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/curve25519-donna.c.o +.PHONY : src/crypto/curve25519-donna.c.o + +src/crypto/curve25519-donna.i: src/crypto/curve25519-donna.c.i + +.PHONY : src/crypto/curve25519-donna.i + +# target to preprocess a source file +src/crypto/curve25519-donna.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/curve25519-donna.c.i +.PHONY : src/crypto/curve25519-donna.c.i + +src/crypto/curve25519-donna.s: src/crypto/curve25519-donna.c.s + +.PHONY : src/crypto/curve25519-donna.s + +# target to generate assembly for a file +src/crypto/curve25519-donna.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/curve25519-donna.c.s +.PHONY : src/crypto/curve25519-donna.c.s + +src/crypto/curve25519_openssl.o: src/crypto/curve25519_openssl.cc.o + +.PHONY : src/crypto/curve25519_openssl.o + +# target to build an object file +src/crypto/curve25519_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/curve25519_openssl.cc.o +.PHONY : src/crypto/curve25519_openssl.cc.o + +src/crypto/curve25519_openssl.i: src/crypto/curve25519_openssl.cc.i + +.PHONY : src/crypto/curve25519_openssl.i + +# target to preprocess a source file +src/crypto/curve25519_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/curve25519_openssl.cc.i +.PHONY : src/crypto/curve25519_openssl.cc.i + +src/crypto/curve25519_openssl.s: src/crypto/curve25519_openssl.cc.s + +.PHONY : src/crypto/curve25519_openssl.s + +# target to generate assembly for a file +src/crypto/curve25519_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/curve25519_openssl.cc.s +.PHONY : src/crypto/curve25519_openssl.cc.s + +src/crypto/hkdf.o: src/crypto/hkdf.cc.o + +.PHONY : src/crypto/hkdf.o + +# target to build an object file +src/crypto/hkdf.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/hkdf.cc.o +.PHONY : src/crypto/hkdf.cc.o + +src/crypto/hkdf.i: src/crypto/hkdf.cc.i + +.PHONY : src/crypto/hkdf.i + +# target to preprocess a source file +src/crypto/hkdf.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/hkdf.cc.i +.PHONY : src/crypto/hkdf.cc.i + +src/crypto/hkdf.s: src/crypto/hkdf.cc.s + +.PHONY : src/crypto/hkdf.s + +# target to generate assembly for a file +src/crypto/hkdf.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/hkdf.cc.s +.PHONY : src/crypto/hkdf.cc.s + +src/crypto/hmac.o: src/crypto/hmac.cc.o + +.PHONY : src/crypto/hmac.o + +# target to build an object file +src/crypto/hmac.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/hmac.cc.o +.PHONY : src/crypto/hmac.cc.o + +src/crypto/hmac.i: src/crypto/hmac.cc.i + +.PHONY : src/crypto/hmac.i + +# target to preprocess a source file +src/crypto/hmac.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/hmac.cc.i +.PHONY : src/crypto/hmac.cc.i + +src/crypto/hmac.s: src/crypto/hmac.cc.s + +.PHONY : src/crypto/hmac.s + +# target to generate assembly for a file +src/crypto/hmac.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/hmac.cc.s +.PHONY : src/crypto/hmac.cc.s + +src/crypto/hmac_openssl.o: src/crypto/hmac_openssl.cc.o + +.PHONY : src/crypto/hmac_openssl.o + +# target to build an object file +src/crypto/hmac_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/hmac_openssl.cc.o +.PHONY : src/crypto/hmac_openssl.cc.o + +src/crypto/hmac_openssl.i: src/crypto/hmac_openssl.cc.i + +.PHONY : src/crypto/hmac_openssl.i + +# target to preprocess a source file +src/crypto/hmac_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/hmac_openssl.cc.i +.PHONY : src/crypto/hmac_openssl.cc.i + +src/crypto/hmac_openssl.s: src/crypto/hmac_openssl.cc.s + +.PHONY : src/crypto/hmac_openssl.s + +# target to generate assembly for a file +src/crypto/hmac_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/hmac_openssl.cc.s +.PHONY : src/crypto/hmac_openssl.cc.s + +src/crypto/openssl_util.o: src/crypto/openssl_util.cc.o + +.PHONY : src/crypto/openssl_util.o + +# target to build an object file +src/crypto/openssl_util.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/openssl_util.cc.o +.PHONY : src/crypto/openssl_util.cc.o + +src/crypto/openssl_util.i: src/crypto/openssl_util.cc.i + +.PHONY : src/crypto/openssl_util.i + +# target to preprocess a source file +src/crypto/openssl_util.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/openssl_util.cc.i +.PHONY : src/crypto/openssl_util.cc.i + +src/crypto/openssl_util.s: src/crypto/openssl_util.cc.s + +.PHONY : src/crypto/openssl_util.s + +# target to generate assembly for a file +src/crypto/openssl_util.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/openssl_util.cc.s +.PHONY : src/crypto/openssl_util.cc.s + +src/crypto/random.o: src/crypto/random.cc.o + +.PHONY : src/crypto/random.o + +# target to build an object file +src/crypto/random.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/random.cc.o +.PHONY : src/crypto/random.cc.o + +src/crypto/random.i: src/crypto/random.cc.i + +.PHONY : src/crypto/random.i + +# target to preprocess a source file +src/crypto/random.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/random.cc.i +.PHONY : src/crypto/random.cc.i + +src/crypto/random.s: src/crypto/random.cc.s + +.PHONY : src/crypto/random.s + +# target to generate assembly for a file +src/crypto/random.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/random.cc.s +.PHONY : src/crypto/random.cc.s + +src/crypto/secure_hash_openssl.o: src/crypto/secure_hash_openssl.cc.o + +.PHONY : src/crypto/secure_hash_openssl.o + +# target to build an object file +src/crypto/secure_hash_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/secure_hash_openssl.cc.o +.PHONY : src/crypto/secure_hash_openssl.cc.o + +src/crypto/secure_hash_openssl.i: src/crypto/secure_hash_openssl.cc.i + +.PHONY : src/crypto/secure_hash_openssl.i + +# target to preprocess a source file +src/crypto/secure_hash_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/secure_hash_openssl.cc.i +.PHONY : src/crypto/secure_hash_openssl.cc.i + +src/crypto/secure_hash_openssl.s: src/crypto/secure_hash_openssl.cc.s + +.PHONY : src/crypto/secure_hash_openssl.s + +# target to generate assembly for a file +src/crypto/secure_hash_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/secure_hash_openssl.cc.s +.PHONY : src/crypto/secure_hash_openssl.cc.s + +src/crypto/secure_util.o: src/crypto/secure_util.cc.o + +.PHONY : src/crypto/secure_util.o + +# target to build an object file +src/crypto/secure_util.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/secure_util.cc.o +.PHONY : src/crypto/secure_util.cc.o + +src/crypto/secure_util.i: src/crypto/secure_util.cc.i + +.PHONY : src/crypto/secure_util.i + +# target to preprocess a source file +src/crypto/secure_util.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/secure_util.cc.i +.PHONY : src/crypto/secure_util.cc.i + +src/crypto/secure_util.s: src/crypto/secure_util.cc.s + +.PHONY : src/crypto/secure_util.s + +# target to generate assembly for a file +src/crypto/secure_util.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/secure_util.cc.s +.PHONY : src/crypto/secure_util.cc.s + +src/crypto/symmetric_key_openssl.o: src/crypto/symmetric_key_openssl.cc.o + +.PHONY : src/crypto/symmetric_key_openssl.o + +# target to build an object file +src/crypto/symmetric_key_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/symmetric_key_openssl.cc.o +.PHONY : src/crypto/symmetric_key_openssl.cc.o + +src/crypto/symmetric_key_openssl.i: src/crypto/symmetric_key_openssl.cc.i + +.PHONY : src/crypto/symmetric_key_openssl.i + +# target to preprocess a source file +src/crypto/symmetric_key_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/symmetric_key_openssl.cc.i +.PHONY : src/crypto/symmetric_key_openssl.cc.i + +src/crypto/symmetric_key_openssl.s: src/crypto/symmetric_key_openssl.cc.s + +.PHONY : src/crypto/symmetric_key_openssl.s + +# target to generate assembly for a file +src/crypto/symmetric_key_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/crypto/symmetric_key_openssl.cc.s +.PHONY : src/crypto/symmetric_key_openssl.cc.s + +src/net/base/address_family.o: src/net/base/address_family.cc.o + +.PHONY : src/net/base/address_family.o + +# target to build an object file +src/net/base/address_family.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/address_family.cc.o +.PHONY : src/net/base/address_family.cc.o + +src/net/base/address_family.i: src/net/base/address_family.cc.i + +.PHONY : src/net/base/address_family.i + +# target to preprocess a source file +src/net/base/address_family.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/address_family.cc.i +.PHONY : src/net/base/address_family.cc.i + +src/net/base/address_family.s: src/net/base/address_family.cc.s + +.PHONY : src/net/base/address_family.s + +# target to generate assembly for a file +src/net/base/address_family.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/address_family.cc.s +.PHONY : src/net/base/address_family.cc.s + +src/net/base/host_port_pair.o: src/net/base/host_port_pair.cc.o + +.PHONY : src/net/base/host_port_pair.o + +# target to build an object file +src/net/base/host_port_pair.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/host_port_pair.cc.o +.PHONY : src/net/base/host_port_pair.cc.o + +src/net/base/host_port_pair.i: src/net/base/host_port_pair.cc.i + +.PHONY : src/net/base/host_port_pair.i + +# target to preprocess a source file +src/net/base/host_port_pair.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/host_port_pair.cc.i +.PHONY : src/net/base/host_port_pair.cc.i + +src/net/base/host_port_pair.s: src/net/base/host_port_pair.cc.s + +.PHONY : src/net/base/host_port_pair.s + +# target to generate assembly for a file +src/net/base/host_port_pair.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/host_port_pair.cc.s +.PHONY : src/net/base/host_port_pair.cc.s + +src/net/base/int128.o: src/net/base/int128.cc.o + +.PHONY : src/net/base/int128.o + +# target to build an object file +src/net/base/int128.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/int128.cc.o +.PHONY : src/net/base/int128.cc.o + +src/net/base/int128.i: src/net/base/int128.cc.i + +.PHONY : src/net/base/int128.i + +# target to preprocess a source file +src/net/base/int128.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/int128.cc.i +.PHONY : src/net/base/int128.cc.i + +src/net/base/int128.s: src/net/base/int128.cc.s + +.PHONY : src/net/base/int128.s + +# target to generate assembly for a file +src/net/base/int128.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/int128.cc.s +.PHONY : src/net/base/int128.cc.s + +src/net/base/io_buffer.o: src/net/base/io_buffer.cc.o + +.PHONY : src/net/base/io_buffer.o + +# target to build an object file +src/net/base/io_buffer.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/io_buffer.cc.o +.PHONY : src/net/base/io_buffer.cc.o + +src/net/base/io_buffer.i: src/net/base/io_buffer.cc.i + +.PHONY : src/net/base/io_buffer.i + +# target to preprocess a source file +src/net/base/io_buffer.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/io_buffer.cc.i +.PHONY : src/net/base/io_buffer.cc.i + +src/net/base/io_buffer.s: src/net/base/io_buffer.cc.s + +.PHONY : src/net/base/io_buffer.s + +# target to generate assembly for a file +src/net/base/io_buffer.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/io_buffer.cc.s +.PHONY : src/net/base/io_buffer.cc.s + +src/net/base/ip_endpoint.o: src/net/base/ip_endpoint.cc.o + +.PHONY : src/net/base/ip_endpoint.o + +# target to build an object file +src/net/base/ip_endpoint.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/ip_endpoint.cc.o +.PHONY : src/net/base/ip_endpoint.cc.o + +src/net/base/ip_endpoint.i: src/net/base/ip_endpoint.cc.i + +.PHONY : src/net/base/ip_endpoint.i + +# target to preprocess a source file +src/net/base/ip_endpoint.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/ip_endpoint.cc.i +.PHONY : src/net/base/ip_endpoint.cc.i + +src/net/base/ip_endpoint.s: src/net/base/ip_endpoint.cc.s + +.PHONY : src/net/base/ip_endpoint.s + +# target to generate assembly for a file +src/net/base/ip_endpoint.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/ip_endpoint.cc.s +.PHONY : src/net/base/ip_endpoint.cc.s + +src/net/base/net_errors.o: src/net/base/net_errors.cc.o + +.PHONY : src/net/base/net_errors.o + +# target to build an object file +src/net/base/net_errors.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/net_errors.cc.o +.PHONY : src/net/base/net_errors.cc.o + +src/net/base/net_errors.i: src/net/base/net_errors.cc.i + +.PHONY : src/net/base/net_errors.i + +# target to preprocess a source file +src/net/base/net_errors.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/net_errors.cc.i +.PHONY : src/net/base/net_errors.cc.i + +src/net/base/net_errors.s: src/net/base/net_errors.cc.s + +.PHONY : src/net/base/net_errors.s + +# target to generate assembly for a file +src/net/base/net_errors.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/net_errors.cc.s +.PHONY : src/net/base/net_errors.cc.s + +src/net/base/port_util.o: src/net/base/port_util.cc.o + +.PHONY : src/net/base/port_util.o + +# target to build an object file +src/net/base/port_util.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/port_util.cc.o +.PHONY : src/net/base/port_util.cc.o + +src/net/base/port_util.i: src/net/base/port_util.cc.i + +.PHONY : src/net/base/port_util.i + +# target to preprocess a source file +src/net/base/port_util.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/port_util.cc.i +.PHONY : src/net/base/port_util.cc.i + +src/net/base/port_util.s: src/net/base/port_util.cc.s + +.PHONY : src/net/base/port_util.s + +# target to generate assembly for a file +src/net/base/port_util.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/base/port_util.cc.s +.PHONY : src/net/base/port_util.cc.s + +src/net/quic/congestion_control/cubic.o: src/net/quic/congestion_control/cubic.cc.o + +.PHONY : src/net/quic/congestion_control/cubic.o + +# target to build an object file +src/net/quic/congestion_control/cubic.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/cubic.cc.o +.PHONY : src/net/quic/congestion_control/cubic.cc.o + +src/net/quic/congestion_control/cubic.i: src/net/quic/congestion_control/cubic.cc.i + +.PHONY : src/net/quic/congestion_control/cubic.i + +# target to preprocess a source file +src/net/quic/congestion_control/cubic.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/cubic.cc.i +.PHONY : src/net/quic/congestion_control/cubic.cc.i + +src/net/quic/congestion_control/cubic.s: src/net/quic/congestion_control/cubic.cc.s + +.PHONY : src/net/quic/congestion_control/cubic.s + +# target to generate assembly for a file +src/net/quic/congestion_control/cubic.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/cubic.cc.s +.PHONY : src/net/quic/congestion_control/cubic.cc.s + +src/net/quic/congestion_control/cubic_bytes.o: src/net/quic/congestion_control/cubic_bytes.cc.o + +.PHONY : src/net/quic/congestion_control/cubic_bytes.o + +# target to build an object file +src/net/quic/congestion_control/cubic_bytes.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/cubic_bytes.cc.o +.PHONY : src/net/quic/congestion_control/cubic_bytes.cc.o + +src/net/quic/congestion_control/cubic_bytes.i: src/net/quic/congestion_control/cubic_bytes.cc.i + +.PHONY : src/net/quic/congestion_control/cubic_bytes.i + +# target to preprocess a source file +src/net/quic/congestion_control/cubic_bytes.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/cubic_bytes.cc.i +.PHONY : src/net/quic/congestion_control/cubic_bytes.cc.i + +src/net/quic/congestion_control/cubic_bytes.s: src/net/quic/congestion_control/cubic_bytes.cc.s + +.PHONY : src/net/quic/congestion_control/cubic_bytes.s + +# target to generate assembly for a file +src/net/quic/congestion_control/cubic_bytes.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/cubic_bytes.cc.s +.PHONY : src/net/quic/congestion_control/cubic_bytes.cc.s + +src/net/quic/congestion_control/general_loss_algorithm.o: src/net/quic/congestion_control/general_loss_algorithm.cc.o + +.PHONY : src/net/quic/congestion_control/general_loss_algorithm.o + +# target to build an object file +src/net/quic/congestion_control/general_loss_algorithm.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/general_loss_algorithm.cc.o +.PHONY : src/net/quic/congestion_control/general_loss_algorithm.cc.o + +src/net/quic/congestion_control/general_loss_algorithm.i: src/net/quic/congestion_control/general_loss_algorithm.cc.i + +.PHONY : src/net/quic/congestion_control/general_loss_algorithm.i + +# target to preprocess a source file +src/net/quic/congestion_control/general_loss_algorithm.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/general_loss_algorithm.cc.i +.PHONY : src/net/quic/congestion_control/general_loss_algorithm.cc.i + +src/net/quic/congestion_control/general_loss_algorithm.s: src/net/quic/congestion_control/general_loss_algorithm.cc.s + +.PHONY : src/net/quic/congestion_control/general_loss_algorithm.s + +# target to generate assembly for a file +src/net/quic/congestion_control/general_loss_algorithm.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/general_loss_algorithm.cc.s +.PHONY : src/net/quic/congestion_control/general_loss_algorithm.cc.s + +src/net/quic/congestion_control/hybrid_slow_start.o: src/net/quic/congestion_control/hybrid_slow_start.cc.o + +.PHONY : src/net/quic/congestion_control/hybrid_slow_start.o + +# target to build an object file +src/net/quic/congestion_control/hybrid_slow_start.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/hybrid_slow_start.cc.o +.PHONY : src/net/quic/congestion_control/hybrid_slow_start.cc.o + +src/net/quic/congestion_control/hybrid_slow_start.i: src/net/quic/congestion_control/hybrid_slow_start.cc.i + +.PHONY : src/net/quic/congestion_control/hybrid_slow_start.i + +# target to preprocess a source file +src/net/quic/congestion_control/hybrid_slow_start.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/hybrid_slow_start.cc.i +.PHONY : src/net/quic/congestion_control/hybrid_slow_start.cc.i + +src/net/quic/congestion_control/hybrid_slow_start.s: src/net/quic/congestion_control/hybrid_slow_start.cc.s + +.PHONY : src/net/quic/congestion_control/hybrid_slow_start.s + +# target to generate assembly for a file +src/net/quic/congestion_control/hybrid_slow_start.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/hybrid_slow_start.cc.s +.PHONY : src/net/quic/congestion_control/hybrid_slow_start.cc.s + +src/net/quic/congestion_control/loss_detection_interface.o: src/net/quic/congestion_control/loss_detection_interface.cc.o + +.PHONY : src/net/quic/congestion_control/loss_detection_interface.o + +# target to build an object file +src/net/quic/congestion_control/loss_detection_interface.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/loss_detection_interface.cc.o +.PHONY : src/net/quic/congestion_control/loss_detection_interface.cc.o + +src/net/quic/congestion_control/loss_detection_interface.i: src/net/quic/congestion_control/loss_detection_interface.cc.i + +.PHONY : src/net/quic/congestion_control/loss_detection_interface.i + +# target to preprocess a source file +src/net/quic/congestion_control/loss_detection_interface.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/loss_detection_interface.cc.i +.PHONY : src/net/quic/congestion_control/loss_detection_interface.cc.i + +src/net/quic/congestion_control/loss_detection_interface.s: src/net/quic/congestion_control/loss_detection_interface.cc.s + +.PHONY : src/net/quic/congestion_control/loss_detection_interface.s + +# target to generate assembly for a file +src/net/quic/congestion_control/loss_detection_interface.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/loss_detection_interface.cc.s +.PHONY : src/net/quic/congestion_control/loss_detection_interface.cc.s + +src/net/quic/congestion_control/pacing_sender.o: src/net/quic/congestion_control/pacing_sender.cc.o + +.PHONY : src/net/quic/congestion_control/pacing_sender.o + +# target to build an object file +src/net/quic/congestion_control/pacing_sender.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/pacing_sender.cc.o +.PHONY : src/net/quic/congestion_control/pacing_sender.cc.o + +src/net/quic/congestion_control/pacing_sender.i: src/net/quic/congestion_control/pacing_sender.cc.i + +.PHONY : src/net/quic/congestion_control/pacing_sender.i + +# target to preprocess a source file +src/net/quic/congestion_control/pacing_sender.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/pacing_sender.cc.i +.PHONY : src/net/quic/congestion_control/pacing_sender.cc.i + +src/net/quic/congestion_control/pacing_sender.s: src/net/quic/congestion_control/pacing_sender.cc.s + +.PHONY : src/net/quic/congestion_control/pacing_sender.s + +# target to generate assembly for a file +src/net/quic/congestion_control/pacing_sender.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/pacing_sender.cc.s +.PHONY : src/net/quic/congestion_control/pacing_sender.cc.s + +src/net/quic/congestion_control/prr_sender.o: src/net/quic/congestion_control/prr_sender.cc.o + +.PHONY : src/net/quic/congestion_control/prr_sender.o + +# target to build an object file +src/net/quic/congestion_control/prr_sender.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/prr_sender.cc.o +.PHONY : src/net/quic/congestion_control/prr_sender.cc.o + +src/net/quic/congestion_control/prr_sender.i: src/net/quic/congestion_control/prr_sender.cc.i + +.PHONY : src/net/quic/congestion_control/prr_sender.i + +# target to preprocess a source file +src/net/quic/congestion_control/prr_sender.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/prr_sender.cc.i +.PHONY : src/net/quic/congestion_control/prr_sender.cc.i + +src/net/quic/congestion_control/prr_sender.s: src/net/quic/congestion_control/prr_sender.cc.s + +.PHONY : src/net/quic/congestion_control/prr_sender.s + +# target to generate assembly for a file +src/net/quic/congestion_control/prr_sender.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/prr_sender.cc.s +.PHONY : src/net/quic/congestion_control/prr_sender.cc.s + +src/net/quic/congestion_control/rtt_stats.o: src/net/quic/congestion_control/rtt_stats.cc.o + +.PHONY : src/net/quic/congestion_control/rtt_stats.o + +# target to build an object file +src/net/quic/congestion_control/rtt_stats.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/rtt_stats.cc.o +.PHONY : src/net/quic/congestion_control/rtt_stats.cc.o + +src/net/quic/congestion_control/rtt_stats.i: src/net/quic/congestion_control/rtt_stats.cc.i + +.PHONY : src/net/quic/congestion_control/rtt_stats.i + +# target to preprocess a source file +src/net/quic/congestion_control/rtt_stats.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/rtt_stats.cc.i +.PHONY : src/net/quic/congestion_control/rtt_stats.cc.i + +src/net/quic/congestion_control/rtt_stats.s: src/net/quic/congestion_control/rtt_stats.cc.s + +.PHONY : src/net/quic/congestion_control/rtt_stats.s + +# target to generate assembly for a file +src/net/quic/congestion_control/rtt_stats.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/rtt_stats.cc.s +.PHONY : src/net/quic/congestion_control/rtt_stats.cc.s + +src/net/quic/congestion_control/send_algorithm_interface.o: src/net/quic/congestion_control/send_algorithm_interface.cc.o + +.PHONY : src/net/quic/congestion_control/send_algorithm_interface.o + +# target to build an object file +src/net/quic/congestion_control/send_algorithm_interface.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/send_algorithm_interface.cc.o +.PHONY : src/net/quic/congestion_control/send_algorithm_interface.cc.o + +src/net/quic/congestion_control/send_algorithm_interface.i: src/net/quic/congestion_control/send_algorithm_interface.cc.i + +.PHONY : src/net/quic/congestion_control/send_algorithm_interface.i + +# target to preprocess a source file +src/net/quic/congestion_control/send_algorithm_interface.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/send_algorithm_interface.cc.i +.PHONY : src/net/quic/congestion_control/send_algorithm_interface.cc.i + +src/net/quic/congestion_control/send_algorithm_interface.s: src/net/quic/congestion_control/send_algorithm_interface.cc.s + +.PHONY : src/net/quic/congestion_control/send_algorithm_interface.s + +# target to generate assembly for a file +src/net/quic/congestion_control/send_algorithm_interface.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/send_algorithm_interface.cc.s +.PHONY : src/net/quic/congestion_control/send_algorithm_interface.cc.s + +src/net/quic/congestion_control/tcp_cubic_bytes_sender.o: src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.o + +.PHONY : src/net/quic/congestion_control/tcp_cubic_bytes_sender.o + +# target to build an object file +src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.o +.PHONY : src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.o + +src/net/quic/congestion_control/tcp_cubic_bytes_sender.i: src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.i + +.PHONY : src/net/quic/congestion_control/tcp_cubic_bytes_sender.i + +# target to preprocess a source file +src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.i +.PHONY : src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.i + +src/net/quic/congestion_control/tcp_cubic_bytes_sender.s: src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.s + +.PHONY : src/net/quic/congestion_control/tcp_cubic_bytes_sender.s + +# target to generate assembly for a file +src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.s +.PHONY : src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc.s + +src/net/quic/congestion_control/tcp_cubic_sender.o: src/net/quic/congestion_control/tcp_cubic_sender.cc.o + +.PHONY : src/net/quic/congestion_control/tcp_cubic_sender.o + +# target to build an object file +src/net/quic/congestion_control/tcp_cubic_sender.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/tcp_cubic_sender.cc.o +.PHONY : src/net/quic/congestion_control/tcp_cubic_sender.cc.o + +src/net/quic/congestion_control/tcp_cubic_sender.i: src/net/quic/congestion_control/tcp_cubic_sender.cc.i + +.PHONY : src/net/quic/congestion_control/tcp_cubic_sender.i + +# target to preprocess a source file +src/net/quic/congestion_control/tcp_cubic_sender.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/tcp_cubic_sender.cc.i +.PHONY : src/net/quic/congestion_control/tcp_cubic_sender.cc.i + +src/net/quic/congestion_control/tcp_cubic_sender.s: src/net/quic/congestion_control/tcp_cubic_sender.cc.s + +.PHONY : src/net/quic/congestion_control/tcp_cubic_sender.s + +# target to generate assembly for a file +src/net/quic/congestion_control/tcp_cubic_sender.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/congestion_control/tcp_cubic_sender.cc.s +.PHONY : src/net/quic/congestion_control/tcp_cubic_sender.cc.s + +src/net/quic/crypto/aead_base_decrypter_openssl.o: src/net/quic/crypto/aead_base_decrypter_openssl.cc.o + +.PHONY : src/net/quic/crypto/aead_base_decrypter_openssl.o + +# target to build an object file +src/net/quic/crypto/aead_base_decrypter_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aead_base_decrypter_openssl.cc.o +.PHONY : src/net/quic/crypto/aead_base_decrypter_openssl.cc.o + +src/net/quic/crypto/aead_base_decrypter_openssl.i: src/net/quic/crypto/aead_base_decrypter_openssl.cc.i + +.PHONY : src/net/quic/crypto/aead_base_decrypter_openssl.i + +# target to preprocess a source file +src/net/quic/crypto/aead_base_decrypter_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aead_base_decrypter_openssl.cc.i +.PHONY : src/net/quic/crypto/aead_base_decrypter_openssl.cc.i + +src/net/quic/crypto/aead_base_decrypter_openssl.s: src/net/quic/crypto/aead_base_decrypter_openssl.cc.s + +.PHONY : src/net/quic/crypto/aead_base_decrypter_openssl.s + +# target to generate assembly for a file +src/net/quic/crypto/aead_base_decrypter_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aead_base_decrypter_openssl.cc.s +.PHONY : src/net/quic/crypto/aead_base_decrypter_openssl.cc.s + +src/net/quic/crypto/aead_base_encrypter_openssl.o: src/net/quic/crypto/aead_base_encrypter_openssl.cc.o + +.PHONY : src/net/quic/crypto/aead_base_encrypter_openssl.o + +# target to build an object file +src/net/quic/crypto/aead_base_encrypter_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aead_base_encrypter_openssl.cc.o +.PHONY : src/net/quic/crypto/aead_base_encrypter_openssl.cc.o + +src/net/quic/crypto/aead_base_encrypter_openssl.i: src/net/quic/crypto/aead_base_encrypter_openssl.cc.i + +.PHONY : src/net/quic/crypto/aead_base_encrypter_openssl.i + +# target to preprocess a source file +src/net/quic/crypto/aead_base_encrypter_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aead_base_encrypter_openssl.cc.i +.PHONY : src/net/quic/crypto/aead_base_encrypter_openssl.cc.i + +src/net/quic/crypto/aead_base_encrypter_openssl.s: src/net/quic/crypto/aead_base_encrypter_openssl.cc.s + +.PHONY : src/net/quic/crypto/aead_base_encrypter_openssl.s + +# target to generate assembly for a file +src/net/quic/crypto/aead_base_encrypter_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aead_base_encrypter_openssl.cc.s +.PHONY : src/net/quic/crypto/aead_base_encrypter_openssl.cc.s + +src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.o: src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.o + +.PHONY : src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.o + +# target to build an object file +src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.o +.PHONY : src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.o + +src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.i: src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.i + +.PHONY : src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.i + +# target to preprocess a source file +src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.i +.PHONY : src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.i + +src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.s: src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.s + +.PHONY : src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.s + +# target to generate assembly for a file +src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.s +.PHONY : src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc.s + +src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.o: src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.o + +.PHONY : src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.o + +# target to build an object file +src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.o +.PHONY : src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.o + +src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.i: src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.i + +.PHONY : src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.i + +# target to preprocess a source file +src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.i +.PHONY : src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.i + +src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.s: src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.s + +.PHONY : src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.s + +# target to generate assembly for a file +src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.s +.PHONY : src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc.s + +src/net/quic/crypto/cert_compressor.o: src/net/quic/crypto/cert_compressor.cc.o + +.PHONY : src/net/quic/crypto/cert_compressor.o + +# target to build an object file +src/net/quic/crypto/cert_compressor.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/cert_compressor.cc.o +.PHONY : src/net/quic/crypto/cert_compressor.cc.o + +src/net/quic/crypto/cert_compressor.i: src/net/quic/crypto/cert_compressor.cc.i + +.PHONY : src/net/quic/crypto/cert_compressor.i + +# target to preprocess a source file +src/net/quic/crypto/cert_compressor.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/cert_compressor.cc.i +.PHONY : src/net/quic/crypto/cert_compressor.cc.i + +src/net/quic/crypto/cert_compressor.s: src/net/quic/crypto/cert_compressor.cc.s + +.PHONY : src/net/quic/crypto/cert_compressor.s + +# target to generate assembly for a file +src/net/quic/crypto/cert_compressor.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/cert_compressor.cc.s +.PHONY : src/net/quic/crypto/cert_compressor.cc.s + +src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.o: src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.o + +.PHONY : src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.o + +# target to build an object file +src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.o +.PHONY : src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.o + +src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.i: src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.i + +.PHONY : src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.i + +# target to preprocess a source file +src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.i +.PHONY : src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.i + +src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.s: src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.s + +.PHONY : src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.s + +# target to generate assembly for a file +src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.s +.PHONY : src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc.s + +src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.o: src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.o + +.PHONY : src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.o + +# target to build an object file +src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.o +.PHONY : src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.o + +src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.i: src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.i + +.PHONY : src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.i + +# target to preprocess a source file +src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.i +.PHONY : src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.i + +src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.s: src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.s + +.PHONY : src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.s + +# target to generate assembly for a file +src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.s +.PHONY : src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc.s + +src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.o: src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.o + +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.o + +# target to build an object file +src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.o +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.o + +src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.i: src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.i + +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.i + +# target to preprocess a source file +src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.i +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.i + +src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.s: src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.s + +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.s + +# target to generate assembly for a file +src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.s +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.cc.s + +src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.o: src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.o + +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.o + +# target to build an object file +src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.o +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.o + +src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.i: src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.i + +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.i + +# target to preprocess a source file +src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.i +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.i + +src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.s: src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.s + +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.s + +# target to generate assembly for a file +src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.s +.PHONY : src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.cc.s + +src/net/quic/crypto/channel_id.o: src/net/quic/crypto/channel_id.cc.o + +.PHONY : src/net/quic/crypto/channel_id.o + +# target to build an object file +src/net/quic/crypto/channel_id.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/channel_id.cc.o +.PHONY : src/net/quic/crypto/channel_id.cc.o + +src/net/quic/crypto/channel_id.i: src/net/quic/crypto/channel_id.cc.i + +.PHONY : src/net/quic/crypto/channel_id.i + +# target to preprocess a source file +src/net/quic/crypto/channel_id.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/channel_id.cc.i +.PHONY : src/net/quic/crypto/channel_id.cc.i + +src/net/quic/crypto/channel_id.s: src/net/quic/crypto/channel_id.cc.s + +.PHONY : src/net/quic/crypto/channel_id.s + +# target to generate assembly for a file +src/net/quic/crypto/channel_id.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/channel_id.cc.s +.PHONY : src/net/quic/crypto/channel_id.cc.s + +src/net/quic/crypto/channel_id_openssl.o: src/net/quic/crypto/channel_id_openssl.cc.o + +.PHONY : src/net/quic/crypto/channel_id_openssl.o + +# target to build an object file +src/net/quic/crypto/channel_id_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/channel_id_openssl.cc.o +.PHONY : src/net/quic/crypto/channel_id_openssl.cc.o + +src/net/quic/crypto/channel_id_openssl.i: src/net/quic/crypto/channel_id_openssl.cc.i + +.PHONY : src/net/quic/crypto/channel_id_openssl.i + +# target to preprocess a source file +src/net/quic/crypto/channel_id_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/channel_id_openssl.cc.i +.PHONY : src/net/quic/crypto/channel_id_openssl.cc.i + +src/net/quic/crypto/channel_id_openssl.s: src/net/quic/crypto/channel_id_openssl.cc.s + +.PHONY : src/net/quic/crypto/channel_id_openssl.s + +# target to generate assembly for a file +src/net/quic/crypto/channel_id_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/channel_id_openssl.cc.s +.PHONY : src/net/quic/crypto/channel_id_openssl.cc.s + +src/net/quic/crypto/common_cert_set.o: src/net/quic/crypto/common_cert_set.cc.o + +.PHONY : src/net/quic/crypto/common_cert_set.o + +# target to build an object file +src/net/quic/crypto/common_cert_set.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/common_cert_set.cc.o +.PHONY : src/net/quic/crypto/common_cert_set.cc.o + +src/net/quic/crypto/common_cert_set.i: src/net/quic/crypto/common_cert_set.cc.i + +.PHONY : src/net/quic/crypto/common_cert_set.i + +# target to preprocess a source file +src/net/quic/crypto/common_cert_set.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/common_cert_set.cc.i +.PHONY : src/net/quic/crypto/common_cert_set.cc.i + +src/net/quic/crypto/common_cert_set.s: src/net/quic/crypto/common_cert_set.cc.s + +.PHONY : src/net/quic/crypto/common_cert_set.s + +# target to generate assembly for a file +src/net/quic/crypto/common_cert_set.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/common_cert_set.cc.s +.PHONY : src/net/quic/crypto/common_cert_set.cc.s + +src/net/quic/crypto/crypto_framer.o: src/net/quic/crypto/crypto_framer.cc.o + +.PHONY : src/net/quic/crypto/crypto_framer.o + +# target to build an object file +src/net/quic/crypto/crypto_framer.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_framer.cc.o +.PHONY : src/net/quic/crypto/crypto_framer.cc.o + +src/net/quic/crypto/crypto_framer.i: src/net/quic/crypto/crypto_framer.cc.i + +.PHONY : src/net/quic/crypto/crypto_framer.i + +# target to preprocess a source file +src/net/quic/crypto/crypto_framer.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_framer.cc.i +.PHONY : src/net/quic/crypto/crypto_framer.cc.i + +src/net/quic/crypto/crypto_framer.s: src/net/quic/crypto/crypto_framer.cc.s + +.PHONY : src/net/quic/crypto/crypto_framer.s + +# target to generate assembly for a file +src/net/quic/crypto/crypto_framer.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_framer.cc.s +.PHONY : src/net/quic/crypto/crypto_framer.cc.s + +src/net/quic/crypto/crypto_handshake.o: src/net/quic/crypto/crypto_handshake.cc.o + +.PHONY : src/net/quic/crypto/crypto_handshake.o + +# target to build an object file +src/net/quic/crypto/crypto_handshake.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_handshake.cc.o +.PHONY : src/net/quic/crypto/crypto_handshake.cc.o + +src/net/quic/crypto/crypto_handshake.i: src/net/quic/crypto/crypto_handshake.cc.i + +.PHONY : src/net/quic/crypto/crypto_handshake.i + +# target to preprocess a source file +src/net/quic/crypto/crypto_handshake.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_handshake.cc.i +.PHONY : src/net/quic/crypto/crypto_handshake.cc.i + +src/net/quic/crypto/crypto_handshake.s: src/net/quic/crypto/crypto_handshake.cc.s + +.PHONY : src/net/quic/crypto/crypto_handshake.s + +# target to generate assembly for a file +src/net/quic/crypto/crypto_handshake.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_handshake.cc.s +.PHONY : src/net/quic/crypto/crypto_handshake.cc.s + +src/net/quic/crypto/crypto_handshake_message.o: src/net/quic/crypto/crypto_handshake_message.cc.o + +.PHONY : src/net/quic/crypto/crypto_handshake_message.o + +# target to build an object file +src/net/quic/crypto/crypto_handshake_message.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_handshake_message.cc.o +.PHONY : src/net/quic/crypto/crypto_handshake_message.cc.o + +src/net/quic/crypto/crypto_handshake_message.i: src/net/quic/crypto/crypto_handshake_message.cc.i + +.PHONY : src/net/quic/crypto/crypto_handshake_message.i + +# target to preprocess a source file +src/net/quic/crypto/crypto_handshake_message.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_handshake_message.cc.i +.PHONY : src/net/quic/crypto/crypto_handshake_message.cc.i + +src/net/quic/crypto/crypto_handshake_message.s: src/net/quic/crypto/crypto_handshake_message.cc.s + +.PHONY : src/net/quic/crypto/crypto_handshake_message.s + +# target to generate assembly for a file +src/net/quic/crypto/crypto_handshake_message.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_handshake_message.cc.s +.PHONY : src/net/quic/crypto/crypto_handshake_message.cc.s + +src/net/quic/crypto/crypto_secret_boxer.o: src/net/quic/crypto/crypto_secret_boxer.cc.o + +.PHONY : src/net/quic/crypto/crypto_secret_boxer.o + +# target to build an object file +src/net/quic/crypto/crypto_secret_boxer.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_secret_boxer.cc.o +.PHONY : src/net/quic/crypto/crypto_secret_boxer.cc.o + +src/net/quic/crypto/crypto_secret_boxer.i: src/net/quic/crypto/crypto_secret_boxer.cc.i + +.PHONY : src/net/quic/crypto/crypto_secret_boxer.i + +# target to preprocess a source file +src/net/quic/crypto/crypto_secret_boxer.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_secret_boxer.cc.i +.PHONY : src/net/quic/crypto/crypto_secret_boxer.cc.i + +src/net/quic/crypto/crypto_secret_boxer.s: src/net/quic/crypto/crypto_secret_boxer.cc.s + +.PHONY : src/net/quic/crypto/crypto_secret_boxer.s + +# target to generate assembly for a file +src/net/quic/crypto/crypto_secret_boxer.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_secret_boxer.cc.s +.PHONY : src/net/quic/crypto/crypto_secret_boxer.cc.s + +src/net/quic/crypto/crypto_server_config_protobuf.o: src/net/quic/crypto/crypto_server_config_protobuf.cc.o + +.PHONY : src/net/quic/crypto/crypto_server_config_protobuf.o + +# target to build an object file +src/net/quic/crypto/crypto_server_config_protobuf.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_server_config_protobuf.cc.o +.PHONY : src/net/quic/crypto/crypto_server_config_protobuf.cc.o + +src/net/quic/crypto/crypto_server_config_protobuf.i: src/net/quic/crypto/crypto_server_config_protobuf.cc.i + +.PHONY : src/net/quic/crypto/crypto_server_config_protobuf.i + +# target to preprocess a source file +src/net/quic/crypto/crypto_server_config_protobuf.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_server_config_protobuf.cc.i +.PHONY : src/net/quic/crypto/crypto_server_config_protobuf.cc.i + +src/net/quic/crypto/crypto_server_config_protobuf.s: src/net/quic/crypto/crypto_server_config_protobuf.cc.s + +.PHONY : src/net/quic/crypto/crypto_server_config_protobuf.s + +# target to generate assembly for a file +src/net/quic/crypto/crypto_server_config_protobuf.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_server_config_protobuf.cc.s +.PHONY : src/net/quic/crypto/crypto_server_config_protobuf.cc.s + +src/net/quic/crypto/crypto_utils.o: src/net/quic/crypto/crypto_utils.cc.o + +.PHONY : src/net/quic/crypto/crypto_utils.o + +# target to build an object file +src/net/quic/crypto/crypto_utils.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_utils.cc.o +.PHONY : src/net/quic/crypto/crypto_utils.cc.o + +src/net/quic/crypto/crypto_utils.i: src/net/quic/crypto/crypto_utils.cc.i + +.PHONY : src/net/quic/crypto/crypto_utils.i + +# target to preprocess a source file +src/net/quic/crypto/crypto_utils.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_utils.cc.i +.PHONY : src/net/quic/crypto/crypto_utils.cc.i + +src/net/quic/crypto/crypto_utils.s: src/net/quic/crypto/crypto_utils.cc.s + +.PHONY : src/net/quic/crypto/crypto_utils.s + +# target to generate assembly for a file +src/net/quic/crypto/crypto_utils.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/crypto_utils.cc.s +.PHONY : src/net/quic/crypto/crypto_utils.cc.s + +src/net/quic/crypto/curve25519_key_exchange.o: src/net/quic/crypto/curve25519_key_exchange.cc.o + +.PHONY : src/net/quic/crypto/curve25519_key_exchange.o + +# target to build an object file +src/net/quic/crypto/curve25519_key_exchange.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/curve25519_key_exchange.cc.o +.PHONY : src/net/quic/crypto/curve25519_key_exchange.cc.o + +src/net/quic/crypto/curve25519_key_exchange.i: src/net/quic/crypto/curve25519_key_exchange.cc.i + +.PHONY : src/net/quic/crypto/curve25519_key_exchange.i + +# target to preprocess a source file +src/net/quic/crypto/curve25519_key_exchange.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/curve25519_key_exchange.cc.i +.PHONY : src/net/quic/crypto/curve25519_key_exchange.cc.i + +src/net/quic/crypto/curve25519_key_exchange.s: src/net/quic/crypto/curve25519_key_exchange.cc.s + +.PHONY : src/net/quic/crypto/curve25519_key_exchange.s + +# target to generate assembly for a file +src/net/quic/crypto/curve25519_key_exchange.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/curve25519_key_exchange.cc.s +.PHONY : src/net/quic/crypto/curve25519_key_exchange.cc.s + +src/net/quic/crypto/local_strike_register_client.o: src/net/quic/crypto/local_strike_register_client.cc.o + +.PHONY : src/net/quic/crypto/local_strike_register_client.o + +# target to build an object file +src/net/quic/crypto/local_strike_register_client.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/local_strike_register_client.cc.o +.PHONY : src/net/quic/crypto/local_strike_register_client.cc.o + +src/net/quic/crypto/local_strike_register_client.i: src/net/quic/crypto/local_strike_register_client.cc.i + +.PHONY : src/net/quic/crypto/local_strike_register_client.i + +# target to preprocess a source file +src/net/quic/crypto/local_strike_register_client.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/local_strike_register_client.cc.i +.PHONY : src/net/quic/crypto/local_strike_register_client.cc.i + +src/net/quic/crypto/local_strike_register_client.s: src/net/quic/crypto/local_strike_register_client.cc.s + +.PHONY : src/net/quic/crypto/local_strike_register_client.s + +# target to generate assembly for a file +src/net/quic/crypto/local_strike_register_client.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/local_strike_register_client.cc.s +.PHONY : src/net/quic/crypto/local_strike_register_client.cc.s + +src/net/quic/crypto/null_decrypter.o: src/net/quic/crypto/null_decrypter.cc.o + +.PHONY : src/net/quic/crypto/null_decrypter.o + +# target to build an object file +src/net/quic/crypto/null_decrypter.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/null_decrypter.cc.o +.PHONY : src/net/quic/crypto/null_decrypter.cc.o + +src/net/quic/crypto/null_decrypter.i: src/net/quic/crypto/null_decrypter.cc.i + +.PHONY : src/net/quic/crypto/null_decrypter.i + +# target to preprocess a source file +src/net/quic/crypto/null_decrypter.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/null_decrypter.cc.i +.PHONY : src/net/quic/crypto/null_decrypter.cc.i + +src/net/quic/crypto/null_decrypter.s: src/net/quic/crypto/null_decrypter.cc.s + +.PHONY : src/net/quic/crypto/null_decrypter.s + +# target to generate assembly for a file +src/net/quic/crypto/null_decrypter.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/null_decrypter.cc.s +.PHONY : src/net/quic/crypto/null_decrypter.cc.s + +src/net/quic/crypto/null_encrypter.o: src/net/quic/crypto/null_encrypter.cc.o + +.PHONY : src/net/quic/crypto/null_encrypter.o + +# target to build an object file +src/net/quic/crypto/null_encrypter.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/null_encrypter.cc.o +.PHONY : src/net/quic/crypto/null_encrypter.cc.o + +src/net/quic/crypto/null_encrypter.i: src/net/quic/crypto/null_encrypter.cc.i + +.PHONY : src/net/quic/crypto/null_encrypter.i + +# target to preprocess a source file +src/net/quic/crypto/null_encrypter.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/null_encrypter.cc.i +.PHONY : src/net/quic/crypto/null_encrypter.cc.i + +src/net/quic/crypto/null_encrypter.s: src/net/quic/crypto/null_encrypter.cc.s + +.PHONY : src/net/quic/crypto/null_encrypter.s + +# target to generate assembly for a file +src/net/quic/crypto/null_encrypter.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/null_encrypter.cc.s +.PHONY : src/net/quic/crypto/null_encrypter.cc.s + +src/net/quic/crypto/p256_key_exchange_openssl.o: src/net/quic/crypto/p256_key_exchange_openssl.cc.o + +.PHONY : src/net/quic/crypto/p256_key_exchange_openssl.o + +# target to build an object file +src/net/quic/crypto/p256_key_exchange_openssl.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/p256_key_exchange_openssl.cc.o +.PHONY : src/net/quic/crypto/p256_key_exchange_openssl.cc.o + +src/net/quic/crypto/p256_key_exchange_openssl.i: src/net/quic/crypto/p256_key_exchange_openssl.cc.i + +.PHONY : src/net/quic/crypto/p256_key_exchange_openssl.i + +# target to preprocess a source file +src/net/quic/crypto/p256_key_exchange_openssl.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/p256_key_exchange_openssl.cc.i +.PHONY : src/net/quic/crypto/p256_key_exchange_openssl.cc.i + +src/net/quic/crypto/p256_key_exchange_openssl.s: src/net/quic/crypto/p256_key_exchange_openssl.cc.s + +.PHONY : src/net/quic/crypto/p256_key_exchange_openssl.s + +# target to generate assembly for a file +src/net/quic/crypto/p256_key_exchange_openssl.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/p256_key_exchange_openssl.cc.s +.PHONY : src/net/quic/crypto/p256_key_exchange_openssl.cc.s + +src/net/quic/crypto/quic_crypto_client_config.o: src/net/quic/crypto/quic_crypto_client_config.cc.o + +.PHONY : src/net/quic/crypto/quic_crypto_client_config.o + +# target to build an object file +src/net/quic/crypto/quic_crypto_client_config.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_crypto_client_config.cc.o +.PHONY : src/net/quic/crypto/quic_crypto_client_config.cc.o + +src/net/quic/crypto/quic_crypto_client_config.i: src/net/quic/crypto/quic_crypto_client_config.cc.i + +.PHONY : src/net/quic/crypto/quic_crypto_client_config.i + +# target to preprocess a source file +src/net/quic/crypto/quic_crypto_client_config.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_crypto_client_config.cc.i +.PHONY : src/net/quic/crypto/quic_crypto_client_config.cc.i + +src/net/quic/crypto/quic_crypto_client_config.s: src/net/quic/crypto/quic_crypto_client_config.cc.s + +.PHONY : src/net/quic/crypto/quic_crypto_client_config.s + +# target to generate assembly for a file +src/net/quic/crypto/quic_crypto_client_config.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_crypto_client_config.cc.s +.PHONY : src/net/quic/crypto/quic_crypto_client_config.cc.s + +src/net/quic/crypto/quic_crypto_server_config.o: src/net/quic/crypto/quic_crypto_server_config.cc.o + +.PHONY : src/net/quic/crypto/quic_crypto_server_config.o + +# target to build an object file +src/net/quic/crypto/quic_crypto_server_config.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_crypto_server_config.cc.o +.PHONY : src/net/quic/crypto/quic_crypto_server_config.cc.o + +src/net/quic/crypto/quic_crypto_server_config.i: src/net/quic/crypto/quic_crypto_server_config.cc.i + +.PHONY : src/net/quic/crypto/quic_crypto_server_config.i + +# target to preprocess a source file +src/net/quic/crypto/quic_crypto_server_config.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_crypto_server_config.cc.i +.PHONY : src/net/quic/crypto/quic_crypto_server_config.cc.i + +src/net/quic/crypto/quic_crypto_server_config.s: src/net/quic/crypto/quic_crypto_server_config.cc.s + +.PHONY : src/net/quic/crypto/quic_crypto_server_config.s + +# target to generate assembly for a file +src/net/quic/crypto/quic_crypto_server_config.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_crypto_server_config.cc.s +.PHONY : src/net/quic/crypto/quic_crypto_server_config.cc.s + +src/net/quic/crypto/quic_decrypter.o: src/net/quic/crypto/quic_decrypter.cc.o + +.PHONY : src/net/quic/crypto/quic_decrypter.o + +# target to build an object file +src/net/quic/crypto/quic_decrypter.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_decrypter.cc.o +.PHONY : src/net/quic/crypto/quic_decrypter.cc.o + +src/net/quic/crypto/quic_decrypter.i: src/net/quic/crypto/quic_decrypter.cc.i + +.PHONY : src/net/quic/crypto/quic_decrypter.i + +# target to preprocess a source file +src/net/quic/crypto/quic_decrypter.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_decrypter.cc.i +.PHONY : src/net/quic/crypto/quic_decrypter.cc.i + +src/net/quic/crypto/quic_decrypter.s: src/net/quic/crypto/quic_decrypter.cc.s + +.PHONY : src/net/quic/crypto/quic_decrypter.s + +# target to generate assembly for a file +src/net/quic/crypto/quic_decrypter.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_decrypter.cc.s +.PHONY : src/net/quic/crypto/quic_decrypter.cc.s + +src/net/quic/crypto/quic_encrypter.o: src/net/quic/crypto/quic_encrypter.cc.o + +.PHONY : src/net/quic/crypto/quic_encrypter.o + +# target to build an object file +src/net/quic/crypto/quic_encrypter.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_encrypter.cc.o +.PHONY : src/net/quic/crypto/quic_encrypter.cc.o + +src/net/quic/crypto/quic_encrypter.i: src/net/quic/crypto/quic_encrypter.cc.i + +.PHONY : src/net/quic/crypto/quic_encrypter.i + +# target to preprocess a source file +src/net/quic/crypto/quic_encrypter.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_encrypter.cc.i +.PHONY : src/net/quic/crypto/quic_encrypter.cc.i + +src/net/quic/crypto/quic_encrypter.s: src/net/quic/crypto/quic_encrypter.cc.s + +.PHONY : src/net/quic/crypto/quic_encrypter.s + +# target to generate assembly for a file +src/net/quic/crypto/quic_encrypter.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_encrypter.cc.s +.PHONY : src/net/quic/crypto/quic_encrypter.cc.s + +src/net/quic/crypto/quic_random.o: src/net/quic/crypto/quic_random.cc.o + +.PHONY : src/net/quic/crypto/quic_random.o + +# target to build an object file +src/net/quic/crypto/quic_random.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_random.cc.o +.PHONY : src/net/quic/crypto/quic_random.cc.o + +src/net/quic/crypto/quic_random.i: src/net/quic/crypto/quic_random.cc.i + +.PHONY : src/net/quic/crypto/quic_random.i + +# target to preprocess a source file +src/net/quic/crypto/quic_random.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_random.cc.i +.PHONY : src/net/quic/crypto/quic_random.cc.i + +src/net/quic/crypto/quic_random.s: src/net/quic/crypto/quic_random.cc.s + +.PHONY : src/net/quic/crypto/quic_random.s + +# target to generate assembly for a file +src/net/quic/crypto/quic_random.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/quic_random.cc.s +.PHONY : src/net/quic/crypto/quic_random.cc.s + +src/net/quic/crypto/scoped_evp_aead_ctx.o: src/net/quic/crypto/scoped_evp_aead_ctx.cc.o + +.PHONY : src/net/quic/crypto/scoped_evp_aead_ctx.o + +# target to build an object file +src/net/quic/crypto/scoped_evp_aead_ctx.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/scoped_evp_aead_ctx.cc.o +.PHONY : src/net/quic/crypto/scoped_evp_aead_ctx.cc.o + +src/net/quic/crypto/scoped_evp_aead_ctx.i: src/net/quic/crypto/scoped_evp_aead_ctx.cc.i + +.PHONY : src/net/quic/crypto/scoped_evp_aead_ctx.i + +# target to preprocess a source file +src/net/quic/crypto/scoped_evp_aead_ctx.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/scoped_evp_aead_ctx.cc.i +.PHONY : src/net/quic/crypto/scoped_evp_aead_ctx.cc.i + +src/net/quic/crypto/scoped_evp_aead_ctx.s: src/net/quic/crypto/scoped_evp_aead_ctx.cc.s + +.PHONY : src/net/quic/crypto/scoped_evp_aead_ctx.s + +# target to generate assembly for a file +src/net/quic/crypto/scoped_evp_aead_ctx.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/scoped_evp_aead_ctx.cc.s +.PHONY : src/net/quic/crypto/scoped_evp_aead_ctx.cc.s + +src/net/quic/crypto/strike_register.o: src/net/quic/crypto/strike_register.cc.o + +.PHONY : src/net/quic/crypto/strike_register.o + +# target to build an object file +src/net/quic/crypto/strike_register.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/strike_register.cc.o +.PHONY : src/net/quic/crypto/strike_register.cc.o + +src/net/quic/crypto/strike_register.i: src/net/quic/crypto/strike_register.cc.i + +.PHONY : src/net/quic/crypto/strike_register.i + +# target to preprocess a source file +src/net/quic/crypto/strike_register.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/strike_register.cc.i +.PHONY : src/net/quic/crypto/strike_register.cc.i + +src/net/quic/crypto/strike_register.s: src/net/quic/crypto/strike_register.cc.s + +.PHONY : src/net/quic/crypto/strike_register.s + +# target to generate assembly for a file +src/net/quic/crypto/strike_register.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/crypto/strike_register.cc.s +.PHONY : src/net/quic/crypto/strike_register.cc.s + +src/net/quic/iovector.o: src/net/quic/iovector.cc.o + +.PHONY : src/net/quic/iovector.o + +# target to build an object file +src/net/quic/iovector.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/iovector.cc.o +.PHONY : src/net/quic/iovector.cc.o + +src/net/quic/iovector.i: src/net/quic/iovector.cc.i + +.PHONY : src/net/quic/iovector.i + +# target to preprocess a source file +src/net/quic/iovector.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/iovector.cc.i +.PHONY : src/net/quic/iovector.cc.i + +src/net/quic/iovector.s: src/net/quic/iovector.cc.s + +.PHONY : src/net/quic/iovector.s + +# target to generate assembly for a file +src/net/quic/iovector.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/iovector.cc.s +.PHONY : src/net/quic/iovector.cc.s + +src/net/quic/proto/cached_network_parameters.pb.o: src/net/quic/proto/cached_network_parameters.pb.cc.o + +.PHONY : src/net/quic/proto/cached_network_parameters.pb.o + +# target to build an object file +src/net/quic/proto/cached_network_parameters.pb.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/proto/cached_network_parameters.pb.cc.o +.PHONY : src/net/quic/proto/cached_network_parameters.pb.cc.o + +src/net/quic/proto/cached_network_parameters.pb.i: src/net/quic/proto/cached_network_parameters.pb.cc.i + +.PHONY : src/net/quic/proto/cached_network_parameters.pb.i + +# target to preprocess a source file +src/net/quic/proto/cached_network_parameters.pb.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/proto/cached_network_parameters.pb.cc.i +.PHONY : src/net/quic/proto/cached_network_parameters.pb.cc.i + +src/net/quic/proto/cached_network_parameters.pb.s: src/net/quic/proto/cached_network_parameters.pb.cc.s + +.PHONY : src/net/quic/proto/cached_network_parameters.pb.s + +# target to generate assembly for a file +src/net/quic/proto/cached_network_parameters.pb.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/proto/cached_network_parameters.pb.cc.s +.PHONY : src/net/quic/proto/cached_network_parameters.pb.cc.s + +src/net/quic/proto/source_address_token.pb.o: src/net/quic/proto/source_address_token.pb.cc.o + +.PHONY : src/net/quic/proto/source_address_token.pb.o + +# target to build an object file +src/net/quic/proto/source_address_token.pb.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/proto/source_address_token.pb.cc.o +.PHONY : src/net/quic/proto/source_address_token.pb.cc.o + +src/net/quic/proto/source_address_token.pb.i: src/net/quic/proto/source_address_token.pb.cc.i + +.PHONY : src/net/quic/proto/source_address_token.pb.i + +# target to preprocess a source file +src/net/quic/proto/source_address_token.pb.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/proto/source_address_token.pb.cc.i +.PHONY : src/net/quic/proto/source_address_token.pb.cc.i + +src/net/quic/proto/source_address_token.pb.s: src/net/quic/proto/source_address_token.pb.cc.s + +.PHONY : src/net/quic/proto/source_address_token.pb.s + +# target to generate assembly for a file +src/net/quic/proto/source_address_token.pb.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/proto/source_address_token.pb.cc.s +.PHONY : src/net/quic/proto/source_address_token.pb.cc.s + +src/net/quic/quic_alarm.o: src/net/quic/quic_alarm.cc.o + +.PHONY : src/net/quic/quic_alarm.o + +# target to build an object file +src/net/quic/quic_alarm.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_alarm.cc.o +.PHONY : src/net/quic/quic_alarm.cc.o + +src/net/quic/quic_alarm.i: src/net/quic/quic_alarm.cc.i + +.PHONY : src/net/quic/quic_alarm.i + +# target to preprocess a source file +src/net/quic/quic_alarm.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_alarm.cc.i +.PHONY : src/net/quic/quic_alarm.cc.i + +src/net/quic/quic_alarm.s: src/net/quic/quic_alarm.cc.s + +.PHONY : src/net/quic/quic_alarm.s + +# target to generate assembly for a file +src/net/quic/quic_alarm.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_alarm.cc.s +.PHONY : src/net/quic/quic_alarm.cc.s + +src/net/quic/quic_bandwidth.o: src/net/quic/quic_bandwidth.cc.o + +.PHONY : src/net/quic/quic_bandwidth.o + +# target to build an object file +src/net/quic/quic_bandwidth.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_bandwidth.cc.o +.PHONY : src/net/quic/quic_bandwidth.cc.o + +src/net/quic/quic_bandwidth.i: src/net/quic/quic_bandwidth.cc.i + +.PHONY : src/net/quic/quic_bandwidth.i + +# target to preprocess a source file +src/net/quic/quic_bandwidth.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_bandwidth.cc.i +.PHONY : src/net/quic/quic_bandwidth.cc.i + +src/net/quic/quic_bandwidth.s: src/net/quic/quic_bandwidth.cc.s + +.PHONY : src/net/quic/quic_bandwidth.s + +# target to generate assembly for a file +src/net/quic/quic_bandwidth.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_bandwidth.cc.s +.PHONY : src/net/quic/quic_bandwidth.cc.s + +src/net/quic/quic_client_session_base.o: src/net/quic/quic_client_session_base.cc.o + +.PHONY : src/net/quic/quic_client_session_base.o + +# target to build an object file +src/net/quic/quic_client_session_base.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_client_session_base.cc.o +.PHONY : src/net/quic/quic_client_session_base.cc.o + +src/net/quic/quic_client_session_base.i: src/net/quic/quic_client_session_base.cc.i + +.PHONY : src/net/quic/quic_client_session_base.i + +# target to preprocess a source file +src/net/quic/quic_client_session_base.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_client_session_base.cc.i +.PHONY : src/net/quic/quic_client_session_base.cc.i + +src/net/quic/quic_client_session_base.s: src/net/quic/quic_client_session_base.cc.s + +.PHONY : src/net/quic/quic_client_session_base.s + +# target to generate assembly for a file +src/net/quic/quic_client_session_base.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_client_session_base.cc.s +.PHONY : src/net/quic/quic_client_session_base.cc.s + +src/net/quic/quic_clock.o: src/net/quic/quic_clock.cc.o + +.PHONY : src/net/quic/quic_clock.o + +# target to build an object file +src/net/quic/quic_clock.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_clock.cc.o +.PHONY : src/net/quic/quic_clock.cc.o + +src/net/quic/quic_clock.i: src/net/quic/quic_clock.cc.i + +.PHONY : src/net/quic/quic_clock.i + +# target to preprocess a source file +src/net/quic/quic_clock.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_clock.cc.i +.PHONY : src/net/quic/quic_clock.cc.i + +src/net/quic/quic_clock.s: src/net/quic/quic_clock.cc.s + +.PHONY : src/net/quic/quic_clock.s + +# target to generate assembly for a file +src/net/quic/quic_clock.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_clock.cc.s +.PHONY : src/net/quic/quic_clock.cc.s + +src/net/quic/quic_config.o: src/net/quic/quic_config.cc.o + +.PHONY : src/net/quic/quic_config.o + +# target to build an object file +src/net/quic/quic_config.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_config.cc.o +.PHONY : src/net/quic/quic_config.cc.o + +src/net/quic/quic_config.i: src/net/quic/quic_config.cc.i + +.PHONY : src/net/quic/quic_config.i + +# target to preprocess a source file +src/net/quic/quic_config.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_config.cc.i +.PHONY : src/net/quic/quic_config.cc.i + +src/net/quic/quic_config.s: src/net/quic/quic_config.cc.s + +.PHONY : src/net/quic/quic_config.s + +# target to generate assembly for a file +src/net/quic/quic_config.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_config.cc.s +.PHONY : src/net/quic/quic_config.cc.s + +src/net/quic/quic_connection.o: src/net/quic/quic_connection.cc.o + +.PHONY : src/net/quic/quic_connection.o + +# target to build an object file +src/net/quic/quic_connection.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_connection.cc.o +.PHONY : src/net/quic/quic_connection.cc.o + +src/net/quic/quic_connection.i: src/net/quic/quic_connection.cc.i + +.PHONY : src/net/quic/quic_connection.i + +# target to preprocess a source file +src/net/quic/quic_connection.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_connection.cc.i +.PHONY : src/net/quic/quic_connection.cc.i + +src/net/quic/quic_connection.s: src/net/quic/quic_connection.cc.s + +.PHONY : src/net/quic/quic_connection.s + +# target to generate assembly for a file +src/net/quic/quic_connection.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_connection.cc.s +.PHONY : src/net/quic/quic_connection.cc.s + +src/net/quic/quic_connection_stats.o: src/net/quic/quic_connection_stats.cc.o + +.PHONY : src/net/quic/quic_connection_stats.o + +# target to build an object file +src/net/quic/quic_connection_stats.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_connection_stats.cc.o +.PHONY : src/net/quic/quic_connection_stats.cc.o + +src/net/quic/quic_connection_stats.i: src/net/quic/quic_connection_stats.cc.i + +.PHONY : src/net/quic/quic_connection_stats.i + +# target to preprocess a source file +src/net/quic/quic_connection_stats.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_connection_stats.cc.i +.PHONY : src/net/quic/quic_connection_stats.cc.i + +src/net/quic/quic_connection_stats.s: src/net/quic/quic_connection_stats.cc.s + +.PHONY : src/net/quic/quic_connection_stats.s + +# target to generate assembly for a file +src/net/quic/quic_connection_stats.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_connection_stats.cc.s +.PHONY : src/net/quic/quic_connection_stats.cc.s + +src/net/quic/quic_crypto_client_stream.o: src/net/quic/quic_crypto_client_stream.cc.o + +.PHONY : src/net/quic/quic_crypto_client_stream.o + +# target to build an object file +src/net/quic/quic_crypto_client_stream.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_crypto_client_stream.cc.o +.PHONY : src/net/quic/quic_crypto_client_stream.cc.o + +src/net/quic/quic_crypto_client_stream.i: src/net/quic/quic_crypto_client_stream.cc.i + +.PHONY : src/net/quic/quic_crypto_client_stream.i + +# target to preprocess a source file +src/net/quic/quic_crypto_client_stream.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_crypto_client_stream.cc.i +.PHONY : src/net/quic/quic_crypto_client_stream.cc.i + +src/net/quic/quic_crypto_client_stream.s: src/net/quic/quic_crypto_client_stream.cc.s + +.PHONY : src/net/quic/quic_crypto_client_stream.s + +# target to generate assembly for a file +src/net/quic/quic_crypto_client_stream.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_crypto_client_stream.cc.s +.PHONY : src/net/quic/quic_crypto_client_stream.cc.s + +src/net/quic/quic_crypto_server_stream.o: src/net/quic/quic_crypto_server_stream.cc.o + +.PHONY : src/net/quic/quic_crypto_server_stream.o + +# target to build an object file +src/net/quic/quic_crypto_server_stream.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_crypto_server_stream.cc.o +.PHONY : src/net/quic/quic_crypto_server_stream.cc.o + +src/net/quic/quic_crypto_server_stream.i: src/net/quic/quic_crypto_server_stream.cc.i + +.PHONY : src/net/quic/quic_crypto_server_stream.i + +# target to preprocess a source file +src/net/quic/quic_crypto_server_stream.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_crypto_server_stream.cc.i +.PHONY : src/net/quic/quic_crypto_server_stream.cc.i + +src/net/quic/quic_crypto_server_stream.s: src/net/quic/quic_crypto_server_stream.cc.s + +.PHONY : src/net/quic/quic_crypto_server_stream.s + +# target to generate assembly for a file +src/net/quic/quic_crypto_server_stream.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_crypto_server_stream.cc.s +.PHONY : src/net/quic/quic_crypto_server_stream.cc.s + +src/net/quic/quic_crypto_stream.o: src/net/quic/quic_crypto_stream.cc.o + +.PHONY : src/net/quic/quic_crypto_stream.o + +# target to build an object file +src/net/quic/quic_crypto_stream.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_crypto_stream.cc.o +.PHONY : src/net/quic/quic_crypto_stream.cc.o + +src/net/quic/quic_crypto_stream.i: src/net/quic/quic_crypto_stream.cc.i + +.PHONY : src/net/quic/quic_crypto_stream.i + +# target to preprocess a source file +src/net/quic/quic_crypto_stream.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_crypto_stream.cc.i +.PHONY : src/net/quic/quic_crypto_stream.cc.i + +src/net/quic/quic_crypto_stream.s: src/net/quic/quic_crypto_stream.cc.s + +.PHONY : src/net/quic/quic_crypto_stream.s + +# target to generate assembly for a file +src/net/quic/quic_crypto_stream.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_crypto_stream.cc.s +.PHONY : src/net/quic/quic_crypto_stream.cc.s + +src/net/quic/quic_data_reader.o: src/net/quic/quic_data_reader.cc.o + +.PHONY : src/net/quic/quic_data_reader.o + +# target to build an object file +src/net/quic/quic_data_reader.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_data_reader.cc.o +.PHONY : src/net/quic/quic_data_reader.cc.o + +src/net/quic/quic_data_reader.i: src/net/quic/quic_data_reader.cc.i + +.PHONY : src/net/quic/quic_data_reader.i + +# target to preprocess a source file +src/net/quic/quic_data_reader.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_data_reader.cc.i +.PHONY : src/net/quic/quic_data_reader.cc.i + +src/net/quic/quic_data_reader.s: src/net/quic/quic_data_reader.cc.s + +.PHONY : src/net/quic/quic_data_reader.s + +# target to generate assembly for a file +src/net/quic/quic_data_reader.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_data_reader.cc.s +.PHONY : src/net/quic/quic_data_reader.cc.s + +src/net/quic/quic_data_writer.o: src/net/quic/quic_data_writer.cc.o + +.PHONY : src/net/quic/quic_data_writer.o + +# target to build an object file +src/net/quic/quic_data_writer.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_data_writer.cc.o +.PHONY : src/net/quic/quic_data_writer.cc.o + +src/net/quic/quic_data_writer.i: src/net/quic/quic_data_writer.cc.i + +.PHONY : src/net/quic/quic_data_writer.i + +# target to preprocess a source file +src/net/quic/quic_data_writer.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_data_writer.cc.i +.PHONY : src/net/quic/quic_data_writer.cc.i + +src/net/quic/quic_data_writer.s: src/net/quic/quic_data_writer.cc.s + +.PHONY : src/net/quic/quic_data_writer.s + +# target to generate assembly for a file +src/net/quic/quic_data_writer.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_data_writer.cc.s +.PHONY : src/net/quic/quic_data_writer.cc.s + +src/net/quic/quic_fec_group.o: src/net/quic/quic_fec_group.cc.o + +.PHONY : src/net/quic/quic_fec_group.o + +# target to build an object file +src/net/quic/quic_fec_group.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_fec_group.cc.o +.PHONY : src/net/quic/quic_fec_group.cc.o + +src/net/quic/quic_fec_group.i: src/net/quic/quic_fec_group.cc.i + +.PHONY : src/net/quic/quic_fec_group.i + +# target to preprocess a source file +src/net/quic/quic_fec_group.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_fec_group.cc.i +.PHONY : src/net/quic/quic_fec_group.cc.i + +src/net/quic/quic_fec_group.s: src/net/quic/quic_fec_group.cc.s + +.PHONY : src/net/quic/quic_fec_group.s + +# target to generate assembly for a file +src/net/quic/quic_fec_group.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_fec_group.cc.s +.PHONY : src/net/quic/quic_fec_group.cc.s + +src/net/quic/quic_fec_group_interface.o: src/net/quic/quic_fec_group_interface.cc.o + +.PHONY : src/net/quic/quic_fec_group_interface.o + +# target to build an object file +src/net/quic/quic_fec_group_interface.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_fec_group_interface.cc.o +.PHONY : src/net/quic/quic_fec_group_interface.cc.o + +src/net/quic/quic_fec_group_interface.i: src/net/quic/quic_fec_group_interface.cc.i + +.PHONY : src/net/quic/quic_fec_group_interface.i + +# target to preprocess a source file +src/net/quic/quic_fec_group_interface.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_fec_group_interface.cc.i +.PHONY : src/net/quic/quic_fec_group_interface.cc.i + +src/net/quic/quic_fec_group_interface.s: src/net/quic/quic_fec_group_interface.cc.s + +.PHONY : src/net/quic/quic_fec_group_interface.s + +# target to generate assembly for a file +src/net/quic/quic_fec_group_interface.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_fec_group_interface.cc.s +.PHONY : src/net/quic/quic_fec_group_interface.cc.s + +src/net/quic/quic_flags.o: src/net/quic/quic_flags.cc.o + +.PHONY : src/net/quic/quic_flags.o + +# target to build an object file +src/net/quic/quic_flags.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_flags.cc.o +.PHONY : src/net/quic/quic_flags.cc.o + +src/net/quic/quic_flags.i: src/net/quic/quic_flags.cc.i + +.PHONY : src/net/quic/quic_flags.i + +# target to preprocess a source file +src/net/quic/quic_flags.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_flags.cc.i +.PHONY : src/net/quic/quic_flags.cc.i + +src/net/quic/quic_flags.s: src/net/quic/quic_flags.cc.s + +.PHONY : src/net/quic/quic_flags.s + +# target to generate assembly for a file +src/net/quic/quic_flags.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_flags.cc.s +.PHONY : src/net/quic/quic_flags.cc.s + +src/net/quic/quic_flow_controller.o: src/net/quic/quic_flow_controller.cc.o + +.PHONY : src/net/quic/quic_flow_controller.o + +# target to build an object file +src/net/quic/quic_flow_controller.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_flow_controller.cc.o +.PHONY : src/net/quic/quic_flow_controller.cc.o + +src/net/quic/quic_flow_controller.i: src/net/quic/quic_flow_controller.cc.i + +.PHONY : src/net/quic/quic_flow_controller.i + +# target to preprocess a source file +src/net/quic/quic_flow_controller.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_flow_controller.cc.i +.PHONY : src/net/quic/quic_flow_controller.cc.i + +src/net/quic/quic_flow_controller.s: src/net/quic/quic_flow_controller.cc.s + +.PHONY : src/net/quic/quic_flow_controller.s + +# target to generate assembly for a file +src/net/quic/quic_flow_controller.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_flow_controller.cc.s +.PHONY : src/net/quic/quic_flow_controller.cc.s + +src/net/quic/quic_frame_list.o: src/net/quic/quic_frame_list.cc.o + +.PHONY : src/net/quic/quic_frame_list.o + +# target to build an object file +src/net/quic/quic_frame_list.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_frame_list.cc.o +.PHONY : src/net/quic/quic_frame_list.cc.o + +src/net/quic/quic_frame_list.i: src/net/quic/quic_frame_list.cc.i + +.PHONY : src/net/quic/quic_frame_list.i + +# target to preprocess a source file +src/net/quic/quic_frame_list.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_frame_list.cc.i +.PHONY : src/net/quic/quic_frame_list.cc.i + +src/net/quic/quic_frame_list.s: src/net/quic/quic_frame_list.cc.s + +.PHONY : src/net/quic/quic_frame_list.s + +# target to generate assembly for a file +src/net/quic/quic_frame_list.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_frame_list.cc.s +.PHONY : src/net/quic/quic_frame_list.cc.s + +src/net/quic/quic_framer.o: src/net/quic/quic_framer.cc.o + +.PHONY : src/net/quic/quic_framer.o + +# target to build an object file +src/net/quic/quic_framer.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_framer.cc.o +.PHONY : src/net/quic/quic_framer.cc.o + +src/net/quic/quic_framer.i: src/net/quic/quic_framer.cc.i + +.PHONY : src/net/quic/quic_framer.i + +# target to preprocess a source file +src/net/quic/quic_framer.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_framer.cc.i +.PHONY : src/net/quic/quic_framer.cc.i + +src/net/quic/quic_framer.s: src/net/quic/quic_framer.cc.s + +.PHONY : src/net/quic/quic_framer.s + +# target to generate assembly for a file +src/net/quic/quic_framer.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_framer.cc.s +.PHONY : src/net/quic/quic_framer.cc.s + +src/net/quic/quic_headers_stream.o: src/net/quic/quic_headers_stream.cc.o + +.PHONY : src/net/quic/quic_headers_stream.o + +# target to build an object file +src/net/quic/quic_headers_stream.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_headers_stream.cc.o +.PHONY : src/net/quic/quic_headers_stream.cc.o + +src/net/quic/quic_headers_stream.i: src/net/quic/quic_headers_stream.cc.i + +.PHONY : src/net/quic/quic_headers_stream.i + +# target to preprocess a source file +src/net/quic/quic_headers_stream.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_headers_stream.cc.i +.PHONY : src/net/quic/quic_headers_stream.cc.i + +src/net/quic/quic_headers_stream.s: src/net/quic/quic_headers_stream.cc.s + +.PHONY : src/net/quic/quic_headers_stream.s + +# target to generate assembly for a file +src/net/quic/quic_headers_stream.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_headers_stream.cc.s +.PHONY : src/net/quic/quic_headers_stream.cc.s + +src/net/quic/quic_packet_creator.o: src/net/quic/quic_packet_creator.cc.o + +.PHONY : src/net/quic/quic_packet_creator.o + +# target to build an object file +src/net/quic/quic_packet_creator.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_packet_creator.cc.o +.PHONY : src/net/quic/quic_packet_creator.cc.o + +src/net/quic/quic_packet_creator.i: src/net/quic/quic_packet_creator.cc.i + +.PHONY : src/net/quic/quic_packet_creator.i + +# target to preprocess a source file +src/net/quic/quic_packet_creator.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_packet_creator.cc.i +.PHONY : src/net/quic/quic_packet_creator.cc.i + +src/net/quic/quic_packet_creator.s: src/net/quic/quic_packet_creator.cc.s + +.PHONY : src/net/quic/quic_packet_creator.s + +# target to generate assembly for a file +src/net/quic/quic_packet_creator.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_packet_creator.cc.s +.PHONY : src/net/quic/quic_packet_creator.cc.s + +src/net/quic/quic_packet_generator.o: src/net/quic/quic_packet_generator.cc.o + +.PHONY : src/net/quic/quic_packet_generator.o + +# target to build an object file +src/net/quic/quic_packet_generator.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_packet_generator.cc.o +.PHONY : src/net/quic/quic_packet_generator.cc.o + +src/net/quic/quic_packet_generator.i: src/net/quic/quic_packet_generator.cc.i + +.PHONY : src/net/quic/quic_packet_generator.i + +# target to preprocess a source file +src/net/quic/quic_packet_generator.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_packet_generator.cc.i +.PHONY : src/net/quic/quic_packet_generator.cc.i + +src/net/quic/quic_packet_generator.s: src/net/quic/quic_packet_generator.cc.s + +.PHONY : src/net/quic/quic_packet_generator.s + +# target to generate assembly for a file +src/net/quic/quic_packet_generator.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_packet_generator.cc.s +.PHONY : src/net/quic/quic_packet_generator.cc.s + +src/net/quic/quic_protocol.o: src/net/quic/quic_protocol.cc.o + +.PHONY : src/net/quic/quic_protocol.o + +# target to build an object file +src/net/quic/quic_protocol.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_protocol.cc.o +.PHONY : src/net/quic/quic_protocol.cc.o + +src/net/quic/quic_protocol.i: src/net/quic/quic_protocol.cc.i + +.PHONY : src/net/quic/quic_protocol.i + +# target to preprocess a source file +src/net/quic/quic_protocol.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_protocol.cc.i +.PHONY : src/net/quic/quic_protocol.cc.i + +src/net/quic/quic_protocol.s: src/net/quic/quic_protocol.cc.s + +.PHONY : src/net/quic/quic_protocol.s + +# target to generate assembly for a file +src/net/quic/quic_protocol.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_protocol.cc.s +.PHONY : src/net/quic/quic_protocol.cc.s + +src/net/quic/quic_received_packet_manager.o: src/net/quic/quic_received_packet_manager.cc.o + +.PHONY : src/net/quic/quic_received_packet_manager.o + +# target to build an object file +src/net/quic/quic_received_packet_manager.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_received_packet_manager.cc.o +.PHONY : src/net/quic/quic_received_packet_manager.cc.o + +src/net/quic/quic_received_packet_manager.i: src/net/quic/quic_received_packet_manager.cc.i + +.PHONY : src/net/quic/quic_received_packet_manager.i + +# target to preprocess a source file +src/net/quic/quic_received_packet_manager.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_received_packet_manager.cc.i +.PHONY : src/net/quic/quic_received_packet_manager.cc.i + +src/net/quic/quic_received_packet_manager.s: src/net/quic/quic_received_packet_manager.cc.s + +.PHONY : src/net/quic/quic_received_packet_manager.s + +# target to generate assembly for a file +src/net/quic/quic_received_packet_manager.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_received_packet_manager.cc.s +.PHONY : src/net/quic/quic_received_packet_manager.cc.s + +src/net/quic/quic_sent_entropy_manager.o: src/net/quic/quic_sent_entropy_manager.cc.o + +.PHONY : src/net/quic/quic_sent_entropy_manager.o + +# target to build an object file +src/net/quic/quic_sent_entropy_manager.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_sent_entropy_manager.cc.o +.PHONY : src/net/quic/quic_sent_entropy_manager.cc.o + +src/net/quic/quic_sent_entropy_manager.i: src/net/quic/quic_sent_entropy_manager.cc.i + +.PHONY : src/net/quic/quic_sent_entropy_manager.i + +# target to preprocess a source file +src/net/quic/quic_sent_entropy_manager.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_sent_entropy_manager.cc.i +.PHONY : src/net/quic/quic_sent_entropy_manager.cc.i + +src/net/quic/quic_sent_entropy_manager.s: src/net/quic/quic_sent_entropy_manager.cc.s + +.PHONY : src/net/quic/quic_sent_entropy_manager.s + +# target to generate assembly for a file +src/net/quic/quic_sent_entropy_manager.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_sent_entropy_manager.cc.s +.PHONY : src/net/quic/quic_sent_entropy_manager.cc.s + +src/net/quic/quic_sent_packet_manager.o: src/net/quic/quic_sent_packet_manager.cc.o + +.PHONY : src/net/quic/quic_sent_packet_manager.o + +# target to build an object file +src/net/quic/quic_sent_packet_manager.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_sent_packet_manager.cc.o +.PHONY : src/net/quic/quic_sent_packet_manager.cc.o + +src/net/quic/quic_sent_packet_manager.i: src/net/quic/quic_sent_packet_manager.cc.i + +.PHONY : src/net/quic/quic_sent_packet_manager.i + +# target to preprocess a source file +src/net/quic/quic_sent_packet_manager.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_sent_packet_manager.cc.i +.PHONY : src/net/quic/quic_sent_packet_manager.cc.i + +src/net/quic/quic_sent_packet_manager.s: src/net/quic/quic_sent_packet_manager.cc.s + +.PHONY : src/net/quic/quic_sent_packet_manager.s + +# target to generate assembly for a file +src/net/quic/quic_sent_packet_manager.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_sent_packet_manager.cc.s +.PHONY : src/net/quic/quic_sent_packet_manager.cc.s + +src/net/quic/quic_server_id.o: src/net/quic/quic_server_id.cc.o + +.PHONY : src/net/quic/quic_server_id.o + +# target to build an object file +src/net/quic/quic_server_id.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_server_id.cc.o +.PHONY : src/net/quic/quic_server_id.cc.o + +src/net/quic/quic_server_id.i: src/net/quic/quic_server_id.cc.i + +.PHONY : src/net/quic/quic_server_id.i + +# target to preprocess a source file +src/net/quic/quic_server_id.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_server_id.cc.i +.PHONY : src/net/quic/quic_server_id.cc.i + +src/net/quic/quic_server_id.s: src/net/quic/quic_server_id.cc.s + +.PHONY : src/net/quic/quic_server_id.s + +# target to generate assembly for a file +src/net/quic/quic_server_id.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_server_id.cc.s +.PHONY : src/net/quic/quic_server_id.cc.s + +src/net/quic/quic_session.o: src/net/quic/quic_session.cc.o + +.PHONY : src/net/quic/quic_session.o + +# target to build an object file +src/net/quic/quic_session.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_session.cc.o +.PHONY : src/net/quic/quic_session.cc.o + +src/net/quic/quic_session.i: src/net/quic/quic_session.cc.i + +.PHONY : src/net/quic/quic_session.i + +# target to preprocess a source file +src/net/quic/quic_session.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_session.cc.i +.PHONY : src/net/quic/quic_session.cc.i + +src/net/quic/quic_session.s: src/net/quic/quic_session.cc.s + +.PHONY : src/net/quic/quic_session.s + +# target to generate assembly for a file +src/net/quic/quic_session.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_session.cc.s +.PHONY : src/net/quic/quic_session.cc.s + +src/net/quic/quic_simple_buffer_allocator.o: src/net/quic/quic_simple_buffer_allocator.cc.o + +.PHONY : src/net/quic/quic_simple_buffer_allocator.o + +# target to build an object file +src/net/quic/quic_simple_buffer_allocator.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_simple_buffer_allocator.cc.o +.PHONY : src/net/quic/quic_simple_buffer_allocator.cc.o + +src/net/quic/quic_simple_buffer_allocator.i: src/net/quic/quic_simple_buffer_allocator.cc.i + +.PHONY : src/net/quic/quic_simple_buffer_allocator.i + +# target to preprocess a source file +src/net/quic/quic_simple_buffer_allocator.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_simple_buffer_allocator.cc.i +.PHONY : src/net/quic/quic_simple_buffer_allocator.cc.i + +src/net/quic/quic_simple_buffer_allocator.s: src/net/quic/quic_simple_buffer_allocator.cc.s + +.PHONY : src/net/quic/quic_simple_buffer_allocator.s + +# target to generate assembly for a file +src/net/quic/quic_simple_buffer_allocator.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_simple_buffer_allocator.cc.s +.PHONY : src/net/quic/quic_simple_buffer_allocator.cc.s + +src/net/quic/quic_socket_address_coder.o: src/net/quic/quic_socket_address_coder.cc.o + +.PHONY : src/net/quic/quic_socket_address_coder.o + +# target to build an object file +src/net/quic/quic_socket_address_coder.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_socket_address_coder.cc.o +.PHONY : src/net/quic/quic_socket_address_coder.cc.o + +src/net/quic/quic_socket_address_coder.i: src/net/quic/quic_socket_address_coder.cc.i + +.PHONY : src/net/quic/quic_socket_address_coder.i + +# target to preprocess a source file +src/net/quic/quic_socket_address_coder.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_socket_address_coder.cc.i +.PHONY : src/net/quic/quic_socket_address_coder.cc.i + +src/net/quic/quic_socket_address_coder.s: src/net/quic/quic_socket_address_coder.cc.s + +.PHONY : src/net/quic/quic_socket_address_coder.s + +# target to generate assembly for a file +src/net/quic/quic_socket_address_coder.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_socket_address_coder.cc.s +.PHONY : src/net/quic/quic_socket_address_coder.cc.s + +src/net/quic/quic_spdy_session.o: src/net/quic/quic_spdy_session.cc.o + +.PHONY : src/net/quic/quic_spdy_session.o + +# target to build an object file +src/net/quic/quic_spdy_session.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_spdy_session.cc.o +.PHONY : src/net/quic/quic_spdy_session.cc.o + +src/net/quic/quic_spdy_session.i: src/net/quic/quic_spdy_session.cc.i + +.PHONY : src/net/quic/quic_spdy_session.i + +# target to preprocess a source file +src/net/quic/quic_spdy_session.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_spdy_session.cc.i +.PHONY : src/net/quic/quic_spdy_session.cc.i + +src/net/quic/quic_spdy_session.s: src/net/quic/quic_spdy_session.cc.s + +.PHONY : src/net/quic/quic_spdy_session.s + +# target to generate assembly for a file +src/net/quic/quic_spdy_session.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_spdy_session.cc.s +.PHONY : src/net/quic/quic_spdy_session.cc.s + +src/net/quic/quic_spdy_stream.o: src/net/quic/quic_spdy_stream.cc.o + +.PHONY : src/net/quic/quic_spdy_stream.o + +# target to build an object file +src/net/quic/quic_spdy_stream.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_spdy_stream.cc.o +.PHONY : src/net/quic/quic_spdy_stream.cc.o + +src/net/quic/quic_spdy_stream.i: src/net/quic/quic_spdy_stream.cc.i + +.PHONY : src/net/quic/quic_spdy_stream.i + +# target to preprocess a source file +src/net/quic/quic_spdy_stream.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_spdy_stream.cc.i +.PHONY : src/net/quic/quic_spdy_stream.cc.i + +src/net/quic/quic_spdy_stream.s: src/net/quic/quic_spdy_stream.cc.s + +.PHONY : src/net/quic/quic_spdy_stream.s + +# target to generate assembly for a file +src/net/quic/quic_spdy_stream.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_spdy_stream.cc.s +.PHONY : src/net/quic/quic_spdy_stream.cc.s + +src/net/quic/quic_stream_sequencer.o: src/net/quic/quic_stream_sequencer.cc.o + +.PHONY : src/net/quic/quic_stream_sequencer.o + +# target to build an object file +src/net/quic/quic_stream_sequencer.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_stream_sequencer.cc.o +.PHONY : src/net/quic/quic_stream_sequencer.cc.o + +src/net/quic/quic_stream_sequencer.i: src/net/quic/quic_stream_sequencer.cc.i + +.PHONY : src/net/quic/quic_stream_sequencer.i + +# target to preprocess a source file +src/net/quic/quic_stream_sequencer.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_stream_sequencer.cc.i +.PHONY : src/net/quic/quic_stream_sequencer.cc.i + +src/net/quic/quic_stream_sequencer.s: src/net/quic/quic_stream_sequencer.cc.s + +.PHONY : src/net/quic/quic_stream_sequencer.s + +# target to generate assembly for a file +src/net/quic/quic_stream_sequencer.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_stream_sequencer.cc.s +.PHONY : src/net/quic/quic_stream_sequencer.cc.s + +src/net/quic/quic_sustained_bandwidth_recorder.o: src/net/quic/quic_sustained_bandwidth_recorder.cc.o + +.PHONY : src/net/quic/quic_sustained_bandwidth_recorder.o + +# target to build an object file +src/net/quic/quic_sustained_bandwidth_recorder.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_sustained_bandwidth_recorder.cc.o +.PHONY : src/net/quic/quic_sustained_bandwidth_recorder.cc.o + +src/net/quic/quic_sustained_bandwidth_recorder.i: src/net/quic/quic_sustained_bandwidth_recorder.cc.i + +.PHONY : src/net/quic/quic_sustained_bandwidth_recorder.i + +# target to preprocess a source file +src/net/quic/quic_sustained_bandwidth_recorder.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_sustained_bandwidth_recorder.cc.i +.PHONY : src/net/quic/quic_sustained_bandwidth_recorder.cc.i + +src/net/quic/quic_sustained_bandwidth_recorder.s: src/net/quic/quic_sustained_bandwidth_recorder.cc.s + +.PHONY : src/net/quic/quic_sustained_bandwidth_recorder.s + +# target to generate assembly for a file +src/net/quic/quic_sustained_bandwidth_recorder.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_sustained_bandwidth_recorder.cc.s +.PHONY : src/net/quic/quic_sustained_bandwidth_recorder.cc.s + +src/net/quic/quic_time.o: src/net/quic/quic_time.cc.o + +.PHONY : src/net/quic/quic_time.o + +# target to build an object file +src/net/quic/quic_time.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_time.cc.o +.PHONY : src/net/quic/quic_time.cc.o + +src/net/quic/quic_time.i: src/net/quic/quic_time.cc.i + +.PHONY : src/net/quic/quic_time.i + +# target to preprocess a source file +src/net/quic/quic_time.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_time.cc.i +.PHONY : src/net/quic/quic_time.cc.i + +src/net/quic/quic_time.s: src/net/quic/quic_time.cc.s + +.PHONY : src/net/quic/quic_time.s + +# target to generate assembly for a file +src/net/quic/quic_time.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_time.cc.s +.PHONY : src/net/quic/quic_time.cc.s + +src/net/quic/quic_types.o: src/net/quic/quic_types.cc.o + +.PHONY : src/net/quic/quic_types.o + +# target to build an object file +src/net/quic/quic_types.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_types.cc.o +.PHONY : src/net/quic/quic_types.cc.o + +src/net/quic/quic_types.i: src/net/quic/quic_types.cc.i + +.PHONY : src/net/quic/quic_types.i + +# target to preprocess a source file +src/net/quic/quic_types.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_types.cc.i +.PHONY : src/net/quic/quic_types.cc.i + +src/net/quic/quic_types.s: src/net/quic/quic_types.cc.s + +.PHONY : src/net/quic/quic_types.s + +# target to generate assembly for a file +src/net/quic/quic_types.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_types.cc.s +.PHONY : src/net/quic/quic_types.cc.s + +src/net/quic/quic_unacked_packet_map.o: src/net/quic/quic_unacked_packet_map.cc.o + +.PHONY : src/net/quic/quic_unacked_packet_map.o + +# target to build an object file +src/net/quic/quic_unacked_packet_map.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_unacked_packet_map.cc.o +.PHONY : src/net/quic/quic_unacked_packet_map.cc.o + +src/net/quic/quic_unacked_packet_map.i: src/net/quic/quic_unacked_packet_map.cc.i + +.PHONY : src/net/quic/quic_unacked_packet_map.i + +# target to preprocess a source file +src/net/quic/quic_unacked_packet_map.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_unacked_packet_map.cc.i +.PHONY : src/net/quic/quic_unacked_packet_map.cc.i + +src/net/quic/quic_unacked_packet_map.s: src/net/quic/quic_unacked_packet_map.cc.s + +.PHONY : src/net/quic/quic_unacked_packet_map.s + +# target to generate assembly for a file +src/net/quic/quic_unacked_packet_map.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_unacked_packet_map.cc.s +.PHONY : src/net/quic/quic_unacked_packet_map.cc.s + +src/net/quic/quic_utils.o: src/net/quic/quic_utils.cc.o + +.PHONY : src/net/quic/quic_utils.o + +# target to build an object file +src/net/quic/quic_utils.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_utils.cc.o +.PHONY : src/net/quic/quic_utils.cc.o + +src/net/quic/quic_utils.i: src/net/quic/quic_utils.cc.i + +.PHONY : src/net/quic/quic_utils.i + +# target to preprocess a source file +src/net/quic/quic_utils.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_utils.cc.i +.PHONY : src/net/quic/quic_utils.cc.i + +src/net/quic/quic_utils.s: src/net/quic/quic_utils.cc.s + +.PHONY : src/net/quic/quic_utils.s + +# target to generate assembly for a file +src/net/quic/quic_utils.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_utils.cc.s +.PHONY : src/net/quic/quic_utils.cc.s + +src/net/quic/quic_write_blocked_list.o: src/net/quic/quic_write_blocked_list.cc.o + +.PHONY : src/net/quic/quic_write_blocked_list.o + +# target to build an object file +src/net/quic/quic_write_blocked_list.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_write_blocked_list.cc.o +.PHONY : src/net/quic/quic_write_blocked_list.cc.o + +src/net/quic/quic_write_blocked_list.i: src/net/quic/quic_write_blocked_list.cc.i + +.PHONY : src/net/quic/quic_write_blocked_list.i + +# target to preprocess a source file +src/net/quic/quic_write_blocked_list.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_write_blocked_list.cc.i +.PHONY : src/net/quic/quic_write_blocked_list.cc.i + +src/net/quic/quic_write_blocked_list.s: src/net/quic/quic_write_blocked_list.cc.s + +.PHONY : src/net/quic/quic_write_blocked_list.s + +# target to generate assembly for a file +src/net/quic/quic_write_blocked_list.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/quic_write_blocked_list.cc.s +.PHONY : src/net/quic/quic_write_blocked_list.cc.s + +src/net/quic/reliable_quic_stream.o: src/net/quic/reliable_quic_stream.cc.o + +.PHONY : src/net/quic/reliable_quic_stream.o + +# target to build an object file +src/net/quic/reliable_quic_stream.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/reliable_quic_stream.cc.o +.PHONY : src/net/quic/reliable_quic_stream.cc.o + +src/net/quic/reliable_quic_stream.i: src/net/quic/reliable_quic_stream.cc.i + +.PHONY : src/net/quic/reliable_quic_stream.i + +# target to preprocess a source file +src/net/quic/reliable_quic_stream.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/reliable_quic_stream.cc.i +.PHONY : src/net/quic/reliable_quic_stream.cc.i + +src/net/quic/reliable_quic_stream.s: src/net/quic/reliable_quic_stream.cc.s + +.PHONY : src/net/quic/reliable_quic_stream.s + +# target to generate assembly for a file +src/net/quic/reliable_quic_stream.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/reliable_quic_stream.cc.s +.PHONY : src/net/quic/reliable_quic_stream.cc.s + +src/net/quic/stream_sequencer_buffer.o: src/net/quic/stream_sequencer_buffer.cc.o + +.PHONY : src/net/quic/stream_sequencer_buffer.o + +# target to build an object file +src/net/quic/stream_sequencer_buffer.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/stream_sequencer_buffer.cc.o +.PHONY : src/net/quic/stream_sequencer_buffer.cc.o + +src/net/quic/stream_sequencer_buffer.i: src/net/quic/stream_sequencer_buffer.cc.i + +.PHONY : src/net/quic/stream_sequencer_buffer.i + +# target to preprocess a source file +src/net/quic/stream_sequencer_buffer.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/stream_sequencer_buffer.cc.i +.PHONY : src/net/quic/stream_sequencer_buffer.cc.i + +src/net/quic/stream_sequencer_buffer.s: src/net/quic/stream_sequencer_buffer.cc.s + +.PHONY : src/net/quic/stream_sequencer_buffer.s + +# target to generate assembly for a file +src/net/quic/stream_sequencer_buffer.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/quic/stream_sequencer_buffer.cc.s +.PHONY : src/net/quic/stream_sequencer_buffer.cc.s + +src/net/spdy/hpack/hpack_constants.o: src/net/spdy/hpack/hpack_constants.cc.o + +.PHONY : src/net/spdy/hpack/hpack_constants.o + +# target to build an object file +src/net/spdy/hpack/hpack_constants.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_constants.cc.o +.PHONY : src/net/spdy/hpack/hpack_constants.cc.o + +src/net/spdy/hpack/hpack_constants.i: src/net/spdy/hpack/hpack_constants.cc.i + +.PHONY : src/net/spdy/hpack/hpack_constants.i + +# target to preprocess a source file +src/net/spdy/hpack/hpack_constants.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_constants.cc.i +.PHONY : src/net/spdy/hpack/hpack_constants.cc.i + +src/net/spdy/hpack/hpack_constants.s: src/net/spdy/hpack/hpack_constants.cc.s + +.PHONY : src/net/spdy/hpack/hpack_constants.s + +# target to generate assembly for a file +src/net/spdy/hpack/hpack_constants.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_constants.cc.s +.PHONY : src/net/spdy/hpack/hpack_constants.cc.s + +src/net/spdy/hpack/hpack_decoder.o: src/net/spdy/hpack/hpack_decoder.cc.o + +.PHONY : src/net/spdy/hpack/hpack_decoder.o + +# target to build an object file +src/net/spdy/hpack/hpack_decoder.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_decoder.cc.o +.PHONY : src/net/spdy/hpack/hpack_decoder.cc.o + +src/net/spdy/hpack/hpack_decoder.i: src/net/spdy/hpack/hpack_decoder.cc.i + +.PHONY : src/net/spdy/hpack/hpack_decoder.i + +# target to preprocess a source file +src/net/spdy/hpack/hpack_decoder.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_decoder.cc.i +.PHONY : src/net/spdy/hpack/hpack_decoder.cc.i + +src/net/spdy/hpack/hpack_decoder.s: src/net/spdy/hpack/hpack_decoder.cc.s + +.PHONY : src/net/spdy/hpack/hpack_decoder.s + +# target to generate assembly for a file +src/net/spdy/hpack/hpack_decoder.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_decoder.cc.s +.PHONY : src/net/spdy/hpack/hpack_decoder.cc.s + +src/net/spdy/hpack/hpack_encoder.o: src/net/spdy/hpack/hpack_encoder.cc.o + +.PHONY : src/net/spdy/hpack/hpack_encoder.o + +# target to build an object file +src/net/spdy/hpack/hpack_encoder.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_encoder.cc.o +.PHONY : src/net/spdy/hpack/hpack_encoder.cc.o + +src/net/spdy/hpack/hpack_encoder.i: src/net/spdy/hpack/hpack_encoder.cc.i + +.PHONY : src/net/spdy/hpack/hpack_encoder.i + +# target to preprocess a source file +src/net/spdy/hpack/hpack_encoder.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_encoder.cc.i +.PHONY : src/net/spdy/hpack/hpack_encoder.cc.i + +src/net/spdy/hpack/hpack_encoder.s: src/net/spdy/hpack/hpack_encoder.cc.s + +.PHONY : src/net/spdy/hpack/hpack_encoder.s + +# target to generate assembly for a file +src/net/spdy/hpack/hpack_encoder.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_encoder.cc.s +.PHONY : src/net/spdy/hpack/hpack_encoder.cc.s + +src/net/spdy/hpack/hpack_entry.o: src/net/spdy/hpack/hpack_entry.cc.o + +.PHONY : src/net/spdy/hpack/hpack_entry.o + +# target to build an object file +src/net/spdy/hpack/hpack_entry.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_entry.cc.o +.PHONY : src/net/spdy/hpack/hpack_entry.cc.o + +src/net/spdy/hpack/hpack_entry.i: src/net/spdy/hpack/hpack_entry.cc.i + +.PHONY : src/net/spdy/hpack/hpack_entry.i + +# target to preprocess a source file +src/net/spdy/hpack/hpack_entry.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_entry.cc.i +.PHONY : src/net/spdy/hpack/hpack_entry.cc.i + +src/net/spdy/hpack/hpack_entry.s: src/net/spdy/hpack/hpack_entry.cc.s + +.PHONY : src/net/spdy/hpack/hpack_entry.s + +# target to generate assembly for a file +src/net/spdy/hpack/hpack_entry.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_entry.cc.s +.PHONY : src/net/spdy/hpack/hpack_entry.cc.s + +src/net/spdy/hpack/hpack_header_table.o: src/net/spdy/hpack/hpack_header_table.cc.o + +.PHONY : src/net/spdy/hpack/hpack_header_table.o + +# target to build an object file +src/net/spdy/hpack/hpack_header_table.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_header_table.cc.o +.PHONY : src/net/spdy/hpack/hpack_header_table.cc.o + +src/net/spdy/hpack/hpack_header_table.i: src/net/spdy/hpack/hpack_header_table.cc.i + +.PHONY : src/net/spdy/hpack/hpack_header_table.i + +# target to preprocess a source file +src/net/spdy/hpack/hpack_header_table.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_header_table.cc.i +.PHONY : src/net/spdy/hpack/hpack_header_table.cc.i + +src/net/spdy/hpack/hpack_header_table.s: src/net/spdy/hpack/hpack_header_table.cc.s + +.PHONY : src/net/spdy/hpack/hpack_header_table.s + +# target to generate assembly for a file +src/net/spdy/hpack/hpack_header_table.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_header_table.cc.s +.PHONY : src/net/spdy/hpack/hpack_header_table.cc.s + +src/net/spdy/hpack/hpack_huffman_table.o: src/net/spdy/hpack/hpack_huffman_table.cc.o + +.PHONY : src/net/spdy/hpack/hpack_huffman_table.o + +# target to build an object file +src/net/spdy/hpack/hpack_huffman_table.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_huffman_table.cc.o +.PHONY : src/net/spdy/hpack/hpack_huffman_table.cc.o + +src/net/spdy/hpack/hpack_huffman_table.i: src/net/spdy/hpack/hpack_huffman_table.cc.i + +.PHONY : src/net/spdy/hpack/hpack_huffman_table.i + +# target to preprocess a source file +src/net/spdy/hpack/hpack_huffman_table.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_huffman_table.cc.i +.PHONY : src/net/spdy/hpack/hpack_huffman_table.cc.i + +src/net/spdy/hpack/hpack_huffman_table.s: src/net/spdy/hpack/hpack_huffman_table.cc.s + +.PHONY : src/net/spdy/hpack/hpack_huffman_table.s + +# target to generate assembly for a file +src/net/spdy/hpack/hpack_huffman_table.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_huffman_table.cc.s +.PHONY : src/net/spdy/hpack/hpack_huffman_table.cc.s + +src/net/spdy/hpack/hpack_input_stream.o: src/net/spdy/hpack/hpack_input_stream.cc.o + +.PHONY : src/net/spdy/hpack/hpack_input_stream.o + +# target to build an object file +src/net/spdy/hpack/hpack_input_stream.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_input_stream.cc.o +.PHONY : src/net/spdy/hpack/hpack_input_stream.cc.o + +src/net/spdy/hpack/hpack_input_stream.i: src/net/spdy/hpack/hpack_input_stream.cc.i + +.PHONY : src/net/spdy/hpack/hpack_input_stream.i + +# target to preprocess a source file +src/net/spdy/hpack/hpack_input_stream.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_input_stream.cc.i +.PHONY : src/net/spdy/hpack/hpack_input_stream.cc.i + +src/net/spdy/hpack/hpack_input_stream.s: src/net/spdy/hpack/hpack_input_stream.cc.s + +.PHONY : src/net/spdy/hpack/hpack_input_stream.s + +# target to generate assembly for a file +src/net/spdy/hpack/hpack_input_stream.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_input_stream.cc.s +.PHONY : src/net/spdy/hpack/hpack_input_stream.cc.s + +src/net/spdy/hpack/hpack_output_stream.o: src/net/spdy/hpack/hpack_output_stream.cc.o + +.PHONY : src/net/spdy/hpack/hpack_output_stream.o + +# target to build an object file +src/net/spdy/hpack/hpack_output_stream.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_output_stream.cc.o +.PHONY : src/net/spdy/hpack/hpack_output_stream.cc.o + +src/net/spdy/hpack/hpack_output_stream.i: src/net/spdy/hpack/hpack_output_stream.cc.i + +.PHONY : src/net/spdy/hpack/hpack_output_stream.i + +# target to preprocess a source file +src/net/spdy/hpack/hpack_output_stream.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_output_stream.cc.i +.PHONY : src/net/spdy/hpack/hpack_output_stream.cc.i + +src/net/spdy/hpack/hpack_output_stream.s: src/net/spdy/hpack/hpack_output_stream.cc.s + +.PHONY : src/net/spdy/hpack/hpack_output_stream.s + +# target to generate assembly for a file +src/net/spdy/hpack/hpack_output_stream.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_output_stream.cc.s +.PHONY : src/net/spdy/hpack/hpack_output_stream.cc.s + +src/net/spdy/hpack/hpack_static_table.o: src/net/spdy/hpack/hpack_static_table.cc.o + +.PHONY : src/net/spdy/hpack/hpack_static_table.o + +# target to build an object file +src/net/spdy/hpack/hpack_static_table.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_static_table.cc.o +.PHONY : src/net/spdy/hpack/hpack_static_table.cc.o + +src/net/spdy/hpack/hpack_static_table.i: src/net/spdy/hpack/hpack_static_table.cc.i + +.PHONY : src/net/spdy/hpack/hpack_static_table.i + +# target to preprocess a source file +src/net/spdy/hpack/hpack_static_table.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_static_table.cc.i +.PHONY : src/net/spdy/hpack/hpack_static_table.cc.i + +src/net/spdy/hpack/hpack_static_table.s: src/net/spdy/hpack/hpack_static_table.cc.s + +.PHONY : src/net/spdy/hpack/hpack_static_table.s + +# target to generate assembly for a file +src/net/spdy/hpack/hpack_static_table.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/hpack/hpack_static_table.cc.s +.PHONY : src/net/spdy/hpack/hpack_static_table.cc.s + +src/net/spdy/spdy_alt_svc_wire_format.o: src/net/spdy/spdy_alt_svc_wire_format.cc.o + +.PHONY : src/net/spdy/spdy_alt_svc_wire_format.o + +# target to build an object file +src/net/spdy/spdy_alt_svc_wire_format.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_alt_svc_wire_format.cc.o +.PHONY : src/net/spdy/spdy_alt_svc_wire_format.cc.o + +src/net/spdy/spdy_alt_svc_wire_format.i: src/net/spdy/spdy_alt_svc_wire_format.cc.i + +.PHONY : src/net/spdy/spdy_alt_svc_wire_format.i + +# target to preprocess a source file +src/net/spdy/spdy_alt_svc_wire_format.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_alt_svc_wire_format.cc.i +.PHONY : src/net/spdy/spdy_alt_svc_wire_format.cc.i + +src/net/spdy/spdy_alt_svc_wire_format.s: src/net/spdy/spdy_alt_svc_wire_format.cc.s + +.PHONY : src/net/spdy/spdy_alt_svc_wire_format.s + +# target to generate assembly for a file +src/net/spdy/spdy_alt_svc_wire_format.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_alt_svc_wire_format.cc.s +.PHONY : src/net/spdy/spdy_alt_svc_wire_format.cc.s + +src/net/spdy/spdy_frame_builder.o: src/net/spdy/spdy_frame_builder.cc.o + +.PHONY : src/net/spdy/spdy_frame_builder.o + +# target to build an object file +src/net/spdy/spdy_frame_builder.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_frame_builder.cc.o +.PHONY : src/net/spdy/spdy_frame_builder.cc.o + +src/net/spdy/spdy_frame_builder.i: src/net/spdy/spdy_frame_builder.cc.i + +.PHONY : src/net/spdy/spdy_frame_builder.i + +# target to preprocess a source file +src/net/spdy/spdy_frame_builder.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_frame_builder.cc.i +.PHONY : src/net/spdy/spdy_frame_builder.cc.i + +src/net/spdy/spdy_frame_builder.s: src/net/spdy/spdy_frame_builder.cc.s + +.PHONY : src/net/spdy/spdy_frame_builder.s + +# target to generate assembly for a file +src/net/spdy/spdy_frame_builder.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_frame_builder.cc.s +.PHONY : src/net/spdy/spdy_frame_builder.cc.s + +src/net/spdy/spdy_frame_reader.o: src/net/spdy/spdy_frame_reader.cc.o + +.PHONY : src/net/spdy/spdy_frame_reader.o + +# target to build an object file +src/net/spdy/spdy_frame_reader.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_frame_reader.cc.o +.PHONY : src/net/spdy/spdy_frame_reader.cc.o + +src/net/spdy/spdy_frame_reader.i: src/net/spdy/spdy_frame_reader.cc.i + +.PHONY : src/net/spdy/spdy_frame_reader.i + +# target to preprocess a source file +src/net/spdy/spdy_frame_reader.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_frame_reader.cc.i +.PHONY : src/net/spdy/spdy_frame_reader.cc.i + +src/net/spdy/spdy_frame_reader.s: src/net/spdy/spdy_frame_reader.cc.s + +.PHONY : src/net/spdy/spdy_frame_reader.s + +# target to generate assembly for a file +src/net/spdy/spdy_frame_reader.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_frame_reader.cc.s +.PHONY : src/net/spdy/spdy_frame_reader.cc.s + +src/net/spdy/spdy_framer.o: src/net/spdy/spdy_framer.cc.o + +.PHONY : src/net/spdy/spdy_framer.o + +# target to build an object file +src/net/spdy/spdy_framer.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_framer.cc.o +.PHONY : src/net/spdy/spdy_framer.cc.o + +src/net/spdy/spdy_framer.i: src/net/spdy/spdy_framer.cc.i + +.PHONY : src/net/spdy/spdy_framer.i + +# target to preprocess a source file +src/net/spdy/spdy_framer.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_framer.cc.i +.PHONY : src/net/spdy/spdy_framer.cc.i + +src/net/spdy/spdy_framer.s: src/net/spdy/spdy_framer.cc.s + +.PHONY : src/net/spdy/spdy_framer.s + +# target to generate assembly for a file +src/net/spdy/spdy_framer.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_framer.cc.s +.PHONY : src/net/spdy/spdy_framer.cc.s + +src/net/spdy/spdy_header_block.o: src/net/spdy/spdy_header_block.cc.o + +.PHONY : src/net/spdy/spdy_header_block.o + +# target to build an object file +src/net/spdy/spdy_header_block.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_header_block.cc.o +.PHONY : src/net/spdy/spdy_header_block.cc.o + +src/net/spdy/spdy_header_block.i: src/net/spdy/spdy_header_block.cc.i + +.PHONY : src/net/spdy/spdy_header_block.i + +# target to preprocess a source file +src/net/spdy/spdy_header_block.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_header_block.cc.i +.PHONY : src/net/spdy/spdy_header_block.cc.i + +src/net/spdy/spdy_header_block.s: src/net/spdy/spdy_header_block.cc.s + +.PHONY : src/net/spdy/spdy_header_block.s + +# target to generate assembly for a file +src/net/spdy/spdy_header_block.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_header_block.cc.s +.PHONY : src/net/spdy/spdy_header_block.cc.s + +src/net/spdy/spdy_protocol.o: src/net/spdy/spdy_protocol.cc.o + +.PHONY : src/net/spdy/spdy_protocol.o + +# target to build an object file +src/net/spdy/spdy_protocol.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_protocol.cc.o +.PHONY : src/net/spdy/spdy_protocol.cc.o + +src/net/spdy/spdy_protocol.i: src/net/spdy/spdy_protocol.cc.i + +.PHONY : src/net/spdy/spdy_protocol.i + +# target to preprocess a source file +src/net/spdy/spdy_protocol.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_protocol.cc.i +.PHONY : src/net/spdy/spdy_protocol.cc.i + +src/net/spdy/spdy_protocol.s: src/net/spdy/spdy_protocol.cc.s + +.PHONY : src/net/spdy/spdy_protocol.s + +# target to generate assembly for a file +src/net/spdy/spdy_protocol.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/net/spdy/spdy_protocol.cc.s +.PHONY : src/net/spdy/spdy_protocol.cc.s + +src/stubs.o: src/stubs.cc.o + +.PHONY : src/stubs.o + +# target to build an object file +src/stubs.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/stubs.cc.o +.PHONY : src/stubs.cc.o + +src/stubs.i: src/stubs.cc.i + +.PHONY : src/stubs.i + +# target to preprocess a source file +src/stubs.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/stubs.cc.i +.PHONY : src/stubs.cc.i + +src/stubs.s: src/stubs.cc.s + +.PHONY : src/stubs.s + +# target to generate assembly for a file +src/stubs.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/stubs.cc.s +.PHONY : src/stubs.cc.s + +src/third_party/modp_b64/modp_b64.o: src/third_party/modp_b64/modp_b64.cc.o + +.PHONY : src/third_party/modp_b64/modp_b64.o + +# target to build an object file +src/third_party/modp_b64/modp_b64.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/modp_b64/modp_b64.cc.o +.PHONY : src/third_party/modp_b64/modp_b64.cc.o + +src/third_party/modp_b64/modp_b64.i: src/third_party/modp_b64/modp_b64.cc.i + +.PHONY : src/third_party/modp_b64/modp_b64.i + +# target to preprocess a source file +src/third_party/modp_b64/modp_b64.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/modp_b64/modp_b64.cc.i +.PHONY : src/third_party/modp_b64/modp_b64.cc.i + +src/third_party/modp_b64/modp_b64.s: src/third_party/modp_b64/modp_b64.cc.s + +.PHONY : src/third_party/modp_b64/modp_b64.s + +# target to generate assembly for a file +src/third_party/modp_b64/modp_b64.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/modp_b64/modp_b64.cc.s +.PHONY : src/third_party/modp_b64/modp_b64.cc.s + +src/third_party/zlib/adler32.o: src/third_party/zlib/adler32.c.o + +.PHONY : src/third_party/zlib/adler32.o + +# target to build an object file +src/third_party/zlib/adler32.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/adler32.c.o +.PHONY : src/third_party/zlib/adler32.c.o + +src/third_party/zlib/adler32.i: src/third_party/zlib/adler32.c.i + +.PHONY : src/third_party/zlib/adler32.i + +# target to preprocess a source file +src/third_party/zlib/adler32.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/adler32.c.i +.PHONY : src/third_party/zlib/adler32.c.i + +src/third_party/zlib/adler32.s: src/third_party/zlib/adler32.c.s + +.PHONY : src/third_party/zlib/adler32.s + +# target to generate assembly for a file +src/third_party/zlib/adler32.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/adler32.c.s +.PHONY : src/third_party/zlib/adler32.c.s + +src/third_party/zlib/compress.o: src/third_party/zlib/compress.c.o + +.PHONY : src/third_party/zlib/compress.o + +# target to build an object file +src/third_party/zlib/compress.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/compress.c.o +.PHONY : src/third_party/zlib/compress.c.o + +src/third_party/zlib/compress.i: src/third_party/zlib/compress.c.i + +.PHONY : src/third_party/zlib/compress.i + +# target to preprocess a source file +src/third_party/zlib/compress.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/compress.c.i +.PHONY : src/third_party/zlib/compress.c.i + +src/third_party/zlib/compress.s: src/third_party/zlib/compress.c.s + +.PHONY : src/third_party/zlib/compress.s + +# target to generate assembly for a file +src/third_party/zlib/compress.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/compress.c.s +.PHONY : src/third_party/zlib/compress.c.s + +src/third_party/zlib/crc32.o: src/third_party/zlib/crc32.c.o + +.PHONY : src/third_party/zlib/crc32.o + +# target to build an object file +src/third_party/zlib/crc32.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/crc32.c.o +.PHONY : src/third_party/zlib/crc32.c.o + +src/third_party/zlib/crc32.i: src/third_party/zlib/crc32.c.i + +.PHONY : src/third_party/zlib/crc32.i + +# target to preprocess a source file +src/third_party/zlib/crc32.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/crc32.c.i +.PHONY : src/third_party/zlib/crc32.c.i + +src/third_party/zlib/crc32.s: src/third_party/zlib/crc32.c.s + +.PHONY : src/third_party/zlib/crc32.s + +# target to generate assembly for a file +src/third_party/zlib/crc32.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/crc32.c.s +.PHONY : src/third_party/zlib/crc32.c.s + +src/third_party/zlib/deflate.o: src/third_party/zlib/deflate.c.o + +.PHONY : src/third_party/zlib/deflate.o + +# target to build an object file +src/third_party/zlib/deflate.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/deflate.c.o +.PHONY : src/third_party/zlib/deflate.c.o + +src/third_party/zlib/deflate.i: src/third_party/zlib/deflate.c.i + +.PHONY : src/third_party/zlib/deflate.i + +# target to preprocess a source file +src/third_party/zlib/deflate.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/deflate.c.i +.PHONY : src/third_party/zlib/deflate.c.i + +src/third_party/zlib/deflate.s: src/third_party/zlib/deflate.c.s + +.PHONY : src/third_party/zlib/deflate.s + +# target to generate assembly for a file +src/third_party/zlib/deflate.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/deflate.c.s +.PHONY : src/third_party/zlib/deflate.c.s + +src/third_party/zlib/gzclose.o: src/third_party/zlib/gzclose.c.o + +.PHONY : src/third_party/zlib/gzclose.o + +# target to build an object file +src/third_party/zlib/gzclose.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzclose.c.o +.PHONY : src/third_party/zlib/gzclose.c.o + +src/third_party/zlib/gzclose.i: src/third_party/zlib/gzclose.c.i + +.PHONY : src/third_party/zlib/gzclose.i + +# target to preprocess a source file +src/third_party/zlib/gzclose.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzclose.c.i +.PHONY : src/third_party/zlib/gzclose.c.i + +src/third_party/zlib/gzclose.s: src/third_party/zlib/gzclose.c.s + +.PHONY : src/third_party/zlib/gzclose.s + +# target to generate assembly for a file +src/third_party/zlib/gzclose.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzclose.c.s +.PHONY : src/third_party/zlib/gzclose.c.s + +src/third_party/zlib/gzlib.o: src/third_party/zlib/gzlib.c.o + +.PHONY : src/third_party/zlib/gzlib.o + +# target to build an object file +src/third_party/zlib/gzlib.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzlib.c.o +.PHONY : src/third_party/zlib/gzlib.c.o + +src/third_party/zlib/gzlib.i: src/third_party/zlib/gzlib.c.i + +.PHONY : src/third_party/zlib/gzlib.i + +# target to preprocess a source file +src/third_party/zlib/gzlib.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzlib.c.i +.PHONY : src/third_party/zlib/gzlib.c.i + +src/third_party/zlib/gzlib.s: src/third_party/zlib/gzlib.c.s + +.PHONY : src/third_party/zlib/gzlib.s + +# target to generate assembly for a file +src/third_party/zlib/gzlib.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzlib.c.s +.PHONY : src/third_party/zlib/gzlib.c.s + +src/third_party/zlib/gzread.o: src/third_party/zlib/gzread.c.o + +.PHONY : src/third_party/zlib/gzread.o + +# target to build an object file +src/third_party/zlib/gzread.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzread.c.o +.PHONY : src/third_party/zlib/gzread.c.o + +src/third_party/zlib/gzread.i: src/third_party/zlib/gzread.c.i + +.PHONY : src/third_party/zlib/gzread.i + +# target to preprocess a source file +src/third_party/zlib/gzread.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzread.c.i +.PHONY : src/third_party/zlib/gzread.c.i + +src/third_party/zlib/gzread.s: src/third_party/zlib/gzread.c.s + +.PHONY : src/third_party/zlib/gzread.s + +# target to generate assembly for a file +src/third_party/zlib/gzread.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzread.c.s +.PHONY : src/third_party/zlib/gzread.c.s + +src/third_party/zlib/gzwrite.o: src/third_party/zlib/gzwrite.c.o + +.PHONY : src/third_party/zlib/gzwrite.o + +# target to build an object file +src/third_party/zlib/gzwrite.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzwrite.c.o +.PHONY : src/third_party/zlib/gzwrite.c.o + +src/third_party/zlib/gzwrite.i: src/third_party/zlib/gzwrite.c.i + +.PHONY : src/third_party/zlib/gzwrite.i + +# target to preprocess a source file +src/third_party/zlib/gzwrite.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzwrite.c.i +.PHONY : src/third_party/zlib/gzwrite.c.i + +src/third_party/zlib/gzwrite.s: src/third_party/zlib/gzwrite.c.s + +.PHONY : src/third_party/zlib/gzwrite.s + +# target to generate assembly for a file +src/third_party/zlib/gzwrite.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/gzwrite.c.s +.PHONY : src/third_party/zlib/gzwrite.c.s + +src/third_party/zlib/infback.o: src/third_party/zlib/infback.c.o + +.PHONY : src/third_party/zlib/infback.o + +# target to build an object file +src/third_party/zlib/infback.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/infback.c.o +.PHONY : src/third_party/zlib/infback.c.o + +src/third_party/zlib/infback.i: src/third_party/zlib/infback.c.i + +.PHONY : src/third_party/zlib/infback.i + +# target to preprocess a source file +src/third_party/zlib/infback.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/infback.c.i +.PHONY : src/third_party/zlib/infback.c.i + +src/third_party/zlib/infback.s: src/third_party/zlib/infback.c.s + +.PHONY : src/third_party/zlib/infback.s + +# target to generate assembly for a file +src/third_party/zlib/infback.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/infback.c.s +.PHONY : src/third_party/zlib/infback.c.s + +src/third_party/zlib/inffast.o: src/third_party/zlib/inffast.c.o + +.PHONY : src/third_party/zlib/inffast.o + +# target to build an object file +src/third_party/zlib/inffast.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/inffast.c.o +.PHONY : src/third_party/zlib/inffast.c.o + +src/third_party/zlib/inffast.i: src/third_party/zlib/inffast.c.i + +.PHONY : src/third_party/zlib/inffast.i + +# target to preprocess a source file +src/third_party/zlib/inffast.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/inffast.c.i +.PHONY : src/third_party/zlib/inffast.c.i + +src/third_party/zlib/inffast.s: src/third_party/zlib/inffast.c.s + +.PHONY : src/third_party/zlib/inffast.s + +# target to generate assembly for a file +src/third_party/zlib/inffast.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/inffast.c.s +.PHONY : src/third_party/zlib/inffast.c.s + +src/third_party/zlib/inflate.o: src/third_party/zlib/inflate.c.o + +.PHONY : src/third_party/zlib/inflate.o + +# target to build an object file +src/third_party/zlib/inflate.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/inflate.c.o +.PHONY : src/third_party/zlib/inflate.c.o + +src/third_party/zlib/inflate.i: src/third_party/zlib/inflate.c.i + +.PHONY : src/third_party/zlib/inflate.i + +# target to preprocess a source file +src/third_party/zlib/inflate.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/inflate.c.i +.PHONY : src/third_party/zlib/inflate.c.i + +src/third_party/zlib/inflate.s: src/third_party/zlib/inflate.c.s + +.PHONY : src/third_party/zlib/inflate.s + +# target to generate assembly for a file +src/third_party/zlib/inflate.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/inflate.c.s +.PHONY : src/third_party/zlib/inflate.c.s + +src/third_party/zlib/inftrees.o: src/third_party/zlib/inftrees.c.o + +.PHONY : src/third_party/zlib/inftrees.o + +# target to build an object file +src/third_party/zlib/inftrees.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/inftrees.c.o +.PHONY : src/third_party/zlib/inftrees.c.o + +src/third_party/zlib/inftrees.i: src/third_party/zlib/inftrees.c.i + +.PHONY : src/third_party/zlib/inftrees.i + +# target to preprocess a source file +src/third_party/zlib/inftrees.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/inftrees.c.i +.PHONY : src/third_party/zlib/inftrees.c.i + +src/third_party/zlib/inftrees.s: src/third_party/zlib/inftrees.c.s + +.PHONY : src/third_party/zlib/inftrees.s + +# target to generate assembly for a file +src/third_party/zlib/inftrees.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/inftrees.c.s +.PHONY : src/third_party/zlib/inftrees.c.s + +src/third_party/zlib/simd_stub.o: src/third_party/zlib/simd_stub.c.o + +.PHONY : src/third_party/zlib/simd_stub.o + +# target to build an object file +src/third_party/zlib/simd_stub.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/simd_stub.c.o +.PHONY : src/third_party/zlib/simd_stub.c.o + +src/third_party/zlib/simd_stub.i: src/third_party/zlib/simd_stub.c.i + +.PHONY : src/third_party/zlib/simd_stub.i + +# target to preprocess a source file +src/third_party/zlib/simd_stub.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/simd_stub.c.i +.PHONY : src/third_party/zlib/simd_stub.c.i + +src/third_party/zlib/simd_stub.s: src/third_party/zlib/simd_stub.c.s + +.PHONY : src/third_party/zlib/simd_stub.s + +# target to generate assembly for a file +src/third_party/zlib/simd_stub.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/simd_stub.c.s +.PHONY : src/third_party/zlib/simd_stub.c.s + +src/third_party/zlib/trees.o: src/third_party/zlib/trees.c.o + +.PHONY : src/third_party/zlib/trees.o + +# target to build an object file +src/third_party/zlib/trees.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/trees.c.o +.PHONY : src/third_party/zlib/trees.c.o + +src/third_party/zlib/trees.i: src/third_party/zlib/trees.c.i + +.PHONY : src/third_party/zlib/trees.i + +# target to preprocess a source file +src/third_party/zlib/trees.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/trees.c.i +.PHONY : src/third_party/zlib/trees.c.i + +src/third_party/zlib/trees.s: src/third_party/zlib/trees.c.s + +.PHONY : src/third_party/zlib/trees.s + +# target to generate assembly for a file +src/third_party/zlib/trees.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/trees.c.s +.PHONY : src/third_party/zlib/trees.c.s + +src/third_party/zlib/uncompr.o: src/third_party/zlib/uncompr.c.o + +.PHONY : src/third_party/zlib/uncompr.o + +# target to build an object file +src/third_party/zlib/uncompr.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/uncompr.c.o +.PHONY : src/third_party/zlib/uncompr.c.o + +src/third_party/zlib/uncompr.i: src/third_party/zlib/uncompr.c.i + +.PHONY : src/third_party/zlib/uncompr.i + +# target to preprocess a source file +src/third_party/zlib/uncompr.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/uncompr.c.i +.PHONY : src/third_party/zlib/uncompr.c.i + +src/third_party/zlib/uncompr.s: src/third_party/zlib/uncompr.c.s + +.PHONY : src/third_party/zlib/uncompr.s + +# target to generate assembly for a file +src/third_party/zlib/uncompr.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/uncompr.c.s +.PHONY : src/third_party/zlib/uncompr.c.s + +src/third_party/zlib/zutil.o: src/third_party/zlib/zutil.c.o + +.PHONY : src/third_party/zlib/zutil.o + +# target to build an object file +src/third_party/zlib/zutil.c.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/zutil.c.o +.PHONY : src/third_party/zlib/zutil.c.o + +src/third_party/zlib/zutil.i: src/third_party/zlib/zutil.c.i + +.PHONY : src/third_party/zlib/zutil.i + +# target to preprocess a source file +src/third_party/zlib/zutil.c.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/zutil.c.i +.PHONY : src/third_party/zlib/zutil.c.i + +src/third_party/zlib/zutil.s: src/third_party/zlib/zutil.c.s + +.PHONY : src/third_party/zlib/zutil.s + +# target to generate assembly for a file +src/third_party/zlib/zutil.c.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/third_party/zlib/zutil.c.s +.PHONY : src/third_party/zlib/zutil.c.s + +src/url/url_constants.o: src/url/url_constants.cc.o + +.PHONY : src/url/url_constants.o + +# target to build an object file +src/url/url_constants.cc.o: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/url/url_constants.cc.o +.PHONY : src/url/url_constants.cc.o + +src/url/url_constants.i: src/url/url_constants.cc.i + +.PHONY : src/url/url_constants.i + +# target to preprocess a source file +src/url/url_constants.cc.i: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/url/url_constants.cc.i +.PHONY : src/url/url_constants.cc.i + +src/url/url_constants.s: src/url/url_constants.cc.s + +.PHONY : src/url/url_constants.s + +# target to generate assembly for a file +src/url/url_constants.cc.s: + $(MAKE) -f CMakeFiles/quic.dir/build.make CMakeFiles/quic.dir/src/url/url_constants.cc.s +.PHONY : src/url/url_constants.cc.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... edit_cache" + @echo "... rebuild_cache" + @echo "... quic" + @echo "... run_tests" + @echo "... all_tests" + @echo "... refcount_test" + @echo "... crypto" + @echo "... thread_test" + @echo "... constant_time_test" + @echo "... stack" + @echo "... lhash_test" + @echo "... lhash" + @echo "... err_test" + @echo "... err" + @echo "... buf" + @echo "... base64_test" + @echo "... base64" + @echo "... bytestring_test" + @echo "... bytestring" + @echo "... sha" + @echo "... md4" + @echo "... md5" + @echo "... gcm_test" + @echo "... modes" + @echo "... aes_test" + @echo "... aes" + @echo "... des" + @echo "... rc4" + @echo "... conf" + @echo "... chacha" + @echo "... poly1305_test" + @echo "... poly1305" + @echo "... x25519_test" + @echo "... ed25519_test" + @echo "... curve25519" + @echo "... digest_test" + @echo "... digest" + @echo "... aead_test" + @echo "... cipher_test" + @echo "... cipher" + @echo "... rand" + @echo "... bio_test" + @echo "... bio" + @echo "... bn_test" + @echo "... bn" + @echo "... obj" + @echo "... asn1" + @echo "... engine" + @echo "... dh_test" + @echo "... dh" + @echo "... dsa_test" + @echo "... dsa" + @echo "... rsa_test" + @echo "... rsa" + @echo "... ec_test" + @echo "... example_mul" + @echo "... ec" + @echo "... ecdh" + @echo "... ecdsa_test" + @echo "... ecdsa" + @echo "... hmac_test" + @echo "... hmac" + @echo "... cmac_test" + @echo "... cmac" + @echo "... evp_test" + @echo "... pbkdf_test" + @echo "... evp_extra_test" + @echo "... evp" + @echo "... hkdf_test" + @echo "... hkdf" + @echo "... pem" + @echo "... pkcs7_test" + @echo "... x509" + @echo "... tab_test" + @echo "... v3name_test" + @echo "... x509v3" + @echo "... pkcs8_test" + @echo "... pkcs12_test" + @echo "... pkcs8" + @echo "... test_support" + @echo "... ssl_test" + @echo "... ssl" + @echo "... pqueue_test" + @echo "... pqueue" + @echo "... bssl_shim" + @echo "... bssl" + @echo "... decrepit" + @echo "... bio_decrepit" + @echo "... blowfish" + @echo "... cast" + @echo "... des_decrepit" + @echo "... rsa_decrepit" + @echo "... xts" + @echo "... protobuf" + @echo "... src/base/at_exit.o" + @echo "... src/base/at_exit.i" + @echo "... src/base/at_exit.s" + @echo "... src/base/base64.o" + @echo "... src/base/base64.i" + @echo "... src/base/base64.s" + @echo "... src/base/base_switches.o" + @echo "... src/base/base_switches.i" + @echo "... src/base/base_switches.s" + @echo "... src/base/bind_helpers.o" + @echo "... src/base/bind_helpers.i" + @echo "... src/base/bind_helpers.s" + @echo "... src/base/callback_helpers.o" + @echo "... src/base/callback_helpers.i" + @echo "... src/base/callback_helpers.s" + @echo "... src/base/callback_internal.o" + @echo "... src/base/callback_internal.i" + @echo "... src/base/callback_internal.s" + @echo "... src/base/command_line.o" + @echo "... src/base/command_line.i" + @echo "... src/base/command_line.s" + @echo "... src/base/debug/alias.o" + @echo "... src/base/debug/alias.i" + @echo "... src/base/debug/alias.s" + @echo "... src/base/debug/debugger.o" + @echo "... src/base/debug/debugger.i" + @echo "... src/base/debug/debugger.s" + @echo "... src/base/debug/stack_trace.o" + @echo "... src/base/debug/stack_trace.i" + @echo "... src/base/debug/stack_trace.s" + @echo "... src/base/files/file_path.o" + @echo "... src/base/files/file_path.i" + @echo "... src/base/files/file_path.s" + @echo "... src/base/files/file_path_constants.o" + @echo "... src/base/files/file_path_constants.i" + @echo "... src/base/files/file_path_constants.s" + @echo "... src/base/json/json_parser.o" + @echo "... src/base/json/json_parser.i" + @echo "... src/base/json/json_parser.s" + @echo "... src/base/json/json_reader.o" + @echo "... src/base/json/json_reader.i" + @echo "... src/base/json/json_reader.s" + @echo "... src/base/json/json_string_value_serializer.o" + @echo "... src/base/json/json_string_value_serializer.i" + @echo "... src/base/json/json_string_value_serializer.s" + @echo "... src/base/json/json_writer.o" + @echo "... src/base/json/json_writer.i" + @echo "... src/base/json/json_writer.s" + @echo "... src/base/json/string_escape.o" + @echo "... src/base/json/string_escape.i" + @echo "... src/base/json/string_escape.s" + @echo "... src/base/lazy_instance.o" + @echo "... src/base/lazy_instance.i" + @echo "... src/base/lazy_instance.s" + @echo "... src/base/location.o" + @echo "... src/base/location.i" + @echo "... src/base/location.s" + @echo "... src/base/logging.o" + @echo "... src/base/logging.i" + @echo "... src/base/logging.s" + @echo "... src/base/mac/bundle_locations.o" + @echo "... src/base/mac/bundle_locations.i" + @echo "... src/base/mac/bundle_locations.s" + @echo "... src/base/mac/foundation_util.o" + @echo "... src/base/mac/foundation_util.i" + @echo "... src/base/mac/foundation_util.s" + @echo "... src/base/mac/mach_logging.o" + @echo "... src/base/mac/mach_logging.i" + @echo "... src/base/mac/mach_logging.s" + @echo "... src/base/mac/scoped_mach_port.o" + @echo "... src/base/mac/scoped_mach_port.i" + @echo "... src/base/mac/scoped_mach_port.s" + @echo "... src/base/md5.o" + @echo "... src/base/md5.i" + @echo "... src/base/md5.s" + @echo "... src/base/memory/aligned_memory.o" + @echo "... src/base/memory/aligned_memory.i" + @echo "... src/base/memory/aligned_memory.s" + @echo "... src/base/memory/ref_counted.o" + @echo "... src/base/memory/ref_counted.i" + @echo "... src/base/memory/ref_counted.s" + @echo "... src/base/memory/singleton.o" + @echo "... src/base/memory/singleton.i" + @echo "... src/base/memory/singleton.s" + @echo "... src/base/memory/weak_ptr.o" + @echo "... src/base/memory/weak_ptr.i" + @echo "... src/base/memory/weak_ptr.s" + @echo "... src/base/metrics/bucket_ranges.o" + @echo "... src/base/metrics/bucket_ranges.i" + @echo "... src/base/metrics/bucket_ranges.s" + @echo "... src/base/metrics/histogram.o" + @echo "... src/base/metrics/histogram.i" + @echo "... src/base/metrics/histogram.s" + @echo "... src/base/metrics/histogram_base.o" + @echo "... src/base/metrics/histogram_base.i" + @echo "... src/base/metrics/histogram_base.s" + @echo "... src/base/metrics/histogram_samples.o" + @echo "... src/base/metrics/histogram_samples.i" + @echo "... src/base/metrics/histogram_samples.s" + @echo "... src/base/metrics/metrics_hashes.o" + @echo "... src/base/metrics/metrics_hashes.i" + @echo "... src/base/metrics/metrics_hashes.s" + @echo "... src/base/metrics/sample_map.o" + @echo "... src/base/metrics/sample_map.i" + @echo "... src/base/metrics/sample_map.s" + @echo "... src/base/metrics/sample_vector.o" + @echo "... src/base/metrics/sample_vector.i" + @echo "... src/base/metrics/sample_vector.s" + @echo "... src/base/metrics/sparse_histogram.o" + @echo "... src/base/metrics/sparse_histogram.i" + @echo "... src/base/metrics/sparse_histogram.s" + @echo "... src/base/metrics/statistics_recorder.o" + @echo "... src/base/metrics/statistics_recorder.i" + @echo "... src/base/metrics/statistics_recorder.s" + @echo "... src/base/pickle.o" + @echo "... src/base/pickle.i" + @echo "... src/base/pickle.s" + @echo "... src/base/posix/safe_strerror.o" + @echo "... src/base/posix/safe_strerror.i" + @echo "... src/base/posix/safe_strerror.s" + @echo "... src/base/process/process_handle_posix.o" + @echo "... src/base/process/process_handle_posix.i" + @echo "... src/base/process/process_handle_posix.s" + @echo "... src/base/rand_util.o" + @echo "... src/base/rand_util.i" + @echo "... src/base/rand_util.s" + @echo "... src/base/rand_util_posix.o" + @echo "... src/base/rand_util_posix.i" + @echo "... src/base/rand_util_posix.s" + @echo "... src/base/strings/string16.o" + @echo "... src/base/strings/string16.i" + @echo "... src/base/strings/string16.s" + @echo "... src/base/strings/string_number_conversions.o" + @echo "... src/base/strings/string_number_conversions.i" + @echo "... src/base/strings/string_number_conversions.s" + @echo "... src/base/strings/string_piece.o" + @echo "... src/base/strings/string_piece.i" + @echo "... src/base/strings/string_piece.s" + @echo "... src/base/strings/string_split.o" + @echo "... src/base/strings/string_split.i" + @echo "... src/base/strings/string_split.s" + @echo "... src/base/strings/string_util.o" + @echo "... src/base/strings/string_util.i" + @echo "... src/base/strings/string_util.s" + @echo "... src/base/strings/string_util_constants.o" + @echo "... src/base/strings/string_util_constants.i" + @echo "... src/base/strings/string_util_constants.s" + @echo "... src/base/strings/stringprintf.o" + @echo "... src/base/strings/stringprintf.i" + @echo "... src/base/strings/stringprintf.s" + @echo "... src/base/strings/sys_string_conversions_mac.o" + @echo "... src/base/strings/sys_string_conversions_mac.i" + @echo "... src/base/strings/sys_string_conversions_mac.s" + @echo "... src/base/strings/sys_string_conversions_posix.o" + @echo "... src/base/strings/sys_string_conversions_posix.i" + @echo "... src/base/strings/sys_string_conversions_posix.s" + @echo "... src/base/strings/utf_string_conversion_utils.o" + @echo "... src/base/strings/utf_string_conversion_utils.i" + @echo "... src/base/strings/utf_string_conversion_utils.s" + @echo "... src/base/strings/utf_string_conversions.o" + @echo "... src/base/strings/utf_string_conversions.i" + @echo "... src/base/strings/utf_string_conversions.s" + @echo "... src/base/synchronization/condition_variable_posix.o" + @echo "... src/base/synchronization/condition_variable_posix.i" + @echo "... src/base/synchronization/condition_variable_posix.s" + @echo "... src/base/synchronization/lock.o" + @echo "... src/base/synchronization/lock.i" + @echo "... src/base/synchronization/lock.s" + @echo "... src/base/synchronization/lock_impl_posix.o" + @echo "... src/base/synchronization/lock_impl_posix.i" + @echo "... src/base/synchronization/lock_impl_posix.s" + @echo "... src/base/third_party/dmg_fp/dtoa.o" + @echo "... src/base/third_party/dmg_fp/dtoa.i" + @echo "... src/base/third_party/dmg_fp/dtoa.s" + @echo "... src/base/third_party/dmg_fp/g_fmt.o" + @echo "... src/base/third_party/dmg_fp/g_fmt.i" + @echo "... src/base/third_party/dmg_fp/g_fmt.s" + @echo "... src/base/third_party/icu/icu_utf.o" + @echo "... src/base/third_party/icu/icu_utf.i" + @echo "... src/base/third_party/icu/icu_utf.s" + @echo "... src/base/third_party/nspr/prtime.o" + @echo "... src/base/third_party/nspr/prtime.i" + @echo "... src/base/third_party/nspr/prtime.s" + @echo "... src/base/third_party/superfasthash/superfasthash.o" + @echo "... src/base/third_party/superfasthash/superfasthash.i" + @echo "... src/base/third_party/superfasthash/superfasthash.s" + @echo "... src/base/threading/platform_thread_internal_posix.o" + @echo "... src/base/threading/platform_thread_internal_posix.i" + @echo "... src/base/threading/platform_thread_internal_posix.s" + @echo "... src/base/threading/platform_thread_mac.o" + @echo "... src/base/threading/platform_thread_mac.i" + @echo "... src/base/threading/platform_thread_mac.s" + @echo "... src/base/threading/platform_thread_posix.o" + @echo "... src/base/threading/platform_thread_posix.i" + @echo "... src/base/threading/platform_thread_posix.s" + @echo "... src/base/threading/thread_collision_warner.o" + @echo "... src/base/threading/thread_collision_warner.i" + @echo "... src/base/threading/thread_collision_warner.s" + @echo "... src/base/threading/thread_id_name_manager.o" + @echo "... src/base/threading/thread_id_name_manager.i" + @echo "... src/base/threading/thread_id_name_manager.s" + @echo "... src/base/threading/thread_local_posix.o" + @echo "... src/base/threading/thread_local_posix.i" + @echo "... src/base/threading/thread_local_posix.s" + @echo "... src/base/threading/thread_local_storage.o" + @echo "... src/base/threading/thread_local_storage.i" + @echo "... src/base/threading/thread_local_storage.s" + @echo "... src/base/threading/thread_local_storage_posix.o" + @echo "... src/base/threading/thread_local_storage_posix.i" + @echo "... src/base/threading/thread_local_storage_posix.s" + @echo "... src/base/threading/thread_restrictions.o" + @echo "... src/base/threading/thread_restrictions.i" + @echo "... src/base/threading/thread_restrictions.s" + @echo "... src/base/time/time.o" + @echo "... src/base/time/time.i" + @echo "... src/base/time/time.s" + @echo "... src/base/time/time_mac.o" + @echo "... src/base/time/time_mac.i" + @echo "... src/base/time/time_mac.s" + @echo "... src/base/time/time_posix.o" + @echo "... src/base/time/time_posix.i" + @echo "... src/base/time/time_posix.s" + @echo "... src/base/values.o" + @echo "... src/base/values.i" + @echo "... src/base/values.s" + @echo "... src/base/vlog.o" + @echo "... src/base/vlog.i" + @echo "... src/base/vlog.s" + @echo "... src/crypto/curve25519-donna.o" + @echo "... src/crypto/curve25519-donna.i" + @echo "... src/crypto/curve25519-donna.s" + @echo "... src/crypto/curve25519_openssl.o" + @echo "... src/crypto/curve25519_openssl.i" + @echo "... src/crypto/curve25519_openssl.s" + @echo "... src/crypto/hkdf.o" + @echo "... src/crypto/hkdf.i" + @echo "... src/crypto/hkdf.s" + @echo "... src/crypto/hmac.o" + @echo "... src/crypto/hmac.i" + @echo "... src/crypto/hmac.s" + @echo "... src/crypto/hmac_openssl.o" + @echo "... src/crypto/hmac_openssl.i" + @echo "... src/crypto/hmac_openssl.s" + @echo "... src/crypto/openssl_util.o" + @echo "... src/crypto/openssl_util.i" + @echo "... src/crypto/openssl_util.s" + @echo "... src/crypto/random.o" + @echo "... src/crypto/random.i" + @echo "... src/crypto/random.s" + @echo "... src/crypto/secure_hash_openssl.o" + @echo "... src/crypto/secure_hash_openssl.i" + @echo "... src/crypto/secure_hash_openssl.s" + @echo "... src/crypto/secure_util.o" + @echo "... src/crypto/secure_util.i" + @echo "... src/crypto/secure_util.s" + @echo "... src/crypto/symmetric_key_openssl.o" + @echo "... src/crypto/symmetric_key_openssl.i" + @echo "... src/crypto/symmetric_key_openssl.s" + @echo "... src/net/base/address_family.o" + @echo "... src/net/base/address_family.i" + @echo "... src/net/base/address_family.s" + @echo "... src/net/base/host_port_pair.o" + @echo "... src/net/base/host_port_pair.i" + @echo "... src/net/base/host_port_pair.s" + @echo "... src/net/base/int128.o" + @echo "... src/net/base/int128.i" + @echo "... src/net/base/int128.s" + @echo "... src/net/base/io_buffer.o" + @echo "... src/net/base/io_buffer.i" + @echo "... src/net/base/io_buffer.s" + @echo "... src/net/base/ip_endpoint.o" + @echo "... src/net/base/ip_endpoint.i" + @echo "... src/net/base/ip_endpoint.s" + @echo "... src/net/base/net_errors.o" + @echo "... src/net/base/net_errors.i" + @echo "... src/net/base/net_errors.s" + @echo "... src/net/base/port_util.o" + @echo "... src/net/base/port_util.i" + @echo "... src/net/base/port_util.s" + @echo "... src/net/quic/congestion_control/cubic.o" + @echo "... src/net/quic/congestion_control/cubic.i" + @echo "... src/net/quic/congestion_control/cubic.s" + @echo "... src/net/quic/congestion_control/cubic_bytes.o" + @echo "... src/net/quic/congestion_control/cubic_bytes.i" + @echo "... src/net/quic/congestion_control/cubic_bytes.s" + @echo "... src/net/quic/congestion_control/general_loss_algorithm.o" + @echo "... src/net/quic/congestion_control/general_loss_algorithm.i" + @echo "... src/net/quic/congestion_control/general_loss_algorithm.s" + @echo "... src/net/quic/congestion_control/hybrid_slow_start.o" + @echo "... src/net/quic/congestion_control/hybrid_slow_start.i" + @echo "... src/net/quic/congestion_control/hybrid_slow_start.s" + @echo "... src/net/quic/congestion_control/loss_detection_interface.o" + @echo "... src/net/quic/congestion_control/loss_detection_interface.i" + @echo "... src/net/quic/congestion_control/loss_detection_interface.s" + @echo "... src/net/quic/congestion_control/pacing_sender.o" + @echo "... src/net/quic/congestion_control/pacing_sender.i" + @echo "... src/net/quic/congestion_control/pacing_sender.s" + @echo "... src/net/quic/congestion_control/prr_sender.o" + @echo "... src/net/quic/congestion_control/prr_sender.i" + @echo "... src/net/quic/congestion_control/prr_sender.s" + @echo "... src/net/quic/congestion_control/rtt_stats.o" + @echo "... src/net/quic/congestion_control/rtt_stats.i" + @echo "... src/net/quic/congestion_control/rtt_stats.s" + @echo "... src/net/quic/congestion_control/send_algorithm_interface.o" + @echo "... src/net/quic/congestion_control/send_algorithm_interface.i" + @echo "... src/net/quic/congestion_control/send_algorithm_interface.s" + @echo "... src/net/quic/congestion_control/tcp_cubic_bytes_sender.o" + @echo "... src/net/quic/congestion_control/tcp_cubic_bytes_sender.i" + @echo "... src/net/quic/congestion_control/tcp_cubic_bytes_sender.s" + @echo "... src/net/quic/congestion_control/tcp_cubic_sender.o" + @echo "... src/net/quic/congestion_control/tcp_cubic_sender.i" + @echo "... src/net/quic/congestion_control/tcp_cubic_sender.s" + @echo "... src/net/quic/crypto/aead_base_decrypter_openssl.o" + @echo "... src/net/quic/crypto/aead_base_decrypter_openssl.i" + @echo "... src/net/quic/crypto/aead_base_decrypter_openssl.s" + @echo "... src/net/quic/crypto/aead_base_encrypter_openssl.o" + @echo "... src/net/quic/crypto/aead_base_encrypter_openssl.i" + @echo "... src/net/quic/crypto/aead_base_encrypter_openssl.s" + @echo "... src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.o" + @echo "... src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.i" + @echo "... src/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.s" + @echo "... src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.o" + @echo "... src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.i" + @echo "... src/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.s" + @echo "... src/net/quic/crypto/cert_compressor.o" + @echo "... src/net/quic/crypto/cert_compressor.i" + @echo "... src/net/quic/crypto/cert_compressor.s" + @echo "... src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.o" + @echo "... src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.i" + @echo "... src/net/quic/crypto/chacha20_poly1305_decrypter_openssl.s" + @echo "... src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.o" + @echo "... src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.i" + @echo "... src/net/quic/crypto/chacha20_poly1305_encrypter_openssl.s" + @echo "... src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.o" + @echo "... src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.i" + @echo "... src/net/quic/crypto/chacha20_poly1305_rfc7539_decrypter_openssl.s" + @echo "... src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.o" + @echo "... src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.i" + @echo "... src/net/quic/crypto/chacha20_poly1305_rfc7539_encrypter_openssl.s" + @echo "... src/net/quic/crypto/channel_id.o" + @echo "... src/net/quic/crypto/channel_id.i" + @echo "... src/net/quic/crypto/channel_id.s" + @echo "... src/net/quic/crypto/channel_id_openssl.o" + @echo "... src/net/quic/crypto/channel_id_openssl.i" + @echo "... src/net/quic/crypto/channel_id_openssl.s" + @echo "... src/net/quic/crypto/common_cert_set.o" + @echo "... src/net/quic/crypto/common_cert_set.i" + @echo "... src/net/quic/crypto/common_cert_set.s" + @echo "... src/net/quic/crypto/crypto_framer.o" + @echo "... src/net/quic/crypto/crypto_framer.i" + @echo "... src/net/quic/crypto/crypto_framer.s" + @echo "... src/net/quic/crypto/crypto_handshake.o" + @echo "... src/net/quic/crypto/crypto_handshake.i" + @echo "... src/net/quic/crypto/crypto_handshake.s" + @echo "... src/net/quic/crypto/crypto_handshake_message.o" + @echo "... src/net/quic/crypto/crypto_handshake_message.i" + @echo "... src/net/quic/crypto/crypto_handshake_message.s" + @echo "... src/net/quic/crypto/crypto_secret_boxer.o" + @echo "... src/net/quic/crypto/crypto_secret_boxer.i" + @echo "... src/net/quic/crypto/crypto_secret_boxer.s" + @echo "... src/net/quic/crypto/crypto_server_config_protobuf.o" + @echo "... src/net/quic/crypto/crypto_server_config_protobuf.i" + @echo "... src/net/quic/crypto/crypto_server_config_protobuf.s" + @echo "... src/net/quic/crypto/crypto_utils.o" + @echo "... src/net/quic/crypto/crypto_utils.i" + @echo "... src/net/quic/crypto/crypto_utils.s" + @echo "... src/net/quic/crypto/curve25519_key_exchange.o" + @echo "... src/net/quic/crypto/curve25519_key_exchange.i" + @echo "... src/net/quic/crypto/curve25519_key_exchange.s" + @echo "... src/net/quic/crypto/local_strike_register_client.o" + @echo "... src/net/quic/crypto/local_strike_register_client.i" + @echo "... src/net/quic/crypto/local_strike_register_client.s" + @echo "... src/net/quic/crypto/null_decrypter.o" + @echo "... src/net/quic/crypto/null_decrypter.i" + @echo "... src/net/quic/crypto/null_decrypter.s" + @echo "... src/net/quic/crypto/null_encrypter.o" + @echo "... src/net/quic/crypto/null_encrypter.i" + @echo "... src/net/quic/crypto/null_encrypter.s" + @echo "... src/net/quic/crypto/p256_key_exchange_openssl.o" + @echo "... src/net/quic/crypto/p256_key_exchange_openssl.i" + @echo "... src/net/quic/crypto/p256_key_exchange_openssl.s" + @echo "... src/net/quic/crypto/quic_crypto_client_config.o" + @echo "... src/net/quic/crypto/quic_crypto_client_config.i" + @echo "... src/net/quic/crypto/quic_crypto_client_config.s" + @echo "... src/net/quic/crypto/quic_crypto_server_config.o" + @echo "... src/net/quic/crypto/quic_crypto_server_config.i" + @echo "... src/net/quic/crypto/quic_crypto_server_config.s" + @echo "... src/net/quic/crypto/quic_decrypter.o" + @echo "... src/net/quic/crypto/quic_decrypter.i" + @echo "... src/net/quic/crypto/quic_decrypter.s" + @echo "... src/net/quic/crypto/quic_encrypter.o" + @echo "... src/net/quic/crypto/quic_encrypter.i" + @echo "... src/net/quic/crypto/quic_encrypter.s" + @echo "... src/net/quic/crypto/quic_random.o" + @echo "... src/net/quic/crypto/quic_random.i" + @echo "... src/net/quic/crypto/quic_random.s" + @echo "... src/net/quic/crypto/scoped_evp_aead_ctx.o" + @echo "... src/net/quic/crypto/scoped_evp_aead_ctx.i" + @echo "... src/net/quic/crypto/scoped_evp_aead_ctx.s" + @echo "... src/net/quic/crypto/strike_register.o" + @echo "... src/net/quic/crypto/strike_register.i" + @echo "... src/net/quic/crypto/strike_register.s" + @echo "... src/net/quic/iovector.o" + @echo "... src/net/quic/iovector.i" + @echo "... src/net/quic/iovector.s" + @echo "... src/net/quic/proto/cached_network_parameters.pb.o" + @echo "... src/net/quic/proto/cached_network_parameters.pb.i" + @echo "... src/net/quic/proto/cached_network_parameters.pb.s" + @echo "... src/net/quic/proto/source_address_token.pb.o" + @echo "... src/net/quic/proto/source_address_token.pb.i" + @echo "... src/net/quic/proto/source_address_token.pb.s" + @echo "... src/net/quic/quic_alarm.o" + @echo "... src/net/quic/quic_alarm.i" + @echo "... src/net/quic/quic_alarm.s" + @echo "... src/net/quic/quic_bandwidth.o" + @echo "... src/net/quic/quic_bandwidth.i" + @echo "... src/net/quic/quic_bandwidth.s" + @echo "... src/net/quic/quic_client_session_base.o" + @echo "... src/net/quic/quic_client_session_base.i" + @echo "... src/net/quic/quic_client_session_base.s" + @echo "... src/net/quic/quic_clock.o" + @echo "... src/net/quic/quic_clock.i" + @echo "... src/net/quic/quic_clock.s" + @echo "... src/net/quic/quic_config.o" + @echo "... src/net/quic/quic_config.i" + @echo "... src/net/quic/quic_config.s" + @echo "... src/net/quic/quic_connection.o" + @echo "... src/net/quic/quic_connection.i" + @echo "... src/net/quic/quic_connection.s" + @echo "... src/net/quic/quic_connection_stats.o" + @echo "... src/net/quic/quic_connection_stats.i" + @echo "... src/net/quic/quic_connection_stats.s" + @echo "... src/net/quic/quic_crypto_client_stream.o" + @echo "... src/net/quic/quic_crypto_client_stream.i" + @echo "... src/net/quic/quic_crypto_client_stream.s" + @echo "... src/net/quic/quic_crypto_server_stream.o" + @echo "... src/net/quic/quic_crypto_server_stream.i" + @echo "... src/net/quic/quic_crypto_server_stream.s" + @echo "... src/net/quic/quic_crypto_stream.o" + @echo "... src/net/quic/quic_crypto_stream.i" + @echo "... src/net/quic/quic_crypto_stream.s" + @echo "... src/net/quic/quic_data_reader.o" + @echo "... src/net/quic/quic_data_reader.i" + @echo "... src/net/quic/quic_data_reader.s" + @echo "... src/net/quic/quic_data_writer.o" + @echo "... src/net/quic/quic_data_writer.i" + @echo "... src/net/quic/quic_data_writer.s" + @echo "... src/net/quic/quic_fec_group.o" + @echo "... src/net/quic/quic_fec_group.i" + @echo "... src/net/quic/quic_fec_group.s" + @echo "... src/net/quic/quic_fec_group_interface.o" + @echo "... src/net/quic/quic_fec_group_interface.i" + @echo "... src/net/quic/quic_fec_group_interface.s" + @echo "... src/net/quic/quic_flags.o" + @echo "... src/net/quic/quic_flags.i" + @echo "... src/net/quic/quic_flags.s" + @echo "... src/net/quic/quic_flow_controller.o" + @echo "... src/net/quic/quic_flow_controller.i" + @echo "... src/net/quic/quic_flow_controller.s" + @echo "... src/net/quic/quic_frame_list.o" + @echo "... src/net/quic/quic_frame_list.i" + @echo "... src/net/quic/quic_frame_list.s" + @echo "... src/net/quic/quic_framer.o" + @echo "... src/net/quic/quic_framer.i" + @echo "... src/net/quic/quic_framer.s" + @echo "... src/net/quic/quic_headers_stream.o" + @echo "... src/net/quic/quic_headers_stream.i" + @echo "... src/net/quic/quic_headers_stream.s" + @echo "... src/net/quic/quic_packet_creator.o" + @echo "... src/net/quic/quic_packet_creator.i" + @echo "... src/net/quic/quic_packet_creator.s" + @echo "... src/net/quic/quic_packet_generator.o" + @echo "... src/net/quic/quic_packet_generator.i" + @echo "... src/net/quic/quic_packet_generator.s" + @echo "... src/net/quic/quic_protocol.o" + @echo "... src/net/quic/quic_protocol.i" + @echo "... src/net/quic/quic_protocol.s" + @echo "... src/net/quic/quic_received_packet_manager.o" + @echo "... src/net/quic/quic_received_packet_manager.i" + @echo "... src/net/quic/quic_received_packet_manager.s" + @echo "... src/net/quic/quic_sent_entropy_manager.o" + @echo "... src/net/quic/quic_sent_entropy_manager.i" + @echo "... src/net/quic/quic_sent_entropy_manager.s" + @echo "... src/net/quic/quic_sent_packet_manager.o" + @echo "... src/net/quic/quic_sent_packet_manager.i" + @echo "... src/net/quic/quic_sent_packet_manager.s" + @echo "... src/net/quic/quic_server_id.o" + @echo "... src/net/quic/quic_server_id.i" + @echo "... src/net/quic/quic_server_id.s" + @echo "... src/net/quic/quic_session.o" + @echo "... src/net/quic/quic_session.i" + @echo "... src/net/quic/quic_session.s" + @echo "... src/net/quic/quic_simple_buffer_allocator.o" + @echo "... src/net/quic/quic_simple_buffer_allocator.i" + @echo "... src/net/quic/quic_simple_buffer_allocator.s" + @echo "... src/net/quic/quic_socket_address_coder.o" + @echo "... src/net/quic/quic_socket_address_coder.i" + @echo "... src/net/quic/quic_socket_address_coder.s" + @echo "... src/net/quic/quic_spdy_session.o" + @echo "... src/net/quic/quic_spdy_session.i" + @echo "... src/net/quic/quic_spdy_session.s" + @echo "... src/net/quic/quic_spdy_stream.o" + @echo "... src/net/quic/quic_spdy_stream.i" + @echo "... src/net/quic/quic_spdy_stream.s" + @echo "... src/net/quic/quic_stream_sequencer.o" + @echo "... src/net/quic/quic_stream_sequencer.i" + @echo "... src/net/quic/quic_stream_sequencer.s" + @echo "... src/net/quic/quic_sustained_bandwidth_recorder.o" + @echo "... src/net/quic/quic_sustained_bandwidth_recorder.i" + @echo "... src/net/quic/quic_sustained_bandwidth_recorder.s" + @echo "... src/net/quic/quic_time.o" + @echo "... src/net/quic/quic_time.i" + @echo "... src/net/quic/quic_time.s" + @echo "... src/net/quic/quic_types.o" + @echo "... src/net/quic/quic_types.i" + @echo "... src/net/quic/quic_types.s" + @echo "... src/net/quic/quic_unacked_packet_map.o" + @echo "... src/net/quic/quic_unacked_packet_map.i" + @echo "... src/net/quic/quic_unacked_packet_map.s" + @echo "... src/net/quic/quic_utils.o" + @echo "... src/net/quic/quic_utils.i" + @echo "... src/net/quic/quic_utils.s" + @echo "... src/net/quic/quic_write_blocked_list.o" + @echo "... src/net/quic/quic_write_blocked_list.i" + @echo "... src/net/quic/quic_write_blocked_list.s" + @echo "... src/net/quic/reliable_quic_stream.o" + @echo "... src/net/quic/reliable_quic_stream.i" + @echo "... src/net/quic/reliable_quic_stream.s" + @echo "... src/net/quic/stream_sequencer_buffer.o" + @echo "... src/net/quic/stream_sequencer_buffer.i" + @echo "... src/net/quic/stream_sequencer_buffer.s" + @echo "... src/net/spdy/hpack/hpack_constants.o" + @echo "... src/net/spdy/hpack/hpack_constants.i" + @echo "... src/net/spdy/hpack/hpack_constants.s" + @echo "... src/net/spdy/hpack/hpack_decoder.o" + @echo "... src/net/spdy/hpack/hpack_decoder.i" + @echo "... src/net/spdy/hpack/hpack_decoder.s" + @echo "... src/net/spdy/hpack/hpack_encoder.o" + @echo "... src/net/spdy/hpack/hpack_encoder.i" + @echo "... src/net/spdy/hpack/hpack_encoder.s" + @echo "... src/net/spdy/hpack/hpack_entry.o" + @echo "... src/net/spdy/hpack/hpack_entry.i" + @echo "... src/net/spdy/hpack/hpack_entry.s" + @echo "... src/net/spdy/hpack/hpack_header_table.o" + @echo "... src/net/spdy/hpack/hpack_header_table.i" + @echo "... src/net/spdy/hpack/hpack_header_table.s" + @echo "... src/net/spdy/hpack/hpack_huffman_table.o" + @echo "... src/net/spdy/hpack/hpack_huffman_table.i" + @echo "... src/net/spdy/hpack/hpack_huffman_table.s" + @echo "... src/net/spdy/hpack/hpack_input_stream.o" + @echo "... src/net/spdy/hpack/hpack_input_stream.i" + @echo "... src/net/spdy/hpack/hpack_input_stream.s" + @echo "... src/net/spdy/hpack/hpack_output_stream.o" + @echo "... src/net/spdy/hpack/hpack_output_stream.i" + @echo "... src/net/spdy/hpack/hpack_output_stream.s" + @echo "... src/net/spdy/hpack/hpack_static_table.o" + @echo "... src/net/spdy/hpack/hpack_static_table.i" + @echo "... src/net/spdy/hpack/hpack_static_table.s" + @echo "... src/net/spdy/spdy_alt_svc_wire_format.o" + @echo "... src/net/spdy/spdy_alt_svc_wire_format.i" + @echo "... src/net/spdy/spdy_alt_svc_wire_format.s" + @echo "... src/net/spdy/spdy_frame_builder.o" + @echo "... src/net/spdy/spdy_frame_builder.i" + @echo "... src/net/spdy/spdy_frame_builder.s" + @echo "... src/net/spdy/spdy_frame_reader.o" + @echo "... src/net/spdy/spdy_frame_reader.i" + @echo "... src/net/spdy/spdy_frame_reader.s" + @echo "... src/net/spdy/spdy_framer.o" + @echo "... src/net/spdy/spdy_framer.i" + @echo "... src/net/spdy/spdy_framer.s" + @echo "... src/net/spdy/spdy_header_block.o" + @echo "... src/net/spdy/spdy_header_block.i" + @echo "... src/net/spdy/spdy_header_block.s" + @echo "... src/net/spdy/spdy_protocol.o" + @echo "... src/net/spdy/spdy_protocol.i" + @echo "... src/net/spdy/spdy_protocol.s" + @echo "... src/stubs.o" + @echo "... src/stubs.i" + @echo "... src/stubs.s" + @echo "... src/third_party/modp_b64/modp_b64.o" + @echo "... src/third_party/modp_b64/modp_b64.i" + @echo "... src/third_party/modp_b64/modp_b64.s" + @echo "... src/third_party/zlib/adler32.o" + @echo "... src/third_party/zlib/adler32.i" + @echo "... src/third_party/zlib/adler32.s" + @echo "... src/third_party/zlib/compress.o" + @echo "... src/third_party/zlib/compress.i" + @echo "... src/third_party/zlib/compress.s" + @echo "... src/third_party/zlib/crc32.o" + @echo "... src/third_party/zlib/crc32.i" + @echo "... src/third_party/zlib/crc32.s" + @echo "... src/third_party/zlib/deflate.o" + @echo "... src/third_party/zlib/deflate.i" + @echo "... src/third_party/zlib/deflate.s" + @echo "... src/third_party/zlib/gzclose.o" + @echo "... src/third_party/zlib/gzclose.i" + @echo "... src/third_party/zlib/gzclose.s" + @echo "... src/third_party/zlib/gzlib.o" + @echo "... src/third_party/zlib/gzlib.i" + @echo "... src/third_party/zlib/gzlib.s" + @echo "... src/third_party/zlib/gzread.o" + @echo "... src/third_party/zlib/gzread.i" + @echo "... src/third_party/zlib/gzread.s" + @echo "... src/third_party/zlib/gzwrite.o" + @echo "... src/third_party/zlib/gzwrite.i" + @echo "... src/third_party/zlib/gzwrite.s" + @echo "... src/third_party/zlib/infback.o" + @echo "... src/third_party/zlib/infback.i" + @echo "... src/third_party/zlib/infback.s" + @echo "... src/third_party/zlib/inffast.o" + @echo "... src/third_party/zlib/inffast.i" + @echo "... src/third_party/zlib/inffast.s" + @echo "... src/third_party/zlib/inflate.o" + @echo "... src/third_party/zlib/inflate.i" + @echo "... src/third_party/zlib/inflate.s" + @echo "... src/third_party/zlib/inftrees.o" + @echo "... src/third_party/zlib/inftrees.i" + @echo "... src/third_party/zlib/inftrees.s" + @echo "... src/third_party/zlib/simd_stub.o" + @echo "... src/third_party/zlib/simd_stub.i" + @echo "... src/third_party/zlib/simd_stub.s" + @echo "... src/third_party/zlib/trees.o" + @echo "... src/third_party/zlib/trees.i" + @echo "... src/third_party/zlib/trees.s" + @echo "... src/third_party/zlib/uncompr.o" + @echo "... src/third_party/zlib/uncompr.i" + @echo "... src/third_party/zlib/uncompr.s" + @echo "... src/third_party/zlib/zutil.o" + @echo "... src/third_party/zlib/zutil.i" + @echo "... src/third_party/zlib/zutil.s" + @echo "... src/url/url_constants.o" + @echo "... src/url/url_constants.i" + @echo "... src/url/url_constants.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/custom/net_util.h b/custom/net_util.h deleted file mode 100644 index ab7970c0..00000000 --- a/custom/net_util.h +++ /dev/null @@ -1,218 +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_NET_UTIL_H_ -#define NET_BASE_NET_UTIL_H_ - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#include -#elif defined(OS_POSIX) -#include -#include -#endif - -#include -#include - -#include "base/strings/string16.h" -#include "net/base/net_export.h" - -class GURL; - -namespace base { -class Time; -} - -namespace url { -struct CanonHostInfo; -struct Parsed; -} - -namespace net { - -class AddressList; - -// This is a "forward declaration" to avoid including ip_address_number.h -// Keep this in sync. -typedef std::vector IPAddressNumber; - -#if defined(OS_WIN) -// Bluetooth address size. Windows Bluetooth is supported via winsock. -static const size_t kBluetoothAddressSize = 6; -#endif - -#if 0 -// Splits an input of the form [":"] into its consitituent parts. -// Saves the result into |*host| and |*port|. If the input did not have -// the optional port, sets |*port| to -1. -// Returns true if the parsing was successful, false otherwise. -// The returned host is NOT canonicalized, and may be invalid. -// -// IPv6 literals must be specified in a bracketed form, for instance: -// [::1]:90 and [::1] -// -// The resultant |*host| in both cases will be "::1" (not bracketed). -NET_EXPORT bool ParseHostAndPort( - std::string::const_iterator host_and_port_begin, - std::string::const_iterator host_and_port_end, - std::string* host, - int* port); -NET_EXPORT bool ParseHostAndPort( - const std::string& host_and_port, - std::string* host, - int* port); -#endif - -#if 0 -// Returns a host:port string for the given URL. -NET_EXPORT std::string GetHostAndPort(const GURL& url); - -// Returns a host[:port] string for the given URL, where the port is omitted -// if it is the default for the URL's scheme. -NET_EXPORT_PRIVATE std::string GetHostAndOptionalPort(const GURL& url); -#endif - -// Returns true if |hostname| contains a non-registerable or non-assignable -// domain name (eg: a gTLD that has not been assigned by IANA) or an IP address -// that falls in an IANA-reserved range. -NET_EXPORT bool IsHostnameNonUnique(const std::string& hostname); - -// Convenience struct for when you need a |struct sockaddr|. -struct SockaddrStorage { - SockaddrStorage() : addr_len(sizeof(addr_storage)), - addr(reinterpret_cast(&addr_storage)) {} - SockaddrStorage(const SockaddrStorage& other); - void operator=(const SockaddrStorage& other); - - struct sockaddr_storage addr_storage; - socklen_t addr_len; - struct sockaddr* const addr; -}; - -// Extracts the IP address and port portions of a sockaddr. |port| is optional, -// and will not be filled in if NULL. -bool GetIPAddressFromSockAddr(const struct sockaddr* sock_addr, - socklen_t sock_addr_len, - const unsigned char** address, - size_t* address_len, - uint16_t* port); - -// Same as IPAddressToString() but for a sockaddr. This output will not include -// the IPv6 scope ID. -NET_EXPORT std::string NetAddressToString(const struct sockaddr* sa, - socklen_t sock_addr_len); - -// Same as IPAddressToStringWithPort() but for a sockaddr. This output will not -// include the IPv6 scope ID. -NET_EXPORT std::string NetAddressToStringWithPort(const struct sockaddr* sa, - socklen_t sock_addr_len); - -// Returns the hostname of the current system. Returns empty string on failure. -NET_EXPORT std::string GetHostName(); - -// Extracts the unescaped username/password from |url|, saving the results -// into |*username| and |*password|. -NET_EXPORT_PRIVATE void GetIdentityFromURL(const GURL& url, - base::string16* username, - base::string16* password); - -// Returns either the host from |url|, or, if the host is empty, the full spec. -NET_EXPORT std::string GetHostOrSpecFromURL(const GURL& url); - -#if 0 -// 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::CanonHostInfo* host_info); -#endif - -// 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. -// The rules are: -// * One or more components separated by '.' -// * Each component contains only alphanumeric characters and '-' or '_' -// * The last component begins with an alphanumeric character -// * Optional trailing dot after last component (means "treat as FQDN") -// -// NOTE: You should only pass in hosts that have been returned from -// CanonicalizeHost(), or you may not get accurate results. -NET_EXPORT bool IsCanonicalizedHostCompliant(const std::string& host); - -#if 0 -// Call these functions to get the html snippet for a directory listing. -// The return values of both functions are in UTF-8. -NET_EXPORT std::string GetDirectoryListingHeader(const base::string16& title); -#endif - -// Given the name of a file in a directory (ftp or local) and -// other information (is_dir, size, modification time), it returns -// the html snippet to add the entry for the file to the directory listing. -// Currently, it's a script tag containing a call to a Javascript function -// |addRow|. -// -// |name| is the file name to be displayed. |raw_bytes| will be used -// as the actual target of the link (so for example, ftp links should use -// server's encoding). If |raw_bytes| is an empty string, UTF-8 encoded |name| -// will be used. -// -// Both |name| and |raw_bytes| are escaped internally. -NET_EXPORT std::string GetDirectoryListingEntry(const base::string16& name, - const std::string& raw_bytes, - bool is_dir, - int64_t size, - base::Time modified); - -// Set socket to non-blocking mode -NET_EXPORT int SetNonBlocking(int fd); - -#if 0 -// Strip the portions of |url| that aren't core to the network request. -// - user name / password -// - reference section -NET_EXPORT_PRIVATE GURL SimplifyUrlForRequest(const GURL& url); -#endif - -// Returns true if it can determine that only loopback addresses are configured. -// i.e. if only 127.0.0.1 and ::1 are routable. -// Also returns false if it cannot determine this. -bool HaveOnlyLoopbackAddresses(); - -// Retuns the port field of the |sockaddr|. -const uint16_t* GetPortFieldFromSockaddr(const struct sockaddr* address, - socklen_t address_len); -// Returns the value of port in |sockaddr| (in host byte ordering). -NET_EXPORT_PRIVATE int GetPortFromSockaddr(const struct sockaddr* address, - socklen_t address_len); - -#if 0 -// Resolves a local hostname (such as "localhost" or "localhost6") into -// IP endpoints with the given port. Returns true if |host| is a local -// hostname and false otherwise. Special IPv6 names (e.g. "localhost6") -// will resolve to an IPv6 address only, whereas other names will -// resolve to both IPv4 and IPv6. -NET_EXPORT_PRIVATE bool ResolveLocalHostname(base::StringPiece host, - uint16_t port, - AddressList* address_list); -#endif - -#if 0 -// Returns true if |host| is one of the local hostnames -// (e.g. "localhost") or IP addresses (IPv4 127.0.0.0/8 or IPv6 ::1). -// -// Note that this function does not check for IP addresses other than -// the above, although other IP addresses may point to the local -// machine. -NET_EXPORT_PRIVATE bool IsLocalhost(base::StringPiece host); -#endif - -// Returns true if the url's host is a Google server. This should only be used -// for histograms and shouldn't be used to affect behavior. -NET_EXPORT_PRIVATE bool HasGoogleHost(const GURL& url); - -} // namespace net - -#endif // NET_BASE_NET_UTIL_H_ diff --git a/manage.py b/manage.py index 126ad366..089093a9 100755 --- a/manage.py +++ b/manage.py @@ -56,7 +56,7 @@ def main(): files = set() for autodep in deps.get("automatic_dependency", []): - tree = DependencyTree(chromium_root, autodep["exclude"], False) + tree = DependencyTree(chromium_root, autodep["exclude"], True) depmap = tree.get_dependencies(autodep["from"]) for node in depmap.keys(): diff --git a/patch/basepatch.patch b/patch/basepatch.patch deleted file mode 100644 index db009ba1..00000000 --- a/patch/basepatch.patch +++ /dev/null @@ -1,405 +0,0 @@ -diff --git a/src/base/debug/stack_trace.cc b/src/base/debug/stack_trace.cc -index 2250c8f..e9e6dd4 100644 ---- a/src/base/debug/stack_trace.cc -+++ b/src/base/debug/stack_trace.cc -@@ -14,29 +14,24 @@ - namespace base { - namespace debug { - -+StackTrace::StackTrace() { -+} -+ - StackTrace::StackTrace(const void* const* trace, size_t count) { -- count = std::min(count, arraysize(trace_)); -- if (count) -- memcpy(trace_, trace, count * sizeof(trace_[0])); -- count_ = count; - } - - StackTrace::~StackTrace() { - } - -+void StackTrace::OutputToStream(std::ostream* os) const { -+} -+ - const void *const *StackTrace::Addresses(size_t* count) const { -- *count = count_; -- if (count_) -- return trace_; -- return NULL; -+ return NULL; - } - - std::string StackTrace::ToString() const { -- std::stringstream stream; --#if !defined(__UCLIBC__) -- OutputToStream(&stream); --#endif -- return stream.str(); -+ return ""; - } - - } // namespace debug -diff --git a/src/base/rand_util_posix.cc b/src/base/rand_util_posix.cc -index 6a6e05a..7098399 100644 ---- a/src/base/rand_util_posix.cc -+++ b/src/base/rand_util_posix.cc -@@ -10,7 +10,6 @@ - #include - #include - --#include "base/files/file_util.h" - #include "base/lazy_instance.h" - #include "base/logging.h" - -@@ -47,6 +46,18 @@ uint64_t RandUint64() { - return number; - } - -+bool ReadFromFD(int fd, char* buffer, size_t bytes) { -+ size_t total_read = 0; -+ while (total_read < bytes) { -+ ssize_t bytes_read = -+ read(fd, buffer + total_read, bytes - total_read); -+ if (bytes_read <= 0) -+ break; -+ total_read += bytes_read; -+ } -+ return total_read == bytes; -+} -+ - void RandBytes(void* output, size_t output_length) { - const int urandom_fd = g_urandom_fd.Pointer()->fd(); - const bool success = -diff --git a/src/base/sequence_checker.h b/src/base/sequence_checker.h -index ad01828..dde18a9 100644 ---- a/src/base/sequence_checker.h -+++ b/src/base/sequence_checker.h -@@ -6,6 +6,7 @@ - #define BASE_SEQUENCE_CHECKER_H_ - - // See comments for the similar block in thread_checker.h. -+#if 0 - #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) - #define ENABLE_SEQUENCE_CHECKER 1 - #else -@@ -13,6 +14,9 @@ - #endif - - #include "base/sequence_checker_impl.h" -+#else -+#define ENABLE_SEQUENCE_CHECKER 0 -+#endif - - namespace base { - -diff --git a/src/base/threading/platform_thread_linux.cc b/src/base/threading/platform_thread_linux.cc -index 4057ede..17eda64 100644 ---- a/src/base/threading/platform_thread_linux.cc -+++ b/src/base/threading/platform_thread_linux.cc -@@ -12,7 +12,9 @@ - #include "base/logging.h" - #include "base/threading/platform_thread_internal_posix.h" - #include "base/threading/thread_id_name_manager.h" -+#if 0 - #include "base/tracked_objects.h" -+#endif - #include "build/build_config.h" - - #if !defined(OS_NACL) -@@ -79,7 +81,9 @@ bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) { - // static - void PlatformThread::SetName(const std::string& name) { - ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); -+#if 0 - tracked_objects::ThreadData::InitializeThreadContext(name); -+#endif - - #if !defined(OS_NACL) - // On linux we can get the thread names to show up in the debugger by setting -diff --git a/src/base/threading/platform_thread_mac.mm b/src/base/threading/platform_thread_mac.mm -index df11f85..27288e7 100644 ---- a/src/base/threading/platform_thread_mac.mm -+++ b/src/base/threading/platform_thread_mac.mm -@@ -17,7 +17,9 @@ - #include "base/logging.h" - #include "base/mac/mach_logging.h" - #include "base/threading/thread_id_name_manager.h" -+#if 0 - #include "base/tracked_objects.h" -+#endif - #include "build/build_config.h" - - namespace base { -@@ -46,7 +48,9 @@ void InitThreading() { - // static - void PlatformThread::SetName(const std::string& name) { - ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); -+#if 0 - tracked_objects::ThreadData::InitializeThreadContext(name); -+#endif - - // Mac OS X does not expose the length limit of the name, so - // hardcode it. -diff --git a/src/net/base/host_port_pair.cc b/src/net/base/host_port_pair.cc -index a0dd2d5..03083f3 100644 ---- a/src/net/base/host_port_pair.cc -+++ b/src/net/base/host_port_pair.cc -@@ -20,11 +20,13 @@ HostPortPair::HostPortPair(const std::string& in_host, uint16_t in_port) - : host_(in_host), port_(in_port) { - } - -+#if 0 - // static - HostPortPair HostPortPair::FromURL(const GURL& url) { - return HostPortPair(url.HostNoBrackets(), - static_cast(url.EffectiveIntPort())); - } -+#endif - - // static - HostPortPair HostPortPair::FromIPEndPoint(const IPEndPoint& ipe) { -diff --git a/src/net/base/ip_endpoint.cc b/src/net/base/ip_endpoint.cc -index 8fe2aed..5b355fe 100644 ---- a/src/net/base/ip_endpoint.cc -+++ b/src/net/base/ip_endpoint.cc -@@ -29,52 +29,6 @@ namespace { - const socklen_t kSockaddrInSize = sizeof(struct sockaddr_in); - const socklen_t kSockaddrIn6Size = sizeof(struct sockaddr_in6); - --// Extracts the address and port portions of a sockaddr. --bool GetIPAddressFromSockAddr(const struct sockaddr* sock_addr, -- socklen_t sock_addr_len, -- const uint8_t** address, -- size_t* address_len, -- uint16_t* port) { -- if (sock_addr->sa_family == AF_INET) { -- if (sock_addr_len < static_cast(sizeof(struct sockaddr_in))) -- return false; -- const struct sockaddr_in* addr = -- reinterpret_cast(sock_addr); -- *address = reinterpret_cast(&addr->sin_addr); -- *address_len = kIPv4AddressSize; -- if (port) -- *port = base::NetToHost16(addr->sin_port); -- return true; -- } -- -- if (sock_addr->sa_family == AF_INET6) { -- if (sock_addr_len < static_cast(sizeof(struct sockaddr_in6))) -- return false; -- const struct sockaddr_in6* addr = -- reinterpret_cast(sock_addr); -- *address = reinterpret_cast(&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(sizeof(SOCKADDR_BTH))) -- return false; -- const SOCKADDR_BTH* addr = reinterpret_cast(sock_addr); -- *address = reinterpret_cast(&addr->btAddr); -- *address_len = kBluetoothAddressSize; -- if (port) -- *port = static_cast(addr->port); -- return true; -- } --#endif -- -- return false; // Unrecognized |sa_family|. --} -- - } // namespace - - IPEndPoint::IPEndPoint() : port_(0) {} -diff --git a/src/net/base/net_errors.cc b/src/net/base/net_errors.cc -index ddba87b..6f07590 100644 ---- a/src/net/base/net_errors.cc -+++ b/src/net/base/net_errors.cc -@@ -52,6 +52,7 @@ bool IsClientCertificateError(int error) { - } - } - -+#if 0 - Error FileErrorToNetError(base::File::Error file_error) { - switch (file_error) { - case base::File::FILE_OK: -@@ -66,5 +67,6 @@ Error FileErrorToNetError(base::File::Error file_error) { - return ERR_FAILED; - } - } -+#endif - - } // namespace net -diff --git a/src/net/base/net_errors.h b/src/net/base/net_errors.h -index 27a2e51..97a716d 100644 ---- a/src/net/base/net_errors.h -+++ b/src/net/base/net_errors.h -@@ -8,7 +8,9 @@ - #include - #include - -+#if 0 - #include "base/files/file.h" -+#endif - #include "base/logging.h" - #include "net/base/net_export.h" - -@@ -47,8 +49,10 @@ NET_EXPORT bool IsClientCertificateError(int error); - // Map system error code to Error. - NET_EXPORT Error MapSystemError(logging::SystemErrorCode os_error); - -+#if 0 - // A convenient function to translate file error to net error code. - NET_EXPORT Error FileErrorToNetError(base::File::Error file_error); -+#endif - - } // namespace net - -diff --git a/src/net/quic/crypto/crypto_utils.cc b/src/net/quic/crypto/crypto_utils.cc -index 0c6a4a7..ec1c46b 100644 ---- a/src/net/quic/crypto/crypto_utils.cc -+++ b/src/net/quic/crypto/crypto_utils.cc -@@ -48,6 +48,7 @@ void CryptoUtils::GenerateNonce(QuicWallTime now, - kNonceSize - bytes_written); - } - -+#if 0 - // static - bool CryptoUtils::IsValidSNI(StringPiece sni) { - // TODO(rtenneti): Support RFC2396 hostname. -@@ -79,6 +80,29 @@ string CryptoUtils::NormalizeHostname(const char* hostname) { - } - return host; - } -+#else -+// TODO(hodduc): We should implement this ! -+// static -+bool CryptoUtils::IsValidSNI(StringPiece sni) { -+ return sni.find_last_of('.') != string::npos; -+} -+ -+string CryptoUtils::NormalizeHostname(const char* hostname) { -+ string host(hostname); -+ -+ // Walk backwards over the string, stopping at the first trailing dot. -+ size_t host_end = host.length(); -+ while (host_end != 0 && host[host_end - 1] == '.') { -+ host_end--; -+ } -+ -+ // Erase the trailing dots. -+ if (host_end != host.length()) { -+ host.erase(host_end, host.length() - host_end); -+ } -+ return host; -+} -+#endif - - // static - bool CryptoUtils::DeriveKeys(StringPiece premaster_secret, -diff --git a/src/net/quic/quic_server_id.cc b/src/net/quic/quic_server_id.cc -index b810bb3..77fe3a3 100644 ---- a/src/net/quic/quic_server_id.cc -+++ b/src/net/quic/quic_server_id.cc -@@ -41,6 +41,7 @@ bool QuicServerId::operator==(const QuicServerId& other) const { - host_port_pair_.Equals(other.host_port_pair_); - } - -+#if 0 - // static - QuicServerId QuicServerId::FromString(const std::string& str) { - GURL url(str); -@@ -50,6 +51,7 @@ QuicServerId QuicServerId::FromString(const std::string& str) { - ? PRIVACY_MODE_ENABLED - : PRIVACY_MODE_DISABLED); - } -+#endif - - string QuicServerId::ToString() const { - return "https://" + host_port_pair_.ToString() + -diff --git a/src/net/quic/quic_server_id.h b/src/net/quic/quic_server_id.h -index 97dbebb..fe219cf 100644 ---- a/src/net/quic/quic_server_id.h -+++ b/src/net/quic/quic_server_id.h -@@ -31,9 +31,11 @@ class NET_EXPORT_PRIVATE QuicServerId { - bool operator<(const QuicServerId& other) const; - bool operator==(const QuicServerId& other) const; - -+#if 0 - // Creates a QuicServerId from a string formatted in same manner as - // ToString(). - static QuicServerId FromString(const std::string& str); -+#endif - - // ToString() will convert the QuicServerId to "scheme:hostname:port" or - // "scheme:hostname:port/private". "scheme" will be "https". -diff --git a/src/net/quic/quic_session.cc b/src/net/quic/quic_session.cc -index ffb2043..814a3f3 100644 ---- a/src/net/quic/quic_session.cc -+++ b/src/net/quic/quic_session.cc -@@ -10,7 +10,9 @@ - #include "net/quic/quic_connection.h" - #include "net/quic/quic_flags.h" - #include "net/quic/quic_flow_controller.h" -+#if 0 - #include "net/ssl/ssl_info.h" -+#endif - - using base::StringPiece; - using base::hash_map; -diff --git a/src/net/spdy/spdy_framer.h b/src/net/spdy/spdy_framer.h -index 35bfafb..13b243f 100644 ---- a/src/net/spdy/spdy_framer.h -+++ b/src/net/spdy/spdy_framer.h -@@ -20,7 +20,9 @@ - #include "net/spdy/hpack/hpack_decoder.h" - #include "net/spdy/hpack/hpack_encoder.h" - #include "net/spdy/spdy_alt_svc_wire_format.h" -+#if 0 - #include "net/spdy/spdy_header_block.h" -+#endif - #include "net/spdy/spdy_protocol.h" - - typedef struct z_stream_s z_stream; // Forward declaration for zlib. -diff --git a/src/net/spdy/spdy_header_block.cc b/src/net/spdy/spdy_header_block.cc -index 49243a7..c312bed 100644 ---- a/src/net/spdy/spdy_header_block.cc -+++ b/src/net/spdy/spdy_header_block.cc -@@ -225,6 +225,7 @@ void SpdyHeaderBlock::AppendHeader(const StringPiece key, - block_.insert(make_pair(storage_->Write(key), storage_->Write(value))); - } - -+#if 0 - scoped_ptr SpdyHeaderBlockNetLogCallback( - const SpdyHeaderBlock* headers, - NetLogCaptureMode capture_mode) { -@@ -240,6 +241,7 @@ scoped_ptr SpdyHeaderBlockNetLogCallback( - dict->Set("headers", headers_dict); - return std::move(dict); - } -+#endif - - bool SpdyHeaderBlockFromNetLogParam( - const base::Value* event_param, -diff --git a/src/net/spdy/spdy_header_block.h b/src/net/spdy/spdy_header_block.h -index 2c57d41..686b65c 100644 ---- a/src/net/spdy/spdy_header_block.h -+++ b/src/net/spdy/spdy_header_block.h -@@ -126,10 +126,12 @@ class NET_EXPORT SpdyHeaderBlock { - scoped_ptr storage_; - }; - -+#if 0 - // Converts a SpdyHeaderBlock into NetLog event parameters. - NET_EXPORT scoped_ptr SpdyHeaderBlockNetLogCallback( - const SpdyHeaderBlock* headers, - NetLogCaptureMode capture_mode); -+#endif - - // Converts NetLog event parameters into a SPDY header block and writes them - // to |headers|. |event_param| must have been created by diff --git a/patch/disable_sequence_checker.patch b/patch/disable_sequence_checker.patch new file mode 100644 index 00000000..ac1a6894 --- /dev/null +++ b/patch/disable_sequence_checker.patch @@ -0,0 +1,22 @@ +diff --git a/src/base/sequence_checker.h b/src/base/sequence_checker.h +index ad01828..dde18a9 100644 +--- a/src/base/sequence_checker.h ++++ b/src/base/sequence_checker.h +@@ -6,6 +6,7 @@ + #define BASE_SEQUENCE_CHECKER_H_ + + // See comments for the similar block in thread_checker.h. ++#if 0 + #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) + #define ENABLE_SEQUENCE_CHECKER 1 + #else +@@ -13,6 +14,9 @@ + #endif + + #include "base/sequence_checker_impl.h" ++#else ++#define ENABLE_SEQUENCE_CHECKER 0 ++#endif + + namespace base { + diff --git a/patch/disable_stack_trace.patch b/patch/disable_stack_trace.patch new file mode 100644 index 00000000..d836507c --- /dev/null +++ b/patch/disable_stack_trace.patch @@ -0,0 +1,34 @@ +diff --git a/src/base/debug/stack_trace.cc b/src/base/debug/stack_trace.cc +index 2250c8f..af54a1d 100644 +--- a/src/base/debug/stack_trace.cc ++++ b/src/base/debug/stack_trace.cc +@@ -14,6 +14,8 @@ + namespace base { + namespace debug { + ++StackTrace::StackTrace() { } ++ + StackTrace::StackTrace(const void* const* trace, size_t count) { + count = std::min(count, arraysize(trace_)); + if (count) +@@ -24,6 +26,8 @@ StackTrace::StackTrace(const void* const* trace, size_t count) { + StackTrace::~StackTrace() { + } + ++void StackTrace::OutputToStream(std::ostream* os) const { } ++ + const void *const *StackTrace::Addresses(size_t* count) const { + *count = count_; + if (count_) +@@ -33,9 +37,11 @@ const void *const *StackTrace::Addresses(size_t* count) const { + + std::string StackTrace::ToString() const { + std::stringstream stream; ++#if 0 + #if !defined(__UCLIBC__) + OutputToStream(&stream); + #endif ++#endif + return stream.str(); + } + diff --git a/patch/net_errors_remove_file_error.patch b/patch/net_errors_remove_file_error.patch new file mode 100644 index 00000000..44e83a78 --- /dev/null +++ b/patch/net_errors_remove_file_error.patch @@ -0,0 +1,44 @@ +diff --git a/src/net/base/net_errors.cc b/src/net/base/net_errors.cc +index ddba87b..6f07590 100644 +--- a/src/net/base/net_errors.cc ++++ b/src/net/base/net_errors.cc +@@ -52,6 +52,7 @@ bool IsClientCertificateError(int error) { + } + } + ++#if 0 + Error FileErrorToNetError(base::File::Error file_error) { + switch (file_error) { + case base::File::FILE_OK: +@@ -66,5 +67,6 @@ Error FileErrorToNetError(base::File::Error file_error) { + return ERR_FAILED; + } + } ++#endif + + } // namespace net +diff --git a/src/net/base/net_errors.h b/src/net/base/net_errors.h +index 27a2e51..97a716d 100644 +--- a/src/net/base/net_errors.h ++++ b/src/net/base/net_errors.h +@@ -8,7 +8,9 @@ + #include + #include + ++#if 0 + #include "base/files/file.h" ++#endif + #include "base/logging.h" + #include "net/base/net_export.h" + +@@ -47,8 +49,10 @@ NET_EXPORT bool IsClientCertificateError(int error); + // Map system error code to Error. + NET_EXPORT Error MapSystemError(logging::SystemErrorCode os_error); + ++#if 0 + // A convenient function to translate file error to net error code. + NET_EXPORT Error FileErrorToNetError(base::File::Error file_error); ++#endif + + } // namespace net + diff --git a/patch/persistent_memory_allocator_disable_sharing.patch b/patch/persistent_memory_allocator_disable_sharing.patch new file mode 100644 index 00000000..0845bdfa --- /dev/null +++ b/patch/persistent_memory_allocator_disable_sharing.patch @@ -0,0 +1,33 @@ +diff --git a/src/base/metrics/persistent_memory_allocator.cc b/src/base/metrics/persistent_memory_allocator.cc +index a1a960c..e2ed44d 100644 +--- a/src/base/metrics/persistent_memory_allocator.cc ++++ b/src/base/metrics/persistent_memory_allocator.cc +@@ -7,9 +7,13 @@ + #include + #include + ++#if 0 + #include "base/files/memory_mapped_file.h" ++#endif + #include "base/logging.h" ++#if 0 + #include "base/memory/shared_memory.h" ++#endif + #include "base/metrics/histogram_macros.h" + + namespace { +@@ -666,6 +670,7 @@ LocalPersistentMemoryAllocator::~LocalPersistentMemoryAllocator() { + + + //----- SharedPersistentMemoryAllocator ---------------------------------------- ++#if 0 + + SharedPersistentMemoryAllocator::SharedPersistentMemoryAllocator( + scoped_ptr memory, +@@ -702,5 +707,6 @@ bool FilePersistentMemoryAllocator::IsFileAcceptable( + const MemoryMappedFile& file) { + return IsMemoryAcceptable(file.data(), file.length(), 0, true); + } ++#endif + + } // namespace base diff --git a/patch/pickle_remove_unused_include.patch b/patch/pickle_remove_unused_include.patch new file mode 100644 index 00000000..7fc5bfad --- /dev/null +++ b/patch/pickle_remove_unused_include.patch @@ -0,0 +1,16 @@ +diff --git a/src/base/pickle.h b/src/base/pickle.h +index eb4888a..37b1dd7 100644 +--- a/src/base/pickle.h ++++ b/src/base/pickle.h +@@ -18,9 +18,11 @@ + #include "base/strings/string16.h" + #include "base/strings/string_piece.h" + ++#if 0 + #if defined(OS_POSIX) + #include "base/files/file.h" + #endif ++#endif + + namespace base { + diff --git a/patch/platform_thread_remove_tracked_objects.patch b/patch/platform_thread_remove_tracked_objects.patch new file mode 100644 index 00000000..c18c1c24 --- /dev/null +++ b/patch/platform_thread_remove_tracked_objects.patch @@ -0,0 +1,72 @@ +diff --git a/src/base/threading/platform_thread_mac.mm b/src/base/threading/platform_thread_mac.mm +index 7ab47cb..53819bc 100644 +--- a/src/base/threading/platform_thread_mac.mm ++++ b/src/base/threading/platform_thread_mac.mm +@@ -17,7 +17,9 @@ + #include "base/logging.h" + #include "base/mac/mach_logging.h" + #include "base/threading/thread_id_name_manager.h" ++#if 0 + #include "base/tracked_objects.h" ++#endif + #include "build/build_config.h" + + namespace base { +@@ -46,7 +48,9 @@ void InitThreading() { + // static + void PlatformThread::SetName(const std::string& name) { + ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); ++#if 0 + tracked_objects::ThreadData::InitializeThreadContext(name); ++#endif + + // Mac OS X does not expose the length limit of the name, so + // hardcode it. +diff --git a/src/base/threading/platform_thread_linux.cc b/src/base/threading/platform_thread_linux.cc +index 95ed324..054de87 100644 +--- a/src/base/threading/platform_thread_linux.cc ++++ b/src/base/threading/platform_thread_linux.cc +@@ -12,7 +12,9 @@ + #include "base/logging.h" + #include "base/threading/platform_thread_internal_posix.h" + #include "base/threading/thread_id_name_manager.h" ++#if 0 + #include "base/tracked_objects.h" ++#endif + #include "build/build_config.h" + + #if !defined(OS_NACL) +@@ -68,7 +70,9 @@ bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) { + // static + void PlatformThread::SetName(const std::string& name) { + ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); ++#if 0 + tracked_objects::ThreadData::InitializeThreadContext(name); ++#endif + + #if !defined(OS_NACL) + // On linux we can get the thread names to show up in the debugger by setting +diff --git a/src/base/threading/platform_thread_freebsd.cc b/src/base/threading/platform_thread_freebsd.cc +index 44f14c8..bddb6ac 100644 +--- a/src/base/threading/platform_thread_freebsd.cc ++++ b/src/base/threading/platform_thread_freebsd.cc +@@ -12,7 +12,9 @@ + #include "base/logging.h" + #include "base/threading/platform_thread_internal_posix.h" + #include "base/threading/thread_id_name_manager.h" ++#if 0 + #include "base/tracked_objects.h" ++#endif + #include "build/build_config.h" + + #if !defined(OS_NACL) +@@ -67,7 +69,9 @@ bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) { + // static + void PlatformThread::SetName(const std::string& name) { + ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); ++#if 0 + tracked_objects::ThreadData::InitializeThreadContext(name); ++#endif + + #if !defined(OS_NACL) + // On FreeBSD we can get the thread names to show up in the debugger by diff --git a/patch/quic_session_remove_unused_include.patch b/patch/quic_session_remove_unused_include.patch new file mode 100644 index 00000000..8557c715 --- /dev/null +++ b/patch/quic_session_remove_unused_include.patch @@ -0,0 +1,14 @@ +diff --git a/src/net/quic/quic_session.cc b/src/net/quic/quic_session.cc +index e3aad3f..9887358 100644 +--- a/src/net/quic/quic_session.cc ++++ b/src/net/quic/quic_session.cc +@@ -12,7 +12,9 @@ + #include "net/quic/quic_connection.h" + #include "net/quic/quic_flags.h" + #include "net/quic/quic_flow_controller.h" ++#if 0 + #include "net/ssl/ssl_info.h" ++#endif + + using base::IntToString; + using base::StringPiece; diff --git a/patch/rand_util_posix.patch b/patch/rand_util_posix.patch new file mode 100644 index 00000000..593a51e8 --- /dev/null +++ b/patch/rand_util_posix.patch @@ -0,0 +1,35 @@ +diff --git a/src/base/rand_util_posix.cc b/src/base/rand_util_posix.cc +index 6a6e05a..9b4513a 100644 +--- a/src/base/rand_util_posix.cc ++++ b/src/base/rand_util_posix.cc +@@ -10,7 +10,11 @@ + #include + #include + ++#if 0 + #include "base/files/file_util.h" ++#else ++#include "base/posix/eintr_wrapper.h" ++#endif + #include "base/lazy_instance.h" + #include "base/logging.h" + +@@ -47,6 +51,18 @@ uint64_t RandUint64() { + return number; + } + ++bool ReadFromFD(int fd, char* buffer, size_t bytes) { ++ size_t total_read = 0; ++ while (total_read < bytes) { ++ ssize_t bytes_read = ++ HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); ++ if (bytes_read <= 0) ++ break; ++ total_read += bytes_read; ++ } ++ return total_read == bytes; ++} ++ + void RandBytes(void* output, size_t output_length) { + const int urandom_fd = g_urandom_fd.Pointer()->fd(); + const bool success = diff --git a/patch/remove_tracked_objects_h.patch b/patch/remove_tracked_objects_h.patch new file mode 100644 index 00000000..e5fee288 --- /dev/null +++ b/patch/remove_tracked_objects_h.patch @@ -0,0 +1,48 @@ +diff --git a/src/base/threading/platform_thread_linux.cc b/src/base/threading/platform_thread_linux.cc +index 95ed324..054de87 100644 +--- a/src/base/threading/platform_thread_linux.cc ++++ b/src/base/threading/platform_thread_linux.cc +@@ -12,7 +12,9 @@ + #include "base/logging.h" + #include "base/threading/platform_thread_internal_posix.h" + #include "base/threading/thread_id_name_manager.h" ++#if 0 + #include "base/tracked_objects.h" ++#endif + #include "build/build_config.h" + + #if !defined(OS_NACL) +@@ -68,7 +70,9 @@ bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) { + // static + void PlatformThread::SetName(const std::string& name) { + ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); ++#if 0 + tracked_objects::ThreadData::InitializeThreadContext(name); ++#endif + + #if !defined(OS_NACL) + // On linux we can get the thread names to show up in the debugger by setting +diff --git a/src/base/threading/platform_thread_mac.mm b/src/base/threading/platform_thread_mac.mm +index 7ab47cb..53819bc 100644 +--- a/src/base/threading/platform_thread_mac.mm ++++ b/src/base/threading/platform_thread_mac.mm +@@ -17,7 +17,9 @@ + #include "base/logging.h" + #include "base/mac/mach_logging.h" + #include "base/threading/thread_id_name_manager.h" ++#if 0 + #include "base/tracked_objects.h" ++#endif + #include "build/build_config.h" + + namespace base { +@@ -46,7 +48,9 @@ void InitThreading() { + // static + void PlatformThread::SetName(const std::string& name) { + ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); ++#if 0 + tracked_objects::ThreadData::InitializeThreadContext(name); ++#endif + + // Mac OS X does not expose the length limit of the name, so + // hardcode it. diff --git a/patch/spdy_header_block_remove_unused_callback.patch b/patch/spdy_header_block_remove_unused_callback.patch new file mode 100644 index 00000000..7b5628db --- /dev/null +++ b/patch/spdy_header_block_remove_unused_callback.patch @@ -0,0 +1,20 @@ +diff --git a/src/net/spdy/spdy_header_block.cc b/src/net/spdy/spdy_header_block.cc +index b24085f..c0765ee 100644 +--- a/src/net/spdy/spdy_header_block.cc ++++ b/src/net/spdy/spdy_header_block.cc +@@ -228,6 +228,7 @@ void SpdyHeaderBlock::AppendHeader(const StringPiece key, + block_.insert(make_pair(storage_->Write(key), storage_->Write(value))); + } + ++#if 0 + scoped_ptr SpdyHeaderBlockNetLogCallback( + const SpdyHeaderBlock* headers, + NetLogCaptureMode capture_mode) { +@@ -243,6 +244,7 @@ scoped_ptr SpdyHeaderBlockNetLogCallback( + dict->Set("headers", headers_dict); + return std::move(dict); + } ++#endif + + bool SpdyHeaderBlockFromNetLogParam( + const base::Value* event_param, diff --git a/patch/url_canon_host_disable_idn_host.patch b/patch/url_canon_host_disable_idn_host.patch new file mode 100644 index 00000000..5cc88971 --- /dev/null +++ b/patch/url_canon_host_disable_idn_host.patch @@ -0,0 +1,18 @@ +diff --git a/src/url/url_canon_host.cc b/src/url/url_canon_host.cc +index d4cdfd5..d7959c7 100644 +--- a/src/url/url_canon_host.cc ++++ b/src/url/url_canon_host.cc +@@ -174,9 +174,13 @@ bool DoIDNHost(const base::char16* src, int src_len, CanonOutput* output) { + DoSimpleHost(src, src_len, &url_escaped_host, &has_non_ascii); + + StackBufferW wide_output; ++#if 0 + if (!IDNToASCII(url_escaped_host.data(), + url_escaped_host.length(), + &wide_output)) { ++#else ++ if (true) { ++#endif + // Some error, give up. This will write some reasonable looking + // representation of the string to the output. + AppendInvalidNarrowString(src, 0, src_len, output); diff --git a/patch/url_util_remove_unused_function.patch b/patch/url_util_remove_unused_function.patch new file mode 100644 index 00000000..62643673 --- /dev/null +++ b/patch/url_util_remove_unused_function.patch @@ -0,0 +1,30 @@ +diff --git a/src/net/base/url_util.cc b/src/net/base/url_util.cc +index 86a6108..4decf0c 100644 +--- a/src/net/base/url_util.cc ++++ b/src/net/base/url_util.cc +@@ -17,7 +17,9 @@ + #include "base/strings/stringprintf.h" + #include "net/base/escape.h" + #include "net/base/ip_address_number.h" ++#if 0 + #include "net/base/registry_controlled_domains/registry_controlled_domain.h" ++#endif + #include "url/gurl.h" + #include "url/url_canon.h" + #include "url/url_canon_ip.h" +@@ -305,6 +307,7 @@ bool IsCanonicalizedHostCompliant(const std::string& host) { + return most_recent_component_started_alphanumeric; + } + ++#if 0 + 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 ? +@@ -350,6 +353,7 @@ bool IsHostnameNonUnique(const std::string& hostname) { + registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, + registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); + } ++#endif + + bool IsLocalhost(base::StringPiece host) { + if (IsLocalHostname(host, nullptr)) diff --git a/src/AUTHORS b/src/AUTHORS index 4a798992..7311f186 100644 --- a/src/AUTHORS +++ b/src/AUTHORS @@ -34,6 +34,7 @@ Alexander Sulfrian Alexandre Abreu Alexandru Chiculita Alexey Korepanov +Alexis Brenon Alexis Menard Alfredo Hernandez Ali Vathi @@ -97,6 +98,7 @@ Caio Marcelo de Oliveira Filho Caitlin Potter Catalin Badea Cem Kocagil +Chakshu Ahuja Chamal De Silva Chandra Shekar Vallala Chang Shu @@ -197,6 +199,7 @@ Gao Chun Gao Chun George Liaskos Georgy Buranov +Gergely Nagy Gitanshu Mehndiratta Giuseppe Iuculano Glenn Adams @@ -269,6 +272,7 @@ Jeado Ko Jeongeun Kim Jeongmin Kim Jeremy Spiegel +Jesper Storm Bache Jesse Miller Jesus Sanchez-Palencia Jiajia Qin @@ -307,11 +311,13 @@ Juhui Lee Julien Brianceau Julien Isorce Julien Racle +Jun Fang Jun Jiang Junchao Han JungJik Lee Jungkee Song Junmin Zhu +Kai Jiang Kal Conley Kalyan Kondapally Kamil Jiwa @@ -371,6 +377,7 @@ Manuel Braun Mao Yujie Mao Yujie Marco Rodrigues +Mario Pistrich Mario Sanchez Prada Mariusz Mlynski Mark Hahnenberg @@ -403,6 +410,7 @@ Mihai Tica Mihai Tica Mike Tilburg Mikhail Pozdnyakov +Milko Leporis Milton Chiang Mingmin Xie Minsoo Max Koo @@ -428,6 +436,7 @@ Nikhil Bansal Nikita Ofitserov Ningxin Hu Nitish Mehrotra +Noj Vek Nolan Cao Pan Deng Parag Radke @@ -488,6 +497,7 @@ Renata Hodovan Rene Bolldorf Rene Ladan Rijubrata Bhaumik +Riku Voipio Rob Buis Rob Wu Robert Bear Travis @@ -619,9 +629,11 @@ Will Hirsch Will Shackleton William Xie Xiang Long +Xiangze Zhang Xiaolei Yu Xinchao He Xing Zhang +Xinghua Cao Xu Samuel Xuefei Ren Xun Sun @@ -639,6 +651,7 @@ Yongsheng Zhu Yoshinori Sano Youngho Seo YoungKi Hong +Youngmin Yoo Youngsoo Choi Youngsun Suh Yumikiyo Osanai @@ -680,6 +693,7 @@ Hewlett-Packard Development Company, L.P. <*@hp.com> Igalia S.L. <*@igalia.com> NVIDIA Corporation <*@nvidia.com> Opera Software ASA <*@opera.com> +Spotify AB <*@spotify.com> TeamSpeak Systems GmbH <*@teamspeak.com> The Chromium Authors <*@chromium.org> The MathWorks, Inc. diff --git a/src/base/at_exit.cc b/src/base/at_exit.cc index 0fba3556..f9aa2d10 100644 --- a/src/base/at_exit.cc +++ b/src/base/at_exit.cc @@ -21,7 +21,8 @@ namespace base { // this for thread-safe access, since it will only be modified in testing. static AtExitManager* g_top_manager = NULL; -AtExitManager::AtExitManager() : next_manager_(g_top_manager) { +AtExitManager::AtExitManager() + : processing_callbacks_(false), next_manager_(g_top_manager) { // If multiple modules instantiate AtExitManagers they'll end up living in this // module... they have to coexist. #if !defined(COMPONENT_BUILD) @@ -55,6 +56,7 @@ void AtExitManager::RegisterTask(base::Closure task) { } AutoLock lock(g_top_manager->lock_); + DCHECK(!g_top_manager->processing_callbacks_); g_top_manager->stack_.push(task); } @@ -65,16 +67,28 @@ void AtExitManager::ProcessCallbacksNow() { return; } - AutoLock lock(g_top_manager->lock_); + // Callbacks may try to add new callbacks, so run them without holding + // |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but + // handle it gracefully in release builds so we don't deadlock. + std::stack tasks; + { + AutoLock lock(g_top_manager->lock_); + tasks.swap(g_top_manager->stack_); + g_top_manager->processing_callbacks_ = true; + } - while (!g_top_manager->stack_.empty()) { - base::Closure task = g_top_manager->stack_.top(); + while (!tasks.empty()) { + base::Closure task = tasks.top(); task.Run(); - g_top_manager->stack_.pop(); + tasks.pop(); } + + // Expect that all callbacks have been run. + DCHECK(g_top_manager->stack_.empty()); } -AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) { +AtExitManager::AtExitManager(bool shadow) + : processing_callbacks_(false), next_manager_(g_top_manager) { DCHECK(shadow || !g_top_manager); g_top_manager = this; } diff --git a/src/base/at_exit.h b/src/base/at_exit.h index 04e3f764..02e18ed9 100644 --- a/src/base/at_exit.h +++ b/src/base/at_exit.h @@ -59,6 +59,7 @@ class BASE_EXPORT AtExitManager { private: base::Lock lock_; std::stack stack_; + bool processing_callbacks_; AtExitManager* next_manager_; // Stack of managers to allow shadowing. DISALLOW_COPY_AND_ASSIGN(AtExitManager); diff --git a/src/base/bind.h b/src/base/bind.h index 770e4570..46dbb913 100644 --- a/src/base/bind.h +++ b/src/base/bind.h @@ -6,7 +6,6 @@ #define BASE_BIND_H_ #include "base/bind_internal.h" -#include "base/callback_internal.h" // ----------------------------------------------------------------------------- // Usage documentation @@ -47,14 +46,27 @@ namespace base { +namespace internal { + +// Don't use Alias Template directly here to avoid a compile error on MSVC2013. +template +struct MakeUnboundRunTypeImpl { + using Type = + typename BindState< + typename FunctorTraits::RunnableType, + typename FunctorTraits::RunType, + Args...>::UnboundRunType; +}; + +} // namespace internal + +template +using MakeUnboundRunType = + typename internal::MakeUnboundRunTypeImpl::Type; + template -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - typename internal::CallbackParamTraits::StorageType...> - ::UnboundRunType> -Bind(Functor functor, const Args&... args) { +base::Callback> +Bind(Functor functor, Args&&... args) { // Type aliases for how to store and run the functor. using RunnableType = typename internal::FunctorTraits::RunnableType; using RunType = typename internal::FunctorTraits::RunType; @@ -88,12 +100,11 @@ Bind(Functor functor, const Args&... args) { !internal::HasRefCountedParamAsRawPtr::value, "a parameter is a refcounted type and needs scoped_refptr"); - using BindState = internal::BindState< - RunnableType, RunType, - typename internal::CallbackParamTraits::StorageType...>; + using BindState = internal::BindState; return Callback( - new BindState(internal::MakeRunnable(functor), args...)); + new BindState(internal::MakeRunnable(functor), + std::forward(args)...)); } } // namespace base diff --git a/src/base/bind_helpers.h b/src/base/bind_helpers.h index 2add755b..660348e6 100644 --- a/src/base/bind_helpers.h +++ b/src/base/bind_helpers.h @@ -378,7 +378,7 @@ class PassedWrapper { : is_valid_(true), scoper_(std::move(scoper)) {} PassedWrapper(const PassedWrapper& other) : is_valid_(other.is_valid_), scoper_(std::move(other.scoper_)) {} - T Pass() const { + T Take() const { CHECK(is_valid_); is_valid_ = false; return std::move(scoper_); @@ -391,98 +391,39 @@ class PassedWrapper { // Unwrap the stored parameters for the wrappers above. template -struct UnwrapTraits { - using ForwardType = const T&; - static ForwardType Unwrap(const T& o) { return o; } -}; +const T& Unwrap(const T& o) { + return o; +} template -struct UnwrapTraits > { - using ForwardType = T*; - static ForwardType Unwrap(UnretainedWrapper unretained) { - return unretained.get(); - } -}; +T* Unwrap(UnretainedWrapper unretained) { + return unretained.get(); +} template -struct UnwrapTraits > { - using ForwardType = const T&; - static ForwardType Unwrap(ConstRefWrapper const_ref) { - return const_ref.get(); - } -}; +const T& Unwrap(ConstRefWrapper const_ref) { + return const_ref.get(); +} template -struct UnwrapTraits > { - using ForwardType = T*; - static ForwardType Unwrap(const scoped_refptr& o) { return o.get(); } -}; +T* Unwrap(const scoped_refptr& o) { + return o.get(); +} template -struct UnwrapTraits > { - using ForwardType = const WeakPtr&; - static ForwardType Unwrap(const WeakPtr& o) { return o; } -}; +const WeakPtr& Unwrap(const WeakPtr& o) { + return o; +} template -struct UnwrapTraits > { - using ForwardType = T*; - static ForwardType Unwrap(const OwnedWrapper& o) { - return o.get(); - } -}; +T* Unwrap(const OwnedWrapper& o) { + return o.get(); +} template -struct UnwrapTraits > { - using ForwardType = T; - static T Unwrap(PassedWrapper& o) { - return o.Pass(); - } -}; - -// Utility for handling different refcounting semantics in the Bind() -// function. -template -struct MaybeScopedRefPtr; - -template -struct MaybeScopedRefPtr { - MaybeScopedRefPtr() {} -}; - -template -struct MaybeScopedRefPtr { - MaybeScopedRefPtr(const T&, const Rest&...) {} -}; - -template -struct MaybeScopedRefPtr { - MaybeScopedRefPtr(const T*, const Rest&...) {} -}; - -template -struct MaybeScopedRefPtr { - MaybeScopedRefPtr(const T& o, const Rest&...) {} -}; - -template -struct MaybeScopedRefPtr { - MaybeScopedRefPtr(T* o, const Rest&...) : ref_(o) {} - scoped_refptr ref_; -}; - -// No need to additionally AddRef() and Release() since we are storing a -// scoped_refptr<> inside the storage object already. -template -struct MaybeScopedRefPtr, Rest...> { - MaybeScopedRefPtr(const scoped_refptr&, const Rest&...) {} -}; - -template -struct MaybeScopedRefPtr { - MaybeScopedRefPtr(const T* o, const Rest&...) : ref_(o) {} - scoped_refptr ref_; -}; +T Unwrap(PassedWrapper& o) { + return o.Take(); +} // IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a // method. It is used internally by Bind() to select the correct diff --git a/src/base/bind_internal.h b/src/base/bind_internal.h index ac7cd009..f296bbcc 100644 --- a/src/base/bind_internal.h +++ b/src/base/bind_internal.h @@ -104,7 +104,8 @@ template struct BindsArrayToFirstArg : false_type {}; template -struct BindsArrayToFirstArg : is_array {}; +struct BindsArrayToFirstArg + : is_array::type> {}; // HasRefCountedParamAsRawPtr is the same to HasRefCountedTypeAsRawPtr except // when |is_method| is true HasRefCountedParamAsRawPtr skips the first argument. @@ -153,8 +154,9 @@ class RunnableAdapter { : function_(function) { } - R Run(typename CallbackParamTraits::ForwardType... args) { - return function_(CallbackForward(args)...); + template + R Run(RunArgs&&... args) { + return function_(std::forward(args)...); } private: @@ -174,8 +176,9 @@ class RunnableAdapter { : method_(method) { } - R Run(T* object, typename CallbackParamTraits::ForwardType... args) { - return (object->*method_)(CallbackForward(args)...); + template + R Run(T* object, RunArgs&&... args) { + return (object->*method_)(std::forward(args)...); } private: @@ -193,9 +196,9 @@ class RunnableAdapter { : method_(method) { } - R Run(const T* object, - typename CallbackParamTraits::ForwardType... args) { - return (object->*method_)(CallbackForward(args)...); + template + R Run(const T* object, RunArgs&&... args) { + return (object->*method_)(std::forward(args)...); } private: @@ -280,38 +283,42 @@ MakeRunnable(const Callback& t) { // // WeakCalls similarly need special syntax that is applied to the first // argument to check if they should no-op themselves. -template +template struct InvokeHelper; -template -struct InvokeHelper> { - static ReturnType MakeItSo(Runnable runnable, Args... args) { - return runnable.Run(CallbackForward(args)...); +template +struct InvokeHelper { + template + static ReturnType MakeItSo(Runnable runnable, RunArgs&&... args) { + return runnable.Run(std::forward(args)...); } }; -template -struct InvokeHelper> { - static void MakeItSo(Runnable runnable, Args... args) { - runnable.Run(CallbackForward(args)...); +template +struct InvokeHelper { + template + static void MakeItSo(Runnable runnable, RunArgs&&... args) { + runnable.Run(std::forward(args)...); } }; -template -struct InvokeHelper> { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, Args... args) { +template +struct InvokeHelper { + template + static void MakeItSo(Runnable runnable, + BoundWeakPtr weak_ptr, + RunArgs&&... args) { if (!weak_ptr.get()) { return; } - runnable.Run(weak_ptr.get(), CallbackForward(args)...); + runnable.Run(weak_ptr.get(), std::forward(args)...); } }; #if !defined(_MSC_VER) -template -struct InvokeHelper { +template +struct InvokeHelper { // WeakCalls are only supported for functions with a void return type. // Otherwise, the function result would be undefined if the the WeakPtr<> // is invalidated. @@ -324,19 +331,16 @@ struct InvokeHelper { // Invoker<> // // See description at the top of the file. -template struct Invoker; template -struct Invoker, - StorageType, TypeList, +struct Invoker, StorageType, InvokeHelperType, R(UnboundForwardArgs...)> { static R Run(BindStateBase* base, UnboundForwardArgs... unbound_args) { @@ -346,11 +350,29 @@ struct Invoker, // InvokeHelper<>::MakeItSo() call below. return InvokeHelperType::MakeItSo( storage->runnable_, - Unwrappers::Unwrap(get(storage->bound_args_))..., + Unwrap(get(storage->bound_args_))..., CallbackForward(unbound_args)...); } }; +// Used to implement MakeArgsStorage. +template +struct MakeArgsStorageImpl { + using Type = std::tuple; +}; + +template +struct MakeArgsStorageImpl { + using Type = std::tuple, BoundArgs...>; +}; + +// Constructs a tuple type to store BoundArgs into BindState. +// This wraps the first argument into a scoped_refptr if |is_method| is true and +// the first argument is a raw pointer. +// Other arguments are adjusted for store and packed into a tuple. +template +using MakeArgsStorage = typename MakeArgsStorageImpl< + is_method, typename std::decay::type...>::Type; // BindState<> // @@ -376,40 +398,35 @@ struct BindState final using StorageType = BindState; using RunnableType = Runnable; + enum { is_method = HasIsMethodTag::value }; + // true_type if Runnable is a method invocation and the first bound argument // is a WeakPtr. using IsWeakCall = - IsWeakMethod::value, BoundArgs...>; + IsWeakMethod::type...>; using BoundIndices = MakeIndexSequence; - using Unwrappers = TypeList...>; using UnboundForwardArgs = DropTypeListItem< sizeof...(BoundArgs), TypeList::ForwardType...>>; using UnboundForwardRunType = MakeFunctionType; - - using InvokeHelperArgs = ConcatTypeLists< - TypeList::ForwardType...>, - UnboundForwardArgs>; - using InvokeHelperType = - InvokeHelper; + using InvokeHelperType = InvokeHelper; using UnboundArgs = DropTypeListItem>; public: - using InvokerType = Invoker; using UnboundRunType = MakeFunctionType; - BindState(const Runnable& runnable, const BoundArgs&... bound_args) + template + BindState(const Runnable& runnable, ForwardArgs&&... bound_args) : BindStateBase(&Destroy), runnable_(runnable), - ref_(bound_args...), - bound_args_(bound_args...) {} + bound_args_(std::forward(bound_args)...) {} RunnableType runnable_; - MaybeScopedRefPtr::value, BoundArgs...> ref_; - Tuple bound_args_; + MakeArgsStorage bound_args_; private: ~BindState() {} diff --git a/src/base/callback_internal.h b/src/base/callback_internal.h index d1d8ab8e..3e8ee82e 100644 --- a/src/base/callback_internal.h +++ b/src/base/callback_internal.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "base/atomic_ref_count.h" #include "base/base_export.h" @@ -115,8 +116,14 @@ template struct IsMoveOnlyType { // Specialization of IsMoveOnlyType so that std::unique_ptr is still considered // move-only, even without the sentinel member. -template -struct IsMoveOnlyType> : std::true_type {}; +template +struct IsMoveOnlyType> : std::true_type {}; + +// Specialization of std::vector, so that it's considered move-only if the +// element type is move-only. Allocator is explicitly ignored when determining +// move-only status of the std::vector. +template +struct IsMoveOnlyType> : IsMoveOnlyType {}; template struct CallbackParamTraitsForMoveOnlyType; @@ -129,16 +136,7 @@ struct CallbackParamTraitsForNonMoveOnlyType; // http://connect.microsoft.com/VisualStudio/feedbackdetail/view/957801/compilation-error-with-variadic-templates // // This is a typetraits object that's used to take an argument type, and -// extract a suitable type for storing and forwarding arguments. -// -// In particular, it strips off references, and converts arrays to -// pointers for storage; and it avoids accidentally trying to create a -// "reference of a reference" if the argument is a reference type. -// -// This array type becomes an issue for storage because we are passing bound -// parameters by const reference. In this case, we end up passing an actual -// array type in the initializer list which C++ does not allow. This will -// break passing of C-string literals. +// extract a suitable type for forwarding arguments. template struct CallbackParamTraits : std::conditional::value, @@ -149,18 +147,6 @@ struct CallbackParamTraits template struct CallbackParamTraitsForNonMoveOnlyType { using ForwardType = const T&; - using StorageType = T; -}; - -// The Storage should almost be impossible to trigger unless someone manually -// specifies type of the bind parameters. However, in case they do, -// this will guard against us accidentally storing a reference parameter. -// -// The ForwardType should only be used for unbound arguments. -template -struct CallbackParamTraitsForNonMoveOnlyType { - using ForwardType = T&; - using StorageType = T; }; // Note that for array types, we implicitly add a const in the conversion. This @@ -171,14 +157,12 @@ struct CallbackParamTraitsForNonMoveOnlyType { template struct CallbackParamTraitsForNonMoveOnlyType { using ForwardType = const T*; - using StorageType = const T*; }; // See comment for CallbackParamTraits. template struct CallbackParamTraitsForNonMoveOnlyType { using ForwardType = const T*; - using StorageType = const T*; }; // Parameter traits for movable-but-not-copyable scopers. @@ -197,7 +181,6 @@ struct CallbackParamTraitsForNonMoveOnlyType { template struct CallbackParamTraitsForMoveOnlyType { using ForwardType = T; - using StorageType = T; }; // CallbackForward() is a very limited simulation of C++11's std::forward() diff --git a/src/base/containers/hash_tables.h b/src/base/containers/hash_tables.h index c421dddf..8da7b672 100644 --- a/src/base/containers/hash_tables.h +++ b/src/base/containers/hash_tables.h @@ -1,281 +1,75 @@ // 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. -// - -// -// Deal with the differences between Microsoft and GNU implemenations -// of hash_map. Allows all platforms to use |base::hash_map| and -// |base::hash_set|. -// eg: -// base::hash_map my_map; -// base::hash_set my_set; -// -// NOTE: It is an explicit non-goal of this class to provide a generic hash -// function for pointers. If you want to hash a pointers to a particular class, -// please define the template specialization elsewhere (for example, in its -// header file) and keep it specific to just pointers to that class. This is -// because identity hashes are not desirable for all types that might show up -// in containers as pointers. #ifndef BASE_CONTAINERS_HASH_TABLES_H_ #define BASE_CONTAINERS_HASH_TABLES_H_ -#include -#include - -#include - -#include "base/strings/string16.h" -#include "build/build_config.h" - -#if defined(COMPILER_MSVC) +#include #include #include +#include -#define BASE_HASH_NAMESPACE std +#include "base/hash.h" -#elif defined(COMPILER_GCC) +// This header file is deprecated. Use the corresponding C++11 type +// instead. https://crbug.com/576864 +// Use a custom hasher instead. #define BASE_HASH_NAMESPACE base_hash -// This is a hack to disable the gcc 4.4 warning about hash_map and hash_set -// being deprecated. We can get rid of this when we upgrade to VS2008 and we -// can use and . -#ifdef __DEPRECATED -#define CHROME_OLD__DEPRECATED __DEPRECATED -#undef __DEPRECATED -#endif - -#include -#include -#define BASE_HASH_IMPL_NAMESPACE __gnu_cxx - -#include - -#ifdef CHROME_OLD__DEPRECATED -#define __DEPRECATED CHROME_OLD__DEPRECATED -#undef CHROME_OLD__DEPRECATED -#endif - namespace BASE_HASH_NAMESPACE { -// The pre-standard hash behaves like C++11's std::hash, except around pointers. -// const char* is specialized to hash the C string and hash functions for -// general T* are missing. Define a BASE_HASH_NAMESPACE::hash which aligns with -// the C++11 behavior. - +// A separate hasher which, by default, forwards to std::hash. This is so legacy +// uses of BASE_HASH_NAMESPACE with base::hash_map do not interfere with +// std::hash mid-transition. template struct hash { - std::size_t operator()(const T& value) const { - return BASE_HASH_IMPL_NAMESPACE::hash()(value); - } + std::size_t operator()(const T& value) const { return std::hash()(value); } }; -template -struct hash { - std::size_t operator()(T* value) const { - return BASE_HASH_IMPL_NAMESPACE::hash()( - reinterpret_cast(value)); +// Use base::IntPairHash from base/hash.h as a custom hasher instead. +template +struct hash> { + std::size_t operator()(std::pair value) const { + return base::HashInts(value.first, value.second); } }; -// The GNU C++ library provides identity hash functions for many integral types, -// but not for |long long|. This hash function will truncate if |size_t| is -// narrower than |long long|. This is probably good enough for what we will -// use it for. - -#define DEFINE_TRIVIAL_HASH(integral_type) \ - template<> \ - struct hash { \ - std::size_t operator()(integral_type value) const { \ - return static_cast(value); \ - } \ - } - -DEFINE_TRIVIAL_HASH(long long); -DEFINE_TRIVIAL_HASH(unsigned long long); - -#undef DEFINE_TRIVIAL_HASH - -// Implement string hash functions so that strings of various flavors can -// be used as keys in STL maps and sets. The hash algorithm comes from the -// GNU C++ library, in . It is duplicated here because GCC -// versions prior to 4.3.2 are unable to compile when RTTI -// is disabled, as it is in our build. - -#define DEFINE_STRING_HASH(string_type) \ - template<> \ - struct hash { \ - std::size_t operator()(const string_type& s) const { \ - std::size_t result = 0; \ - for (string_type::const_iterator i = s.begin(); i != s.end(); ++i) \ - result = (result * 131) + *i; \ - return result; \ - } \ - } - -DEFINE_STRING_HASH(std::string); -DEFINE_STRING_HASH(base::string16); - -#undef DEFINE_STRING_HASH - } // namespace BASE_HASH_NAMESPACE -#else // COMPILER -#error define BASE_HASH_NAMESPACE for your compiler -#endif // COMPILER - namespace base { -// On MSVC, use the C++11 containers. -#if defined(COMPILER_MSVC) - -template, - class Pred = std::equal_to, - class Alloc = std::allocator>> +// Use std::unordered_map instead. +template , + class Pred = std::equal_to, + class Alloc = std::allocator>> using hash_map = std::unordered_map; -template, - class Pred = std::equal_to, - class Alloc = std::allocator>> +// Use std::unordered_multimap instead. +template , + class Pred = std::equal_to, + class Alloc = std::allocator>> using hash_multimap = std::unordered_multimap; -template, - class Pred = std::equal_to, - class Alloc = std::allocator> +// Use std::unordered_multiset instead. +template , + class Pred = std::equal_to, + class Alloc = std::allocator> using hash_multiset = std::unordered_multiset; -template, - class Pred = std::equal_to, - class Alloc = std::allocator> +// Use std::unordered_set instead. +template , + class Pred = std::equal_to, + class Alloc = std::allocator> using hash_set = std::unordered_set; -#else // !COMPILER_MSVC - -// Otherwise, use the pre-standard ones, but override the default hash to match -// C++11. -template, - class Pred = std::equal_to, - class Alloc = std::allocator>> -using hash_map = BASE_HASH_IMPL_NAMESPACE::hash_map; - -template, - class Pred = std::equal_to, - class Alloc = std::allocator>> -using hash_multimap = - BASE_HASH_IMPL_NAMESPACE::hash_multimap; - -template, - class Pred = std::equal_to, - class Alloc = std::allocator> -using hash_multiset = - BASE_HASH_IMPL_NAMESPACE::hash_multiset; - -template, - class Pred = std::equal_to, - class Alloc = std::allocator> -using hash_set = BASE_HASH_IMPL_NAMESPACE::hash_set; - -#undef BASE_HASH_IMPL_NAMESPACE - -#endif // COMPILER_MSVC - -// Implement hashing for pairs of at-most 32 bit integer values. -// When size_t is 32 bits, we turn the 64-bit hash code into 32 bits by using -// multiply-add hashing. This algorithm, as described in -// Theorem 4.3.3 of the thesis "Ãœber die Komplexität der Multiplikation in -// eingeschränkten Branchingprogrammmodellen" by Woelfel, is: -// -// h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32 -// -// Contact danakj@chromium.org for any questions. -inline std::size_t HashInts32(uint32_t value1, uint32_t value2) { - uint64_t value1_64 = value1; - uint64_t hash64 = (value1_64 << 32) | value2; - - if (sizeof(std::size_t) >= sizeof(uint64_t)) - return static_cast(hash64); - - uint64_t odd_random = 481046412LL << 32 | 1025306955LL; - uint32_t shift_random = 10121U << 16; - - hash64 = hash64 * odd_random + shift_random; - std::size_t high_bits = static_cast( - hash64 >> (8 * (sizeof(uint64_t) - sizeof(std::size_t)))); - return high_bits; -} - -// Implement hashing for pairs of up-to 64-bit integer values. -// We use the compound integer hash method to produce a 64-bit hash code, by -// breaking the two 64-bit inputs into 4 32-bit values: -// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000 -// Then we reduce our result to 32 bits if required, similar to above. -inline std::size_t HashInts64(uint64_t value1, uint64_t value2) { - uint32_t short_random1 = 842304669U; - uint32_t short_random2 = 619063811U; - uint32_t short_random3 = 937041849U; - uint32_t short_random4 = 3309708029U; - - uint32_t value1a = static_cast(value1 & 0xffffffff); - uint32_t value1b = static_cast((value1 >> 32) & 0xffffffff); - uint32_t value2a = static_cast(value2 & 0xffffffff); - uint32_t value2b = static_cast((value2 >> 32) & 0xffffffff); - - uint64_t product1 = static_cast(value1a) * short_random1; - uint64_t product2 = static_cast(value1b) * short_random2; - uint64_t product3 = static_cast(value2a) * short_random3; - uint64_t product4 = static_cast(value2b) * short_random4; - - uint64_t hash64 = product1 + product2 + product3 + product4; - - if (sizeof(std::size_t) >= sizeof(uint64_t)) - return static_cast(hash64); - - uint64_t odd_random = 1578233944LL << 32 | 194370989LL; - uint32_t shift_random = 20591U << 16; - - hash64 = hash64 * odd_random + shift_random; - std::size_t high_bits = static_cast( - hash64 >> (8 * (sizeof(uint64_t) - sizeof(std::size_t)))); - return high_bits; -} - -template -inline std::size_t HashPair(T1 value1, T2 value2) { - // This condition is expected to be compile-time evaluated and optimised away - // in release builds. - if (sizeof(T1) > sizeof(uint32_t) || (sizeof(T2) > sizeof(uint32_t))) - return HashInts64(value1, value2); - - return HashInts32(value1, value2); -} - } // namespace base -namespace BASE_HASH_NAMESPACE { - -// Implement methods for hashing a pair of integers, so they can be used as -// keys in STL containers. - -template -struct hash > { - std::size_t operator()(std::pair value) const { - return base::HashPair(value.first, value.second); - } -}; - -} // namespace BASE_HASH_NAMESPACE - -#undef DEFINE_PAIR_HASH_FUNCTION_START -#undef DEFINE_PAIR_HASH_FUNCTION_END - #endif // BASE_CONTAINERS_HASH_TABLES_H_ diff --git a/src/base/debug/stack_trace.cc b/src/base/debug/stack_trace.cc index e9e6dd49..af54a1d0 100644 --- a/src/base/debug/stack_trace.cc +++ b/src/base/debug/stack_trace.cc @@ -14,24 +14,35 @@ namespace base { namespace debug { -StackTrace::StackTrace() { -} +StackTrace::StackTrace() { } StackTrace::StackTrace(const void* const* trace, size_t count) { + count = std::min(count, arraysize(trace_)); + if (count) + memcpy(trace_, trace, count * sizeof(trace_[0])); + count_ = count; } StackTrace::~StackTrace() { } -void StackTrace::OutputToStream(std::ostream* os) const { -} +void StackTrace::OutputToStream(std::ostream* os) const { } const void *const *StackTrace::Addresses(size_t* count) const { - return NULL; + *count = count_; + if (count_) + return trace_; + return NULL; } std::string StackTrace::ToString() const { - return ""; + std::stringstream stream; +#if 0 +#if !defined(__UCLIBC__) + OutputToStream(&stream); +#endif +#endif + return stream.str(); } } // namespace debug diff --git a/src/base/feature_list.h b/src/base/feature_list.h new file mode 100644 index 00000000..875d3b59 --- /dev/null +++ b/src/base/feature_list.h @@ -0,0 +1,238 @@ +// Copyright 2015 The Chromium 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 BASE_FEATURE_LIST_H_ +#define BASE_FEATURE_LIST_H_ + +#include +#include +#include + +#include "base/base_export.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "base/synchronization/lock.h" + +namespace base { + +class FieldTrial; + +// Specifies whether a given feature is enabled or disabled by default. +enum FeatureState { + FEATURE_DISABLED_BY_DEFAULT, + FEATURE_ENABLED_BY_DEFAULT, +}; + +// The Feature struct is used to define the default state for a feature. See +// comment below for more details. There must only ever be one struct instance +// for a given feature name - generally defined as a constant global variable or +// file static. +struct BASE_EXPORT Feature { + // The name of the feature. This should be unique to each feature and is used + // for enabling/disabling features via command line flags and experiments. + const char* const name; + + // The default state (i.e. enabled or disabled) for this feature. + const FeatureState default_state; +}; + +// The FeatureList class is used to determine whether a given feature is on or +// off. It provides an authoritative answer, taking into account command-line +// overrides and experimental control. +// +// The basic use case is for any feature that can be toggled (e.g. through +// command-line or an experiment) to have a defined Feature struct, e.g.: +// +// const base::Feature kMyGreatFeature { +// "MyGreatFeature", base::FEATURE_ENABLED_BY_DEFAULT +// }; +// +// Then, client code that wishes to query the state of the feature would check: +// +// if (base::FeatureList::IsEnabled(kMyGreatFeature)) { +// // Feature code goes here. +// } +// +// Behind the scenes, the above call would take into account any command-line +// flags to enable or disable the feature, any experiments that may control it +// and finally its default state (in that order of priority), to determine +// whether the feature is on. +// +// Features can be explicitly forced on or off by specifying a list of comma- +// separated feature names via the following command-line flags: +// +// --enable-features=Feature5,Feature7 +// --disable-features=Feature1,Feature2,Feature3 +// +// After initialization (which should be done single-threaded), the FeatureList +// API is thread safe. +// +// Note: This class is a singleton, but does not use base/memory/singleton.h in +// order to have control over its initialization sequence. Specifically, the +// intended use is to create an instance of this class and fully initialize it, +// before setting it as the singleton for a process, via SetInstance(). +class BASE_EXPORT FeatureList { + public: + FeatureList(); + ~FeatureList(); + + // Initializes feature overrides via command-line flags |enable_features| and + // |disable_features|, each of which is a comma-separated list of features to + // enable or disable, respectively. If a feature appears on both lists, then + // it will be disabled. If a list entry has the format "FeatureName SplitFeatureListString( + const std::string& input); + + // Initializes and sets a default instance of FeatureList if one has not yet + // already been set. No-op otherwise. + static void InitializeInstance(); + + // Returns the singleton instance of FeatureList. Will return null until an + // instance is registered via SetInstance(). + static FeatureList* GetInstance(); + + // Registers the given |instance| to be the singleton feature list for this + // process. This should only be called once and |instance| must not be null. + static void SetInstance(scoped_ptr instance); + + // Clears the previously-registered singleton instance for tests. + static void ClearInstanceForTesting(); + + private: + FRIEND_TEST_ALL_PREFIXES(FeatureListTest, CheckFeatureIdentity); + + struct OverrideEntry { + // The overridden enable (on/off) state of the feature. + const OverrideState overridden_state; + + // An optional associated field trial, which will be activated when the + // state of the feature is queried for the first time. Weak pointer to the + // FieldTrial object that is owned by the FieldTrialList singleton. + base::FieldTrial* field_trial; + + // Specifies whether the feature's state is overridden by |field_trial|. + // If it's not, and |field_trial| is not null, it means it is simply an + // associated field trial for reporting purposes (and |overridden_state| + // came from the command-line). + const bool overridden_by_field_trial; + + // TODO(asvitkine): Expand this as more support is added. + + // Constructs an OverrideEntry for the given |overridden_state|. If + // |field_trial| is not null, it implies that |overridden_state| comes from + // the trial, so |overridden_by_field_trial| will be set to true. + OverrideEntry(OverrideState overridden_state, FieldTrial* field_trial); + }; + + // Finalizes the initialization state of the FeatureList, so that no further + // overrides can be registered. This is called by SetInstance() on the + // singleton feature list that is being registered. + void FinalizeInitialization(); + + // Returns whether the given |feature| is enabled. This is invoked by the + // public FeatureList::IsEnabled() static function on the global singleton. + // Requires the FeatureList to have already been fully initialized. + bool IsFeatureEnabled(const Feature& feature); + + // For each feature name in comma-separated list of strings |feature_list|, + // registers an override with the specified |overridden_state|. Also, will + // associate an optional named field trial if the entry is of the format + // "FeatureName overrides_; + + // Locked map that keeps track of seen features, to ensure a single feature is + // only defined once. This verification is only done in builds with DCHECKs + // enabled. + Lock feature_identity_tracker_lock_; + std::map feature_identity_tracker_; + + // Whether this object has been fully initialized. This gets set to true as a + // result of FinalizeInitialization(). + bool initialized_; + + DISALLOW_COPY_AND_ASSIGN(FeatureList); +}; + +} // namespace base + +#endif // BASE_FEATURE_LIST_H_ diff --git a/src/base/files/file_path.cc b/src/base/files/file_path.cc index 63e93879..de431232 100644 --- a/src/base/files/file_path.cc +++ b/src/base/files/file_path.cc @@ -502,10 +502,10 @@ FilePath FilePath::Append(StringPieceType component) const { // Don't append a separator if the path is empty (indicating the current // directory) or if the path component is empty (indicating nothing to // append). - if (appended.length() > 0 && new_path.path_.length() > 0) { + if (!appended.empty() && !new_path.path_.empty()) { // Don't append a separator if the path still ends with a trailing // separator after stripping (indicating the root directory). - if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) { + if (!IsSeparator(new_path.path_.back())) { // Don't append a separator if the path is just a drive letter. if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { new_path.path_.append(1, kSeparators[0]); @@ -610,7 +610,7 @@ string16 FilePath::AsUTF16Unsafe() const { } // static -FilePath FilePath::FromUTF8Unsafe(const std::string& utf8) { +FilePath FilePath::FromUTF8Unsafe(StringPiece utf8) { #if defined(SYSTEM_NATIVE_UTF8) return FilePath(utf8); #else @@ -619,11 +619,11 @@ FilePath FilePath::FromUTF8Unsafe(const std::string& utf8) { } // static -FilePath FilePath::FromUTF16Unsafe(const string16& utf16) { +FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) { #if defined(SYSTEM_NATIVE_UTF8) return FilePath(UTF16ToUTF8(utf16)); #else - return FilePath(SysWideToNativeMB(UTF16ToWide(utf16))); + return FilePath(SysWideToNativeMB(UTF16ToWide(utf16.as_string()))); #endif } @@ -647,16 +647,24 @@ string16 FilePath::AsUTF16Unsafe() const { } // static -FilePath FilePath::FromUTF8Unsafe(const std::string& utf8) { +FilePath FilePath::FromUTF8Unsafe(StringPiece utf8) { return FilePath(UTF8ToWide(utf8)); } // static -FilePath FilePath::FromUTF16Unsafe(const string16& utf16) { +FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) { return FilePath(utf16); } #endif +void FilePath::GetSizeForPickle(PickleSizer* sizer) const { +#if defined(OS_WIN) + sizer->AddString16(path_); +#else + sizer->AddString(path_); +#endif +} + void FilePath::WriteToPickle(Pickle* pickle) const { #if defined(OS_WIN) pickle->WriteString16(path_); diff --git a/src/base/files/file_path.h b/src/base/files/file_path.h index 89e9cbfb..3234df7b 100644 --- a/src/base/files/file_path.h +++ b/src/base/files/file_path.h @@ -138,6 +138,7 @@ namespace base { class Pickle; class PickleIterator; +class PickleSizer; // An abstraction to isolate users from the differences between native // pathnames on different platforms. @@ -372,11 +373,12 @@ class BASE_EXPORT FilePath { // internally calls SysWideToNativeMB() on POSIX systems other than Mac // and Chrome OS, to mitigate the encoding issue. See the comment at // AsUTF8Unsafe() for details. - static FilePath FromUTF8Unsafe(const std::string& utf8); + static FilePath FromUTF8Unsafe(StringPiece utf8); // Similar to FromUTF8Unsafe, but accepts UTF-16 instead. - static FilePath FromUTF16Unsafe(const string16& utf16); + static FilePath FromUTF16Unsafe(StringPiece16 utf16); + void GetSizeForPickle(PickleSizer* sizer) const; void WriteToPickle(Pickle* pickle) const; bool ReadFromPickle(PickleIterator* iter); diff --git a/src/base/files/file_util.cc b/src/base/files/file_util.cc index 9e35b671..31693704 100644 --- a/src/base/files/file_util.cc +++ b/src/base/files/file_util.cc @@ -124,9 +124,9 @@ bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { } #endif // !defined(OS_NACL_NONSFI) -bool ReadFileToString(const FilePath& path, - std::string* contents, - size_t max_size) { +bool ReadFileToStringWithMaxSize(const FilePath& path, + std::string* contents, + size_t max_size) { if (contents) contents->clear(); if (path.ReferencesParent()) @@ -162,7 +162,8 @@ bool ReadFileToString(const FilePath& path, } bool ReadFileToString(const FilePath& path, std::string* contents) { - return ReadFileToString(path, contents, std::numeric_limits::max()); + return ReadFileToStringWithMaxSize(path, contents, + std::numeric_limits::max()); } #if !defined(OS_NACL_NONSFI) diff --git a/src/base/files/file_util.h b/src/base/files/file_util.h index dfc10a35..05b3cbf7 100644 --- a/src/base/files/file_util.h +++ b/src/base/files/file_util.h @@ -154,9 +154,9 @@ BASE_EXPORT bool ReadFileToString(const FilePath& path, std::string* contents); // |max_size|. // |contents| may be NULL, in which case this function is useful for its side // effect of priming the disk cache (could be used for unit tests). -BASE_EXPORT bool ReadFileToString(const FilePath& path, - std::string* contents, - size_t max_size); +BASE_EXPORT bool ReadFileToStringWithMaxSize(const FilePath& path, + std::string* contents, + size_t max_size); #if defined(OS_POSIX) diff --git a/src/base/files/file_util_posix.cc b/src/base/files/file_util_posix.cc new file mode 100644 index 00000000..802ed515 --- /dev/null +++ b/src/base/files/file_util_posix.cc @@ -0,0 +1,936 @@ +// 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_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/path_service.h" +#include "base/posix/eintr_wrapper.h" +#include "base/stl_util.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_info.h" +#include "base/threading/thread_restrictions.h" +#include "base/time/time.h" +#include "build/build_config.h" + +#if defined(OS_MACOSX) +#include +#include "base/mac/foundation_util.h" +#endif + +#if defined(OS_ANDROID) +#include "base/android/content_uri_utils.h" +#include "base/os_compat_android.h" +#endif + +#if !defined(OS_IOS) +#include +#endif + +namespace base { + +namespace { + +#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) +static int CallStat(const char *path, stat_wrapper_t *sb) { + ThreadRestrictions::AssertIOAllowed(); + return stat(path, sb); +} +static int CallLstat(const char *path, stat_wrapper_t *sb) { + ThreadRestrictions::AssertIOAllowed(); + return lstat(path, sb); +} +#else // defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) +static int CallStat(const char *path, stat_wrapper_t *sb) { + ThreadRestrictions::AssertIOAllowed(); + return stat64(path, sb); +} +static int CallLstat(const char *path, stat_wrapper_t *sb) { + ThreadRestrictions::AssertIOAllowed(); + return lstat64(path, sb); +} +#endif // !(defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)) + +#if !defined(OS_NACL_NONSFI) +// Helper for NormalizeFilePath(), defined below. +bool RealPath(const FilePath& path, FilePath* real_path) { + ThreadRestrictions::AssertIOAllowed(); // For realpath(). + FilePath::CharType buf[PATH_MAX]; + if (!realpath(path.value().c_str(), buf)) + return false; + + *real_path = FilePath(buf); + return true; +} + +// Helper for VerifyPathControlledByUser. +bool VerifySpecificPathControlledByUser(const FilePath& path, + uid_t owner_uid, + const std::set& group_gids) { + stat_wrapper_t stat_info; + if (CallLstat(path.value().c_str(), &stat_info) != 0) { + DPLOG(ERROR) << "Failed to get information on path " + << path.value(); + return false; + } + + if (S_ISLNK(stat_info.st_mode)) { + DLOG(ERROR) << "Path " << path.value() + << " is a symbolic link."; + return false; + } + + if (stat_info.st_uid != owner_uid) { + DLOG(ERROR) << "Path " << path.value() + << " is owned by the wrong user."; + return false; + } + + if ((stat_info.st_mode & S_IWGRP) && + !ContainsKey(group_gids, stat_info.st_gid)) { + DLOG(ERROR) << "Path " << path.value() + << " is writable by an unprivileged group."; + return false; + } + + if (stat_info.st_mode & S_IWOTH) { + DLOG(ERROR) << "Path " << path.value() + << " is writable by any user."; + return false; + } + + return true; +} + +std::string TempFileName() { +#if defined(OS_MACOSX) + return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID()); +#endif + +#if defined(GOOGLE_CHROME_BUILD) + return std::string(".com.google.Chrome.XXXXXX"); +#else + return std::string(".org.chromium.Chromium.XXXXXX"); +#endif +} + +// Creates and opens a temporary file in |directory|, returning the +// file descriptor. |path| is set to the temporary file path. +// This function does NOT unlink() the file. +int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { + ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp(). + *path = directory.Append(base::TempFileName()); + const std::string& tmpdir_string = path->value(); + // this should be OK since mkstemp just replaces characters in place + char* buffer = const_cast(tmpdir_string.c_str()); + + return HANDLE_EINTR(mkstemp(buffer)); +} + +#if defined(OS_LINUX) +// Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC. +// This depends on the mount options used for /dev/shm, which vary among +// different Linux distributions and possibly local configuration. It also +// depends on details of kernel--ChromeOS uses the noexec option for /dev/shm +// but its kernel allows mprotect with PROT_EXEC anyway. +bool DetermineDevShmExecutable() { + bool result = false; + FilePath path; + + ScopedFD fd(CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path)); + if (fd.is_valid()) { + DeleteFile(path, false); + long sysconf_result = sysconf(_SC_PAGESIZE); + CHECK_GE(sysconf_result, 0); + size_t pagesize = static_cast(sysconf_result); + CHECK_GE(sizeof(pagesize), sizeof(sysconf_result)); + void* mapping = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd.get(), 0); + if (mapping != MAP_FAILED) { + if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0) + result = true; + munmap(mapping, pagesize); + } + } + return result; +} +#endif // defined(OS_LINUX) +#endif // !defined(OS_NACL_NONSFI) + +} // namespace + +#if !defined(OS_NACL_NONSFI) +FilePath MakeAbsoluteFilePath(const FilePath& input) { + ThreadRestrictions::AssertIOAllowed(); + char full_path[PATH_MAX]; + if (realpath(input.value().c_str(), full_path) == NULL) + return FilePath(); + return FilePath(full_path); +} + +// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*" +// which works both with and without the recursive flag. I'm not sure we need +// that functionality. If not, remove from file_util_win.cc, otherwise add it +// here. +bool DeleteFile(const FilePath& path, bool recursive) { + ThreadRestrictions::AssertIOAllowed(); + const char* path_str = path.value().c_str(); + stat_wrapper_t file_info; + int test = CallLstat(path_str, &file_info); + if (test != 0) { + // The Windows version defines this condition as success. + bool ret = (errno == ENOENT || errno == ENOTDIR); + return ret; + } + if (!S_ISDIR(file_info.st_mode)) + return (unlink(path_str) == 0); + if (!recursive) + return (rmdir(path_str) == 0); + + bool success = true; + std::stack directories; + directories.push(path.value()); + FileEnumerator traversal(path, true, + FileEnumerator::FILES | FileEnumerator::DIRECTORIES | + FileEnumerator::SHOW_SYM_LINKS); + for (FilePath current = traversal.Next(); success && !current.empty(); + current = traversal.Next()) { + if (traversal.GetInfo().IsDirectory()) + directories.push(current.value()); + else + success = (unlink(current.value().c_str()) == 0); + } + + while (success && !directories.empty()) { + FilePath dir = FilePath(directories.top()); + directories.pop(); + success = (rmdir(dir.value().c_str()) == 0); + } + return success; +} + +bool ReplaceFile(const FilePath& from_path, + const FilePath& to_path, + File::Error* error) { + ThreadRestrictions::AssertIOAllowed(); + if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) + return true; + if (error) + *error = File::OSErrorToFileError(errno); + return false; +} + +bool CopyDirectory(const FilePath& from_path, + const FilePath& to_path, + bool recursive) { + ThreadRestrictions::AssertIOAllowed(); + // Some old callers of CopyDirectory want it to support wildcards. + // After some discussion, we decided to fix those callers. + // Break loudly here if anyone tries to do this. + DCHECK(to_path.value().find('*') == std::string::npos); + DCHECK(from_path.value().find('*') == std::string::npos); + + if (from_path.value().size() >= PATH_MAX) { + return false; + } + + // This function does not properly handle destinations within the source + FilePath real_to_path = to_path; + if (PathExists(real_to_path)) { + real_to_path = MakeAbsoluteFilePath(real_to_path); + if (real_to_path.empty()) + return false; + } else { + real_to_path = MakeAbsoluteFilePath(real_to_path.DirName()); + if (real_to_path.empty()) + return false; + } + FilePath real_from_path = MakeAbsoluteFilePath(from_path); + if (real_from_path.empty()) + return false; + if (real_to_path.value().size() >= real_from_path.value().size() && + real_to_path.value().compare(0, real_from_path.value().size(), + real_from_path.value()) == 0) { + return false; + } + + int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS; + if (recursive) + traverse_type |= FileEnumerator::DIRECTORIES; + FileEnumerator traversal(from_path, recursive, traverse_type); + + // We have to mimic windows behavior here. |to_path| may not exist yet, + // start the loop with |to_path|. + struct stat from_stat; + FilePath current = from_path; + if (stat(from_path.value().c_str(), &from_stat) < 0) { + DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " + << from_path.value() << " errno = " << errno; + return false; + } + struct stat to_path_stat; + FilePath from_path_base = from_path; + if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 && + S_ISDIR(to_path_stat.st_mode)) { + // If the destination already exists and is a directory, then the + // top level of source needs to be copied. + from_path_base = from_path.DirName(); + } + + // The Windows version of this function assumes that non-recursive calls + // will always have a directory for from_path. + // TODO(maruel): This is not necessary anymore. + DCHECK(recursive || S_ISDIR(from_stat.st_mode)); + + bool success = true; + while (success && !current.empty()) { + // current is the source path, including from_path, so append + // the suffix after from_path to to_path to create the target_path. + FilePath target_path(to_path); + if (from_path_base != current) { + if (!from_path_base.AppendRelativePath(current, &target_path)) { + success = false; + break; + } + } + + if (S_ISDIR(from_stat.st_mode)) { + if (mkdir(target_path.value().c_str(), + (from_stat.st_mode & 01777) | S_IRUSR | S_IXUSR | S_IWUSR) != + 0 && + errno != EEXIST) { + DLOG(ERROR) << "CopyDirectory() couldn't create directory: " + << target_path.value() << " errno = " << errno; + success = false; + } + } else if (S_ISREG(from_stat.st_mode)) { + if (!CopyFile(current, target_path)) { + DLOG(ERROR) << "CopyDirectory() couldn't create file: " + << target_path.value(); + success = false; + } + } else { + DLOG(WARNING) << "CopyDirectory() skipping non-regular file: " + << current.value(); + } + + current = traversal.Next(); + if (!current.empty()) + from_stat = traversal.GetInfo().stat(); + } + + return success; +} +#endif // !defined(OS_NACL_NONSFI) + +bool SetNonBlocking(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) + return false; + if (flags & O_NONBLOCK) + return true; + if (HANDLE_EINTR(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1) + return false; + return true; +} + +bool PathExists(const FilePath& path) { + ThreadRestrictions::AssertIOAllowed(); +#if defined(OS_ANDROID) + if (path.IsContentUri()) { + return ContentUriExists(path); + } +#endif + return access(path.value().c_str(), F_OK) == 0; +} + +#if !defined(OS_NACL_NONSFI) +bool PathIsWritable(const FilePath& path) { + ThreadRestrictions::AssertIOAllowed(); + return access(path.value().c_str(), W_OK) == 0; +} +#endif // !defined(OS_NACL_NONSFI) + +bool DirectoryExists(const FilePath& path) { + ThreadRestrictions::AssertIOAllowed(); + stat_wrapper_t file_info; + if (CallStat(path.value().c_str(), &file_info) == 0) + return S_ISDIR(file_info.st_mode); + return false; +} + +bool ReadFromFD(int fd, char* buffer, size_t bytes) { + size_t total_read = 0; + while (total_read < bytes) { + ssize_t bytes_read = + HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); + if (bytes_read <= 0) + break; + total_read += bytes_read; + } + return total_read == bytes; +} + +#if !defined(OS_NACL_NONSFI) +bool CreateSymbolicLink(const FilePath& target_path, + const FilePath& symlink_path) { + DCHECK(!symlink_path.empty()); + DCHECK(!target_path.empty()); + return ::symlink(target_path.value().c_str(), + symlink_path.value().c_str()) != -1; +} + +bool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) { + DCHECK(!symlink_path.empty()); + DCHECK(target_path); + char buf[PATH_MAX]; + ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf)); + + if (count <= 0) { + target_path->clear(); + return false; + } + + *target_path = FilePath(FilePath::StringType(buf, count)); + return true; +} + +bool GetPosixFilePermissions(const FilePath& path, int* mode) { + ThreadRestrictions::AssertIOAllowed(); + DCHECK(mode); + + stat_wrapper_t file_info; + // Uses stat(), because on symbolic link, lstat() does not return valid + // permission bits in st_mode + if (CallStat(path.value().c_str(), &file_info) != 0) + return false; + + *mode = file_info.st_mode & FILE_PERMISSION_MASK; + return true; +} + +bool SetPosixFilePermissions(const FilePath& path, + int mode) { + ThreadRestrictions::AssertIOAllowed(); + DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0); + + // Calls stat() so that we can preserve the higher bits like S_ISGID. + stat_wrapper_t stat_buf; + if (CallStat(path.value().c_str(), &stat_buf) != 0) + return false; + + // Clears the existing permission bits, and adds the new ones. + mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK; + updated_mode_bits |= mode & FILE_PERMISSION_MASK; + + if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0) + return false; + + return true; +} + +#if !defined(OS_MACOSX) +// This is implemented in file_util_mac.mm for Mac. +bool GetTempDir(FilePath* path) { + const char* tmp = getenv("TMPDIR"); + if (tmp) { + *path = FilePath(tmp); + } else { +#if defined(OS_ANDROID) + return PathService::Get(base::DIR_CACHE, path); +#else + *path = FilePath("/tmp"); +#endif + } + return true; +} +#endif // !defined(OS_MACOSX) + +#if !defined(OS_MACOSX) // Mac implementation is in file_util_mac.mm. +FilePath GetHomeDir() { +#if defined(OS_CHROMEOS) + if (SysInfo::IsRunningOnChromeOS()) { + // On Chrome OS chrome::DIR_USER_DATA is overridden with a primary user + // homedir once it becomes available. Return / as the safe option. + return FilePath("/"); + } +#endif + + const char* home_dir = getenv("HOME"); + if (home_dir && home_dir[0]) + return FilePath(home_dir); + +#if defined(OS_ANDROID) + DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented."; +#endif + + FilePath rv; + if (GetTempDir(&rv)) + return rv; + + // Last resort. + return FilePath("/tmp"); +} +#endif // !defined(OS_MACOSX) + +bool CreateTemporaryFile(FilePath* path) { + ThreadRestrictions::AssertIOAllowed(); // For call to close(). + FilePath directory; + if (!GetTempDir(&directory)) + return false; + int fd = CreateAndOpenFdForTemporaryFile(directory, path); + if (fd < 0) + return false; + close(fd); + return true; +} + +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { + int fd = CreateAndOpenFdForTemporaryFile(dir, path); + if (fd < 0) + return NULL; + + FILE* file = fdopen(fd, "a+"); + if (!file) + close(fd); + return file; +} + +bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { + ThreadRestrictions::AssertIOAllowed(); // For call to close(). + int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file); + return ((fd >= 0) && !IGNORE_EINTR(close(fd))); +} + +static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir, + const FilePath::StringType& name_tmpl, + FilePath* new_dir) { + ThreadRestrictions::AssertIOAllowed(); // For call to mkdtemp(). + DCHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos) + << "Directory name template must contain \"XXXXXX\"."; + + FilePath sub_dir = base_dir.Append(name_tmpl); + std::string sub_dir_string = sub_dir.value(); + + // this should be OK since mkdtemp just replaces characters in place + char* buffer = const_cast(sub_dir_string.c_str()); + char* dtemp = mkdtemp(buffer); + if (!dtemp) { + DPLOG(ERROR) << "mkdtemp"; + return false; + } + *new_dir = FilePath(dtemp); + return true; +} + +bool CreateTemporaryDirInDir(const FilePath& base_dir, + const FilePath::StringType& prefix, + FilePath* new_dir) { + FilePath::StringType mkdtemp_template = prefix; + mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX")); + return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir); +} + +bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path) { + FilePath tmpdir; + if (!GetTempDir(&tmpdir)) + return false; + + return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path); +} + +bool CreateDirectoryAndGetError(const FilePath& full_path, + File::Error* error) { + ThreadRestrictions::AssertIOAllowed(); // For call to mkdir(). + std::vector subpaths; + + // Collect a list of all parent directories. + FilePath last_path = full_path; + subpaths.push_back(full_path); + for (FilePath path = full_path.DirName(); + path.value() != last_path.value(); path = path.DirName()) { + subpaths.push_back(path); + last_path = path; + } + + // Iterate through the parents and create the missing ones. + for (std::vector::reverse_iterator i = subpaths.rbegin(); + i != subpaths.rend(); ++i) { + if (DirectoryExists(*i)) + continue; + if (mkdir(i->value().c_str(), 0700) == 0) + continue; + // Mkdir failed, but it might have failed with EEXIST, or some other error + // due to the the directory appearing out of thin air. This can occur if + // two processes are trying to create the same file system tree at the same + // time. Check to see if it exists and make sure it is a directory. + int saved_errno = errno; + if (!DirectoryExists(*i)) { + if (error) + *error = File::OSErrorToFileError(saved_errno); + return false; + } + } + return true; +} + +bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { + FilePath real_path_result; + if (!RealPath(path, &real_path_result)) + return false; + + // To be consistant with windows, fail if |real_path_result| is a + // directory. + stat_wrapper_t file_info; + if (CallStat(real_path_result.value().c_str(), &file_info) != 0 || + S_ISDIR(file_info.st_mode)) + return false; + + *normalized_path = real_path_result; + return true; +} + +// TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks +// correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948 +bool IsLink(const FilePath& file_path) { + stat_wrapper_t st; + // If we can't lstat the file, it's safe to assume that the file won't at + // least be a 'followable' link. + if (CallLstat(file_path.value().c_str(), &st) != 0) + return false; + + if (S_ISLNK(st.st_mode)) + return true; + else + return false; +} + +bool GetFileInfo(const FilePath& file_path, File::Info* results) { + stat_wrapper_t file_info; +#if defined(OS_ANDROID) + if (file_path.IsContentUri()) { + File file = OpenContentUriForRead(file_path); + if (!file.IsValid()) + return false; + return file.GetInfo(results); + } else { +#endif // defined(OS_ANDROID) + if (CallStat(file_path.value().c_str(), &file_info) != 0) + return false; +#if defined(OS_ANDROID) + } +#endif // defined(OS_ANDROID) + + results->FromStat(file_info); + return true; +} +#endif // !defined(OS_NACL_NONSFI) + +FILE* OpenFile(const FilePath& filename, const char* mode) { + ThreadRestrictions::AssertIOAllowed(); + FILE* result = NULL; + do { + result = fopen(filename.value().c_str(), mode); + } while (!result && errno == EINTR); + return result; +} + +// NaCl doesn't implement system calls to open files directly. +#if !defined(OS_NACL) +FILE* FileToFILE(File file, const char* mode) { + FILE* stream = fdopen(file.GetPlatformFile(), mode); + if (stream) + file.TakePlatformFile(); + return stream; +} +#endif // !defined(OS_NACL) + +int ReadFile(const FilePath& filename, char* data, int max_size) { + ThreadRestrictions::AssertIOAllowed(); + int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY)); + if (fd < 0) + return -1; + + ssize_t bytes_read = HANDLE_EINTR(read(fd, data, max_size)); + if (IGNORE_EINTR(close(fd)) < 0) + return -1; + return bytes_read; +} + +int WriteFile(const FilePath& filename, const char* data, int size) { + ThreadRestrictions::AssertIOAllowed(); + int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666)); + if (fd < 0) + return -1; + + int bytes_written = WriteFileDescriptor(fd, data, size) ? size : -1; + if (IGNORE_EINTR(close(fd)) < 0) + return -1; + return bytes_written; +} + +bool WriteFileDescriptor(const int fd, const char* data, int size) { + // Allow for partial writes. + ssize_t bytes_written_total = 0; + for (ssize_t bytes_written_partial = 0; bytes_written_total < size; + bytes_written_total += bytes_written_partial) { + bytes_written_partial = + HANDLE_EINTR(write(fd, data + bytes_written_total, + size - bytes_written_total)); + if (bytes_written_partial < 0) + return false; + } + + return true; +} + +#if !defined(OS_NACL_NONSFI) + +bool AppendToFile(const FilePath& filename, const char* data, int size) { + ThreadRestrictions::AssertIOAllowed(); + bool ret = true; + int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND)); + if (fd < 0) { + VPLOG(1) << "Unable to create file " << filename.value(); + return false; + } + + // This call will either write all of the data or return false. + if (!WriteFileDescriptor(fd, data, size)) { + VPLOG(1) << "Error while writing to file " << filename.value(); + ret = false; + } + + if (IGNORE_EINTR(close(fd)) < 0) { + VPLOG(1) << "Error while closing file " << filename.value(); + return false; + } + + return ret; +} + +// Gets the current working directory for the process. +bool GetCurrentDirectory(FilePath* dir) { + // getcwd can return ENOENT, which implies it checks against the disk. + ThreadRestrictions::AssertIOAllowed(); + + char system_buffer[PATH_MAX] = ""; + if (!getcwd(system_buffer, sizeof(system_buffer))) { + NOTREACHED(); + return false; + } + *dir = FilePath(system_buffer); + return true; +} + +// Sets the current working directory for the process. +bool SetCurrentDirectory(const FilePath& path) { + ThreadRestrictions::AssertIOAllowed(); + int ret = chdir(path.value().c_str()); + return !ret; +} + +bool VerifyPathControlledByUser(const FilePath& base, + const FilePath& path, + uid_t owner_uid, + const std::set& group_gids) { + if (base != path && !base.IsParent(path)) { + DLOG(ERROR) << "|base| must be a subdirectory of |path|. base = \"" + << base.value() << "\", path = \"" << path.value() << "\""; + return false; + } + + std::vector base_components; + std::vector path_components; + + base.GetComponents(&base_components); + path.GetComponents(&path_components); + + std::vector::const_iterator ib, ip; + for (ib = base_components.begin(), ip = path_components.begin(); + ib != base_components.end(); ++ib, ++ip) { + // |base| must be a subpath of |path|, so all components should match. + // If these CHECKs fail, look at the test that base is a parent of + // path at the top of this function. + DCHECK(ip != path_components.end()); + DCHECK(*ip == *ib); + } + + FilePath current_path = base; + if (!VerifySpecificPathControlledByUser(current_path, owner_uid, group_gids)) + return false; + + for (; ip != path_components.end(); ++ip) { + current_path = current_path.Append(*ip); + if (!VerifySpecificPathControlledByUser( + current_path, owner_uid, group_gids)) + return false; + } + return true; +} + +#if defined(OS_MACOSX) && !defined(OS_IOS) +bool VerifyPathControlledByAdmin(const FilePath& path) { + const unsigned kRootUid = 0; + const FilePath kFileSystemRoot("/"); + + // The name of the administrator group on mac os. + const char* const kAdminGroupNames[] = { + "admin", + "wheel" + }; + + // Reading the groups database may touch the file system. + ThreadRestrictions::AssertIOAllowed(); + + std::set allowed_group_ids; + for (int i = 0, ie = arraysize(kAdminGroupNames); i < ie; ++i) { + struct group *group_record = getgrnam(kAdminGroupNames[i]); + if (!group_record) { + DPLOG(ERROR) << "Could not get the group ID of group \"" + << kAdminGroupNames[i] << "\"."; + continue; + } + + allowed_group_ids.insert(group_record->gr_gid); + } + + return VerifyPathControlledByUser( + kFileSystemRoot, path, kRootUid, allowed_group_ids); +} +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + +int GetMaximumPathComponentLength(const FilePath& path) { + ThreadRestrictions::AssertIOAllowed(); + return pathconf(path.value().c_str(), _PC_NAME_MAX); +} + +#if !defined(OS_ANDROID) +// This is implemented in file_util_android.cc for that platform. +bool GetShmemTempDir(bool executable, FilePath* path) { +#if defined(OS_LINUX) + bool use_dev_shm = true; + if (executable) { + static const bool s_dev_shm_executable = DetermineDevShmExecutable(); + use_dev_shm = s_dev_shm_executable; + } + if (use_dev_shm) { + *path = FilePath("/dev/shm"); + return true; + } +#endif + return GetTempDir(path); +} +#endif // !defined(OS_ANDROID) + +#if !defined(OS_MACOSX) +// Mac has its own implementation, this is for all other Posix systems. +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + ThreadRestrictions::AssertIOAllowed(); + File infile; +#if defined(OS_ANDROID) + if (from_path.IsContentUri()) { + infile = OpenContentUriForRead(from_path); + } else { + infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ); + } +#else + infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ); +#endif + if (!infile.IsValid()) + return false; + + File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS); + if (!outfile.IsValid()) + return false; + + const size_t kBufferSize = 32768; + std::vector buffer(kBufferSize); + bool result = true; + + while (result) { + ssize_t bytes_read = infile.ReadAtCurrentPos(&buffer[0], buffer.size()); + if (bytes_read < 0) { + result = false; + break; + } + if (bytes_read == 0) + break; + // Allow for partial writes + ssize_t bytes_written_per_read = 0; + do { + ssize_t bytes_written_partial = outfile.WriteAtCurrentPos( + &buffer[bytes_written_per_read], bytes_read - bytes_written_per_read); + if (bytes_written_partial < 0) { + result = false; + break; + } + bytes_written_per_read += bytes_written_partial; + } while (bytes_written_per_read < bytes_read); + } + + return result; +} +#endif // !defined(OS_MACOSX) + +// ----------------------------------------------------------------------------- + +namespace internal { + +bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { + ThreadRestrictions::AssertIOAllowed(); + // Windows compatibility: if to_path exists, from_path and to_path + // must be the same type, either both files, or both directories. + stat_wrapper_t to_file_info; + if (CallStat(to_path.value().c_str(), &to_file_info) == 0) { + stat_wrapper_t from_file_info; + if (CallStat(from_path.value().c_str(), &from_file_info) == 0) { + if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode)) + return false; + } else { + return false; + } + } + + if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) + return true; + + if (!CopyDirectory(from_path, to_path, true)) + return false; + + DeleteFile(from_path, true); + return true; +} + +} // namespace internal + +#endif // !defined(OS_NACL_NONSFI) +} // namespace base diff --git a/src/base/files/memory_mapped_file.cc b/src/base/files/memory_mapped_file.cc new file mode 100644 index 00000000..0fd9d679 --- /dev/null +++ b/src/base/files/memory_mapped_file.cc @@ -0,0 +1,95 @@ +// 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 "base/files/memory_mapped_file.h" + +#include + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/sys_info.h" +#include "build/build_config.h" + +namespace base { + +const MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile = {0, 0}; + +bool MemoryMappedFile::Region::operator==( + const MemoryMappedFile::Region& other) const { + return other.offset == offset && other.size == size; +} + +bool MemoryMappedFile::Region::operator!=( + const MemoryMappedFile::Region& other) const { + return other.offset != offset || other.size != size; +} + +MemoryMappedFile::~MemoryMappedFile() { + CloseHandles(); +} + +#if !defined(OS_NACL) +bool MemoryMappedFile::Initialize(const FilePath& file_name) { + if (IsValid()) + return false; + + file_.Initialize(file_name, File::FLAG_OPEN | File::FLAG_READ); + + if (!file_.IsValid()) { + DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe(); + return false; + } + + if (!MapFileRegionToMemory(Region::kWholeFile)) { + CloseHandles(); + return false; + } + + return true; +} + +bool MemoryMappedFile::Initialize(File file) { + return Initialize(std::move(file), Region::kWholeFile); +} + +bool MemoryMappedFile::Initialize(File file, const Region& region) { + if (IsValid()) + return false; + + if (region != Region::kWholeFile) { + DCHECK_GE(region.offset, 0); + DCHECK_GT(region.size, 0); + } + + file_ = std::move(file); + + if (!MapFileRegionToMemory(region)) { + CloseHandles(); + return false; + } + + return true; +} + +bool MemoryMappedFile::IsValid() const { + return data_ != NULL; +} + +// static +void MemoryMappedFile::CalculateVMAlignedBoundaries(int64_t start, + int64_t size, + int64_t* aligned_start, + int64_t* aligned_size, + int32_t* offset) { + // Sadly, on Windows, the mmap alignment is not just equal to the page size. + const int64_t mask = + static_cast(SysInfo::VMAllocationGranularity()) - 1; + DCHECK_LT(mask, std::numeric_limits::max()); + *offset = start & mask; + *aligned_start = start & ~mask; + *aligned_size = (size + *offset + mask) & ~mask; +} +#endif + +} // namespace base diff --git a/src/base/files/memory_mapped_file.h b/src/base/files/memory_mapped_file.h new file mode 100644 index 00000000..6362e765 --- /dev/null +++ b/src/base/files/memory_mapped_file.h @@ -0,0 +1,104 @@ +// 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 BASE_FILES_MEMORY_MAPPED_FILE_H_ +#define BASE_FILES_MEMORY_MAPPED_FILE_H_ + +#include +#include + +#include "base/base_export.h" +#include "base/files/file.h" +#include "base/macros.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif + +namespace base { + +class FilePath; + +class BASE_EXPORT MemoryMappedFile { + public: + // The default constructor sets all members to invalid/null values. + MemoryMappedFile(); + ~MemoryMappedFile(); + + // Used to hold information about a region [offset + size] of a file. + struct BASE_EXPORT Region { + static const Region kWholeFile; + + bool operator==(const Region& other) const; + bool operator!=(const Region& other) const; + + // Start of the region (measured in bytes from the beginning of the file). + int64_t offset; + + // Length of the region in bytes. + int64_t size; + }; + + // Opens an existing file and maps it into memory. Access is restricted to + // read only. If this object already points to a valid memory mapped file + // then this method will fail and return false. If it cannot open the file, + // the file does not exist, or the memory mapping fails, it will return false. + // Later we may want to allow the user to specify access. + bool Initialize(const FilePath& file_name); + + // As above, but works with an already-opened file. MemoryMappedFile takes + // ownership of |file| and closes it when done. + bool Initialize(File file); + + // As above, but works with a region of an already-opened file. + bool Initialize(File file, const Region& region); + +#if defined(OS_WIN) + // Opens an existing file and maps it as an image section. Please refer to + // the Initialize function above for additional information. + bool InitializeAsImageSection(const FilePath& file_name); +#endif // OS_WIN + + const uint8_t* data() const { return data_; } + size_t length() const { return length_; } + + // Is file_ a valid file handle that points to an open, memory mapped file? + bool IsValid() const; + + private: + // Given the arbitrarily aligned memory region [start, size], returns the + // boundaries of the region aligned to the granularity specified by the OS, + // (a page on Linux, ~32k on Windows) as follows: + // - |aligned_start| is page aligned and <= |start|. + // - |aligned_size| is a multiple of the VM granularity and >= |size|. + // - |offset| is the displacement of |start| w.r.t |aligned_start|. + static void CalculateVMAlignedBoundaries(int64_t start, + int64_t size, + int64_t* aligned_start, + int64_t* aligned_size, + int32_t* offset); + + // Map the file to memory, set data_ to that memory address. Return true on + // success, false on any kind of failure. This is a helper for Initialize(). + bool MapFileRegionToMemory(const Region& region); + + // Closes all open handles. + void CloseHandles(); + + File file_; + uint8_t* data_; + size_t length_; + +#if defined(OS_WIN) + win::ScopedHandle file_mapping_; + bool image_; // Map as an image. +#endif + + DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile); +}; + +} // namespace base + +#endif // BASE_FILES_MEMORY_MAPPED_FILE_H_ diff --git a/src/base/hash.cc b/src/base/hash.cc new file mode 100644 index 00000000..d3206f6a --- /dev/null +++ b/src/base/hash.cc @@ -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. + +#include "base/hash.h" + +// Definition in base/third_party/superfasthash/superfasthash.c. (Third-party +// code did not come with its own header file, so declaring the function here.) +// Note: This algorithm is also in Blink under Source/wtf/StringHasher.h. +extern "C" uint32_t SuperFastHash(const char* data, int len); + +namespace base { + +uint32_t SuperFastHash(const char* data, int len) { + return ::SuperFastHash(data, len); +} + +} // namespace base diff --git a/src/base/hash.h b/src/base/hash.h new file mode 100644 index 00000000..97e251cd --- /dev/null +++ b/src/base/hash.h @@ -0,0 +1,122 @@ +// 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 BASE_HASH_H_ +#define BASE_HASH_H_ + +#include +#include + +#include +#include +#include + +#include "base/base_export.h" +#include "base/logging.h" + +namespace base { + +// WARNING: This hash function should not be used for any cryptographic purpose. +BASE_EXPORT uint32_t SuperFastHash(const char* data, int len); + +// Computes a hash of a memory buffer |data| of a given |length|. +// WARNING: This hash function should not be used for any cryptographic purpose. +inline uint32_t Hash(const char* data, size_t length) { + if (length > static_cast(std::numeric_limits::max())) { + NOTREACHED(); + return 0; + } + return SuperFastHash(data, static_cast(length)); +} + +// Computes a hash of a string |str|. +// WARNING: This hash function should not be used for any cryptographic purpose. +inline uint32_t Hash(const std::string& str) { + return Hash(str.data(), str.size()); +} + +// Implement hashing for pairs of at-most 32 bit integer values. +// When size_t is 32 bits, we turn the 64-bit hash code into 32 bits by using +// multiply-add hashing. This algorithm, as described in +// Theorem 4.3.3 of the thesis "Ãœber die Komplexität der Multiplikation in +// eingeschränkten Branchingprogrammmodellen" by Woelfel, is: +// +// h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32 +// +// Contact danakj@chromium.org for any questions. +inline size_t HashInts32(uint32_t value1, uint32_t value2) { + uint64_t value1_64 = value1; + uint64_t hash64 = (value1_64 << 32) | value2; + + if (sizeof(size_t) >= sizeof(uint64_t)) + return static_cast(hash64); + + uint64_t odd_random = 481046412LL << 32 | 1025306955LL; + uint32_t shift_random = 10121U << 16; + + hash64 = hash64 * odd_random + shift_random; + size_t high_bits = + static_cast(hash64 >> (8 * (sizeof(uint64_t) - sizeof(size_t)))); + return high_bits; +} + +// Implement hashing for pairs of up-to 64-bit integer values. +// We use the compound integer hash method to produce a 64-bit hash code, by +// breaking the two 64-bit inputs into 4 32-bit values: +// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000 +// Then we reduce our result to 32 bits if required, similar to above. +inline size_t HashInts64(uint64_t value1, uint64_t value2) { + uint32_t short_random1 = 842304669U; + uint32_t short_random2 = 619063811U; + uint32_t short_random3 = 937041849U; + uint32_t short_random4 = 3309708029U; + + uint32_t value1a = static_cast(value1 & 0xffffffff); + uint32_t value1b = static_cast((value1 >> 32) & 0xffffffff); + uint32_t value2a = static_cast(value2 & 0xffffffff); + uint32_t value2b = static_cast((value2 >> 32) & 0xffffffff); + + uint64_t product1 = static_cast(value1a) * short_random1; + uint64_t product2 = static_cast(value1b) * short_random2; + uint64_t product3 = static_cast(value2a) * short_random3; + uint64_t product4 = static_cast(value2b) * short_random4; + + uint64_t hash64 = product1 + product2 + product3 + product4; + + if (sizeof(size_t) >= sizeof(uint64_t)) + return static_cast(hash64); + + uint64_t odd_random = 1578233944LL << 32 | 194370989LL; + uint32_t shift_random = 20591U << 16; + + hash64 = hash64 * odd_random + shift_random; + size_t high_bits = + static_cast(hash64 >> (8 * (sizeof(uint64_t) - sizeof(size_t)))); + return high_bits; +} + +template +inline size_t HashInts(T1 value1, T2 value2) { + // This condition is expected to be compile-time evaluated and optimised away + // in release builds. + if (sizeof(T1) > sizeof(uint32_t) || (sizeof(T2) > sizeof(uint32_t))) + return HashInts64(value1, value2); + + return HashInts32(value1, value2); +} + +// A templated hasher for pairs of integer types. +template +struct IntPairHash; + +template +struct IntPairHash> { + size_t operator()(std::pair value) const { + return HashInts(value.first, value.second); + } +}; + +} // namespace base + +#endif // BASE_HASH_H_ diff --git a/src/base/json/json_parser.cc b/src/base/json/json_parser.cc index 32292754..fbd4da45 100644 --- a/src/base/json/json_parser.cc +++ b/src/base/json/json_parser.cc @@ -275,6 +275,14 @@ std::string JSONParser::GetErrorMessage() const { JSONReader::ErrorCodeToString(error_code_)); } +int JSONParser::error_line() const { + return error_line_; +} + +int JSONParser::error_column() const { + return error_column_; +} + // StringBuilder /////////////////////////////////////////////////////////////// JSONParser::StringBuilder::StringBuilder() diff --git a/src/base/json/json_parser.h b/src/base/json/json_parser.h index 57c07ba1..fc04594a 100644 --- a/src/base/json/json_parser.h +++ b/src/base/json/json_parser.h @@ -58,6 +58,14 @@ class BASE_EXPORT JSONParser { // Returns the human-friendly error message. std::string GetErrorMessage() const; + // Returns the error line number if parse error happened. Otherwise always + // returns 0. + int error_line() const; + + // Returns the error column number if parse error happened. Otherwise always + // returns 0. + int error_column() const; + private: enum Token { T_OBJECT_BEGIN, // { diff --git a/src/base/json/json_reader.cc b/src/base/json/json_reader.cc index 015df437..3ab5f754 100644 --- a/src/base/json/json_reader.cc +++ b/src/base/json/json_reader.cc @@ -59,7 +59,9 @@ scoped_ptr JSONReader::Read(const StringPiece& json, int options) { scoped_ptr JSONReader::ReadAndReturnError(const StringPiece& json, int options, int* error_code_out, - std::string* error_msg_out) { + std::string* error_msg_out, + int* error_line_out, + int* error_column_out) { internal::JSONParser parser(options); scoped_ptr root(parser.Parse(json)); if (!root) { @@ -67,6 +69,10 @@ scoped_ptr JSONReader::ReadAndReturnError(const StringPiece& json, *error_code_out = parser.error_code(); if (error_msg_out) *error_msg_out = parser.GetErrorMessage(); + if (error_line_out) + *error_line_out = parser.error_line(); + if (error_column_out) + *error_column_out = parser.error_column(); } return root; diff --git a/src/base/json/json_reader.h b/src/base/json/json_reader.h index 45783b0c..c6bcb528 100644 --- a/src/base/json/json_reader.h +++ b/src/base/json/json_reader.h @@ -107,7 +107,9 @@ class BASE_EXPORT JSONReader { static scoped_ptr ReadAndReturnError(const StringPiece& json, int options, // JSONParserOptions int* error_code_out, - std::string* error_msg_out); + std::string* error_msg_out, + int* error_line_out = nullptr, + int* error_column_out = nullptr); // Converts a JSON parse error code into a human readable message. // Returns an empty string if error_code is JSON_NO_ERROR. diff --git a/src/base/location.h b/src/base/location.h index d3bb23c6..21e270c5 100644 --- a/src/base/location.h +++ b/src/base/location.h @@ -11,7 +11,7 @@ #include #include "base/base_export.h" -#include "base/containers/hash_tables.h" +#include "base/hash.h" namespace tracked_objects { @@ -59,7 +59,7 @@ class BASE_EXPORT Location { // it comes from __FILE__, so no need to check the contents of the string. // See the definition of FROM_HERE in location.h, and how it is used // elsewhere. - return base::HashPair(reinterpret_cast(location.file_name()), + return base::HashInts(reinterpret_cast(location.file_name()), location.line_number()); } }; diff --git a/src/base/logging.h b/src/base/logging.h index 300c9b52..adf8fde6 100644 --- a/src/base/logging.h +++ b/src/base/logging.h @@ -373,9 +373,6 @@ const LogSeverity LOG_0 = LOG_ERROR; #define LOG_IF(severity, condition) \ LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) -#define SYSLOG(severity) LOG(severity) -#define SYSLOG_IF(severity, condition) LOG_IF(severity, condition) - // The VLOG macros log with negative verbosities. #define VLOG_STREAM(verbose_level) \ logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream() @@ -408,8 +405,6 @@ const LogSeverity LOG_0 = LOG_ERROR; #define LOG_ASSERT(condition) \ LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". " -#define SYSLOG_ASSERT(condition) \ - SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". " #if defined(OS_WIN) #define PLOG_STREAM(severity) \ @@ -807,12 +802,6 @@ class BASE_EXPORT LogMessage { DISALLOW_COPY_AND_ASSIGN(LogMessage); }; -// A non-macro interface to the log facility; (useful -// when the logging level is not a compile-time constant). -inline void LogAtLevel(int log_level, const std::string& msg) { - LogMessage(__FILE__, __LINE__, log_level).stream() << msg; -} - // This class is used to explicitly ignore values in the conditional // logging macros. This avoids compiler warnings like "value computed // is not used" and "statement has no effect". diff --git a/src/base/mac/foundation_util.mm b/src/base/mac/foundation_util.mm index 524f17cd..6ae5df33 100644 --- a/src/base/mac/foundation_util.mm +++ b/src/base/mac/foundation_util.mm @@ -165,7 +165,7 @@ FilePath GetAppBundlePath(const FilePath& exec_name) { // The first component may be "/" or "//", etc. Only append '/' if it doesn't // already end in '/'. - if (bundle_name[bundle_name.length() - 1] != '/') + if (bundle_name.back() != '/') bundle_name += '/'; // Go through the remaining components. diff --git a/src/base/mac/mac_logging.h b/src/base/mac/mac_logging.h index f5589028..30e43ead 100644 --- a/src/base/mac/mac_logging.h +++ b/src/base/mac/mac_logging.h @@ -29,6 +29,9 @@ namespace logging { +// Returns a UTF8 description from an OS X Status error. +BASE_EXPORT std::string DescriptionFromOSStatus(OSStatus err); + class BASE_EXPORT OSStatusLogMessage : public logging::LogMessage { public: OSStatusLogMessage(const char* file_path, diff --git a/src/base/mac/mac_logging.cc b/src/base/mac/mac_logging.mm similarity index 78% rename from src/base/mac/mac_logging.cc rename to src/base/mac/mac_logging.mm index e82b0ed5..381ad306 100644 --- a/src/base/mac/mac_logging.cc +++ b/src/base/mac/mac_logging.mm @@ -3,16 +3,25 @@ // found in the LICENSE file. #include "base/mac/mac_logging.h" -#include "build/build_config.h" + +#import #include +#include "build/build_config.h" + #if !defined(OS_IOS) #include #endif namespace logging { +std::string DescriptionFromOSStatus(OSStatus err) { + NSError* error = + [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]; + return error.description.UTF8String; +} + OSStatusLogMessage::OSStatusLogMessage(const char* file_path, int line, LogSeverity severity, @@ -28,7 +37,7 @@ OSStatusLogMessage::~OSStatusLogMessage() { stream() << ": " << status_; #else stream() << ": " - << GetMacOSStatusErrorString(status_) + << DescriptionFromOSStatus(status_) << " (" << status_ << ")"; diff --git a/src/base/macros.h b/src/base/macros.h index fa12dd2e..46ee1dad 100644 --- a/src/base/macros.h +++ b/src/base/macros.h @@ -11,7 +11,6 @@ #define BASE_MACROS_H_ #include // For size_t. -#include // For memcpy. // Put this in the declarations for a class to be uncopyable. #define DISALLOW_COPY(TypeName) \ @@ -49,70 +48,6 @@ template char (&ArraySizeHelper(T (&array)[N]))[N]; #define arraysize(array) (sizeof(ArraySizeHelper(array))) -// bit_cast is a template function that implements the -// equivalent of "*reinterpret_cast(&source)". We need this in -// very low-level functions like the protobuf library and fast math -// support. -// -// float f = 3.14159265358979; -// int i = bit_cast(f); -// // i = 0x40490fdb -// -// The classical address-casting method is: -// -// // WRONG -// float f = 3.14159265358979; // WRONG -// int i = * reinterpret_cast(&f); // WRONG -// -// The address-casting method actually produces undefined behavior -// according to ISO C++ specification section 3.10 -15 -. Roughly, this -// section says: if an object in memory has one type, and a program -// accesses it with a different type, then the result is undefined -// behavior for most values of "different type". -// -// This is true for any cast syntax, either *(int*)&f or -// *reinterpret_cast(&f). And it is particularly true for -// conversions between integral lvalues and floating-point lvalues. -// -// The purpose of 3.10 -15- is to allow optimizing compilers to assume -// that expressions with different types refer to different memory. gcc -// 4.0.1 has an optimizer that takes advantage of this. So a -// non-conforming program quietly produces wildly incorrect output. -// -// The problem is not the use of reinterpret_cast. The problem is type -// punning: holding an object in memory of one type and reading its bits -// back using a different type. -// -// The C++ standard is more subtle and complex than this, but that -// is the basic idea. -// -// Anyways ... -// -// bit_cast<> calls memcpy() which is blessed by the standard, -// especially by the example in section 3.9 . Also, of course, -// bit_cast<> wraps up the nasty logic in one place. -// -// Fortunately memcpy() is very fast. In optimized mode, with a -// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline -// code with the minimal amount of data movement. On a 32-bit system, -// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8) -// compiles to two loads and two stores. -// -// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1. -// -// WARNING: if Dest or Source is a non-POD type, the result of the memcpy -// is likely to surprise you. - -template -inline Dest bit_cast(const Source& source) { - static_assert(sizeof(Dest) == sizeof(Source), - "bit_cast requires source and destination to be the same size"); - - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -} - // Used to explicitly mark the return value of a function as unused. If you are // really sure you don't want to do anything with the return value of a function // that has been marked WARN_UNUSED_RESULT, wrap it with this. Example: diff --git a/src/base/memory/raw_scoped_refptr_mismatch_checker.h b/src/base/memory/raw_scoped_refptr_mismatch_checker.h index 09f982b1..1e88b487 100644 --- a/src/base/memory/raw_scoped_refptr_mismatch_checker.h +++ b/src/base/memory/raw_scoped_refptr_mismatch_checker.h @@ -47,14 +47,14 @@ struct ParamsUseScopedRefptrCorrectly { }; template <> -struct ParamsUseScopedRefptrCorrectly> { +struct ParamsUseScopedRefptrCorrectly> { enum { value = 1 }; }; template -struct ParamsUseScopedRefptrCorrectly> { +struct ParamsUseScopedRefptrCorrectly> { enum { value = !NeedsScopedRefptrButGetsRawPtr::value && - ParamsUseScopedRefptrCorrectly>::value }; + ParamsUseScopedRefptrCorrectly>::value }; }; } // namespace internal diff --git a/src/base/memory/ref_counted.h b/src/base/memory/ref_counted.h index e569c080..e7395148 100644 --- a/src/base/memory/ref_counted.h +++ b/src/base/memory/ref_counted.h @@ -118,7 +118,7 @@ class BASE_EXPORT RefCountedThreadSafeBase { // ~MyFoo(); // }; // -// You should always make your destructor private, to avoid any code deleting +// You should always make your destructor non-public, to avoid any code deleting // the object accidently while there are references to it. template class RefCounted : public subtle::RefCountedBase { @@ -360,15 +360,25 @@ class scoped_refptr { private: template friend class scoped_refptr; - // Allow scoped_refptr to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). + // Implement "Safe Bool Idiom" + // https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Safe_bool // - // Note that this trick is only safe when the == and != operators - // are declared explicitly, as otherwise "refptr1 == refptr2" - // will compile but do the wrong thing (i.e., convert to Testable - // and then do the comparison). + // Allow scoped_refptr to be used in boolean expressions such as + // if (ref_ptr_instance) + // But do not become convertible to a real bool (which is dangerous). + // Implementation requires: + // typedef Testable + // operator Testable() const + // operator== + // operator!= + // + // == and != operators must be declared explicitly or dissallowed, as + // otherwise "ptr1 == ptr2" will compile but do the wrong thing (i.e., convert + // to Testable and then do the comparison). + // + // C++11 provides for "explicit operator bool()", however it is currently + // banned due to MSVS2013. https://chromium-cpp.appspot.com/#core-blacklist typedef T* scoped_refptr::*Testable; - public: operator Testable() const { return ptr_ ? &scoped_refptr::ptr_ : nullptr; } @@ -416,8 +426,6 @@ scoped_refptr make_scoped_refptr(T* t) { return scoped_refptr(t); } -// Temporary operator overloads to facilitate the transition. See -// https://crbug.com/110610. template bool operator==(const scoped_refptr& lhs, const U* rhs) { return lhs.get() == rhs; diff --git a/src/base/memory/scoped_ptr.h b/src/base/memory/scoped_ptr.h index 282a0148..b14287ce 100644 --- a/src/base/memory/scoped_ptr.h +++ b/src/base/memory/scoped_ptr.h @@ -33,6 +33,15 @@ // foo[10].Method(); // Foo::Method on the 10th element. // } // +// Scopers are testable as booleans: +// { +// scoped_ptr foo; +// if (!foo) +// foo.reset(new Foo()); +// if (foo) +// LOG(INFO) << "This code is reached." +// } +// // These scopers also implement part of the functionality of C++11 unique_ptr // in that they are "movable but not copyable." You can use the scopers in // the parameter and return types of functions to signify ownership transfer @@ -81,7 +90,6 @@ // This is an implementation designed to match the anticipated future TR2 // implementation of the scoped_ptr class. -#include #include #include @@ -91,17 +99,14 @@ #include #include "base/compiler_specific.h" +#include "base/logging.h" #include "base/macros.h" #include "base/move.h" #include "base/template_util.h" +#include "build/build_config.h" namespace base { -namespace subtle { -class RefCountedBase; -class RefCountedThreadSafeBase; -} // namespace subtle - // Function object which invokes 'free' on its parameter, which must be // a pointer. Can be used to store malloc-allocated pointers in scoped_ptr: // @@ -113,6 +118,63 @@ struct FreeDeleter { } }; +} // namespace base + +// Now that scoped_ptr is almost 100% compatible with std::unique_ptr, we're +// incrementally migrating scoped_ptr to just be a type alias for +// std::unique_ptr. The eventual goal is to delete scoped_ptr altogether. +#if defined(OS_LINUX) +template > +using scoped_ptr = std::unique_ptr; + +// Versions of libstdc++ 4.8 lack overloads for <, <=, >, and >= when comparing +// a std::unique_ptr and nullptr_t. +#if defined(__GLIBCXX__) && __GLIBCXX__ < 20150426 +template +bool operator<(const scoped_ptr& p, std::nullptr_t) { + return p.get() < nullptr; +} +template +bool operator<(std::nullptr_t, const scoped_ptr& p) { + return nullptr < p.get(); +} + +template +bool operator>(const scoped_ptr& p, std::nullptr_t) { + return nullptr < p; +} +template +bool operator>(std::nullptr_t, const scoped_ptr& p) { + return p < nullptr; +} + +template +bool operator<=(const scoped_ptr& p, std::nullptr_t) { + return !(p > nullptr); +} +template +bool operator<=(std::nullptr_t, const scoped_ptr& p) { + return !(nullptr > p); +} + +template +bool operator>=(const scoped_ptr& p, std::nullptr_t) { + return !(p < nullptr); +} +template +bool operator>=(std::nullptr_t, const scoped_ptr& p) { + return !(nullptr < p); +} +#endif // defined(__GLIBCXX__) && __GLIBCX__ < 20150426 + +#else +namespace base { + +namespace subtle { +class RefCountedBase; +class RefCountedThreadSafeBase; +} // namespace subtle + namespace internal { template struct IsNotRefCounted { @@ -350,13 +412,13 @@ class scoped_ptr { void reset(element_type* p = nullptr) { impl_.reset(p); } // Accessors to get the owned object. - // operator* and operator-> will assert() if there is no current object. + // operator* and operator-> will DCHECK() if there is no current object. element_type& operator*() const { - assert(impl_.get() != nullptr); + DCHECK(impl_.get() != nullptr); return *impl_.get(); } element_type* operator->() const { - assert(impl_.get() != nullptr); + DCHECK(impl_.get() != nullptr); return impl_.get(); } element_type* get() const { return impl_.get(); } @@ -365,17 +427,27 @@ class scoped_ptr { deleter_type& get_deleter() { return impl_.get_deleter(); } const deleter_type& get_deleter() const { return impl_.get_deleter(); } - // Allow scoped_ptr to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). + // Implement "Safe Bool Idiom" + // https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Safe_bool + // + // Allow scoped_ptr to be used in boolean expressions such as + // if (scoped_ptr_instance) + // But do not become convertible to a real bool (which is dangerous). + // Implementation requires: + // typedef Testable + // operator Testable() const + // operator== + // operator!= // - // Note that this trick is only safe when the == and != operators - // are declared explicitly, as otherwise "scoped_ptr1 == - // scoped_ptr2" will compile but do the wrong thing (i.e., convert + // == and != operators must be declared explicitly or dissallowed, as + // otherwise "ptr1 == ptr2" will compile but do the wrong thing (i.e., convert // to Testable and then do the comparison). + // + // C++11 provides for "explicit operator bool()", however it is currently + // banned due to MSVS2013. https://chromium-cpp.appspot.com/#core-blacklist private: typedef base::internal::scoped_ptr_impl scoped_ptr::*Testable; - public: operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : nullptr; @@ -453,7 +525,7 @@ class scoped_ptr { // Accessors to get the owned array. element_type& operator[](size_t i) const { - assert(impl_.get() != nullptr); + DCHECK(impl_.get() != nullptr); return impl_.get()[i]; } element_type* get() const { return impl_.get(); } @@ -591,6 +663,12 @@ bool operator>=(std::nullptr_t, const scoped_ptr& p) { return !(nullptr < p); } +template +std::ostream& operator<<(std::ostream& out, const scoped_ptr& p) { + return out << p.get(); +} +#endif // defined(OS_LINUX) + // A function to convert T* into scoped_ptr // Doing e.g. make_scoped_ptr(new FooBarBaz(arg)) is a shorter notation // for scoped_ptr >(new FooBarBaz(arg)) @@ -599,9 +677,4 @@ scoped_ptr make_scoped_ptr(T* ptr) { return scoped_ptr(ptr); } -template -std::ostream& operator<<(std::ostream& out, const scoped_ptr& p) { - return out << p.get(); -} - #endif // BASE_MEMORY_SCOPED_PTR_H_ diff --git a/src/base/memory/shared_memory.h b/src/base/memory/shared_memory.h new file mode 100644 index 00000000..13238aab --- /dev/null +++ b/src/base/memory/shared_memory.h @@ -0,0 +1,303 @@ +// 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 BASE_MEMORY_SHARED_MEMORY_H_ +#define BASE_MEMORY_SHARED_MEMORY_H_ + +#include + +#include + +#include "base/base_export.h" +#include "base/macros.h" +#include "base/memory/shared_memory_handle.h" +#include "base/process/process_handle.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include +#include +#include +#include "base/file_descriptor_posix.h" +#include "base/files/file_util.h" +#include "base/files/scoped_file.h" +#endif + +namespace base { + +class FilePath; + +// Options for creating a shared memory object. +struct BASE_EXPORT SharedMemoryCreateOptions { + SharedMemoryCreateOptions(); + +#if defined(OS_MACOSX) && !defined(OS_IOS) + // The type of OS primitive that should back the SharedMemory object. + SharedMemoryHandle::Type type; +#else + // DEPRECATED (crbug.com/345734): + // If NULL, the object is anonymous. This pointer is owned by the caller + // and must live through the call to Create(). + const std::string* name_deprecated; + + // DEPRECATED (crbug.com/345734): + // If true, and the shared memory already exists, Create() will open the + // existing shared memory and ignore the size parameter. If false, + // shared memory must not exist. This flag is meaningless unless + // name_deprecated is non-NULL. + bool open_existing_deprecated; +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + + // Size of the shared memory object to be created. + // When opening an existing object, this has no effect. + size_t size; + + // If true, mappings might need to be made executable later. + bool executable; + + // If true, the file can be shared read-only to a process. + bool share_read_only; +}; + +// Platform abstraction for shared memory. Provides a C++ wrapper +// around the OS primitive for a memory mapped file. +class BASE_EXPORT SharedMemory { + public: + SharedMemory(); + +#if defined(OS_WIN) + // Similar to the default constructor, except that this allows for + // calling LockDeprecated() to acquire the named mutex before either Create or + // Open are called on Windows. + explicit SharedMemory(const std::wstring& name); +#endif + + // Create a new SharedMemory object from an existing, open + // shared memory file. + // + // WARNING: This does not reduce the OS-level permissions on the handle; it + // only affects how the SharedMemory will be mmapped. Use + // ShareReadOnlyToProcess to drop permissions. TODO(jln,jyasskin): DCHECK + // that |read_only| matches the permissions of the handle. + SharedMemory(const SharedMemoryHandle& handle, bool read_only); + + // Closes any open files. + ~SharedMemory(); + + // Return true iff the given handle is valid (i.e. not the distingished + // invalid value; NULL for a HANDLE and -1 for a file descriptor) + static bool IsHandleValid(const SharedMemoryHandle& handle); + + // Returns invalid handle (see comment above for exact definition). + static SharedMemoryHandle NULLHandle(); + + // Closes a shared memory handle. + static void CloseHandle(const SharedMemoryHandle& handle); + + // Returns the maximum number of handles that can be open at once per process. + static size_t GetHandleLimit(); + + // Duplicates The underlying OS primitive. Returns NULLHandle() on failure. + // The caller is responsible for destroying the duplicated OS primitive. + static SharedMemoryHandle DuplicateHandle(const SharedMemoryHandle& handle); + +#if defined(OS_POSIX) + // This method requires that the SharedMemoryHandle is backed by a POSIX fd. + static int GetFdFromSharedMemoryHandle(const SharedMemoryHandle& handle); +#endif + +#if defined(OS_POSIX) && !defined(OS_ANDROID) + // Gets the size of the shared memory region referred to by |handle|. + // Returns false on a failure to determine the size. On success, populates the + // output variable |size|. + static bool GetSizeFromSharedMemoryHandle(const SharedMemoryHandle& handle, + size_t* size); +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) + + // Creates a shared memory object as described by the options struct. + // Returns true on success and false on failure. + bool Create(const SharedMemoryCreateOptions& options); + + // Creates and maps an anonymous shared memory segment of size size. + // Returns true on success and false on failure. + bool CreateAndMapAnonymous(size_t size); + +#if defined(OS_MACOSX) && !defined(OS_IOS) + // These two methods are analogs of CreateAndMapAnonymous and CreateAnonymous + // that force the underlying OS primitive to be a POSIX fd. Do not add new + // uses of these methods unless absolutely necessary, since constructing a + // fd-backed SharedMemory object frequently takes 100ms+. + // http://crbug.com/466437. + bool CreateAndMapAnonymousPosix(size_t size); + bool CreateAnonymousPosix(size_t size); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + + // Creates an anonymous shared memory segment of size size. + // Returns true on success and false on failure. + bool CreateAnonymous(size_t size) { + SharedMemoryCreateOptions options; + options.size = size; + return Create(options); + } + +#if !defined(OS_MACOSX) || defined(OS_IOS) + // DEPRECATED (crbug.com/345734): + // Creates or opens a shared memory segment based on a name. + // If open_existing is true, and the shared memory already exists, + // opens the existing shared memory and ignores the size parameter. + // If open_existing is false, shared memory must not exist. + // size is the size of the block to be created. + // Returns true on success, false on failure. + bool CreateNamedDeprecated( + const std::string& name, bool open_existing, size_t size) { + SharedMemoryCreateOptions options; + options.name_deprecated = &name; + options.open_existing_deprecated = open_existing; + options.size = size; + return Create(options); + } + + // Deletes resources associated with a shared memory segment based on name. + // Not all platforms require this call. + bool Delete(const std::string& name); + + // Opens a shared memory segment based on a name. + // If read_only is true, opens for read-only access. + // Returns true on success, false on failure. + bool Open(const std::string& name, bool read_only); +#endif // !defined(OS_MACOSX) || defined(OS_IOS) + + // Maps the shared memory into the caller's address space. + // Returns true on success, false otherwise. The memory address + // is accessed via the memory() accessor. The mapped address is guaranteed to + // have an alignment of at least MAP_MINIMUM_ALIGNMENT. This method will fail + // if this object is currently mapped. + bool Map(size_t bytes) { + return MapAt(0, bytes); + } + + // Same as above, but with |offset| to specify from begining of the shared + // memory block to map. + // |offset| must be alignent to value of |SysInfo::VMAllocationGranularity()|. + bool MapAt(off_t offset, size_t bytes); + enum { MAP_MINIMUM_ALIGNMENT = 32 }; + + // Unmaps the shared memory from the caller's address space. + // Returns true if successful; returns false on error or if the + // memory is not mapped. + bool Unmap(); + + // The size requested when the map is first created. + size_t requested_size() const { return requested_size_; } + + // The actual size of the mapped memory (may be larger than requested). + size_t mapped_size() const { return mapped_size_; } + + // Gets a pointer to the opened memory space if it has been + // Mapped via Map(). Returns NULL if it is not mapped. + void* memory() const { return memory_; } + + // Returns the underlying OS handle for this segment. + // Use of this handle for anything other than an opaque + // identifier is not portable. + SharedMemoryHandle handle() const; + + // Closes the open shared memory segment. The memory will remain mapped if + // it was previously mapped. + // It is safe to call Close repeatedly. + void Close(); + + // Shares the shared memory to another process. Attempts to create a + // platform-specific new_handle which can be used in a remote process to read + // the shared memory file. new_handle is an output parameter to receive the + // handle for use in the remote process. + // + // |*this| must have been initialized using one of the Create*() or Open() + // methods with share_read_only=true. If it was constructed from a + // SharedMemoryHandle, this call will CHECK-fail. + // + // Returns true on success, false otherwise. + bool ShareReadOnlyToProcess(ProcessHandle process, + SharedMemoryHandle* new_handle) { + return ShareToProcessCommon(process, new_handle, false, SHARE_READONLY); + } + + // Logically equivalent to: + // bool ok = ShareReadOnlyToProcess(process, new_handle); + // Close(); + // return ok; + // Note that the memory is unmapped by calling this method, regardless of the + // return value. + bool GiveReadOnlyToProcess(ProcessHandle process, + SharedMemoryHandle* new_handle) { + return ShareToProcessCommon(process, new_handle, true, SHARE_READONLY); + } + + // Shares the shared memory to another process. Attempts + // to create a platform-specific new_handle which can be + // used in a remote process to access the shared memory + // file. new_handle is an output parameter to receive + // the handle for use in the remote process. + // Returns true on success, false otherwise. + bool ShareToProcess(ProcessHandle process, + SharedMemoryHandle* new_handle) { + return ShareToProcessCommon(process, new_handle, false, SHARE_CURRENT_MODE); + } + + // Logically equivalent to: + // bool ok = ShareToProcess(process, new_handle); + // Close(); + // return ok; + // Note that the memory is unmapped by calling this method, regardless of the + // return value. + bool GiveToProcess(ProcessHandle process, + SharedMemoryHandle* new_handle) { + return ShareToProcessCommon(process, new_handle, true, SHARE_CURRENT_MODE); + } + + private: +#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID) + bool PrepareMapFile(ScopedFILE fp, ScopedFD readonly); +#if !(defined(OS_MACOSX) && !defined(OS_IOS)) + bool FilePathForMemoryName(const std::string& mem_name, FilePath* path); +#endif +#endif // defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID) + enum ShareMode { + SHARE_READONLY, + SHARE_CURRENT_MODE, + }; + bool ShareToProcessCommon(ProcessHandle process, + SharedMemoryHandle* new_handle, + bool close_self, + ShareMode); + +#if defined(OS_WIN) + // If true indicates this came from an external source so needs extra checks + // before being mapped. + bool external_section_; + std::wstring name_; + HANDLE mapped_file_; +#elif defined(OS_MACOSX) && !defined(OS_IOS) + // The OS primitive that backs the shared memory region. + SharedMemoryHandle shm_; + + // The mechanism by which the memory is mapped. Only valid if |memory_| is not + // |nullptr|. + SharedMemoryHandle::Type mapped_memory_mechanism_; + + int readonly_mapped_file_; +#elif defined(OS_POSIX) + int mapped_file_; + int readonly_mapped_file_; +#endif + size_t mapped_size_; + void* memory_; + bool read_only_; + size_t requested_size_; + + DISALLOW_COPY_AND_ASSIGN(SharedMemory); +}; +} // namespace base + +#endif // BASE_MEMORY_SHARED_MEMORY_H_ diff --git a/src/base/memory/shared_memory_handle.h b/src/base/memory/shared_memory_handle.h new file mode 100644 index 00000000..5befcdd6 --- /dev/null +++ b/src/base/memory/shared_memory_handle.h @@ -0,0 +1,211 @@ +// Copyright 2015 The Chromium 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 BASE_MEMORY_SHARED_MEMORY_HANDLE_H_ +#define BASE_MEMORY_SHARED_MEMORY_HANDLE_H_ + +#include + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#include "base/process/process_handle.h" +#elif defined(OS_MACOSX) && !defined(OS_IOS) +#include +#include +#include "base/base_export.h" +#include "base/file_descriptor_posix.h" +#include "base/macros.h" +#include "base/process/process_handle.h" +#elif defined(OS_POSIX) +#include +#include "base/file_descriptor_posix.h" +#endif + +namespace base { + +class Pickle; + +// SharedMemoryHandle is a platform specific type which represents +// the underlying OS handle to a shared memory segment. +#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS)) +typedef FileDescriptor SharedMemoryHandle; +#elif defined(OS_WIN) +class BASE_EXPORT SharedMemoryHandle { + public: + // The default constructor returns an invalid SharedMemoryHandle. + SharedMemoryHandle(); + SharedMemoryHandle(HANDLE h, base::ProcessId pid); + + // Standard copy constructor. The new instance shares the underlying OS + // primitives. + SharedMemoryHandle(const SharedMemoryHandle& handle); + + // Standard assignment operator. The updated instance shares the underlying + // OS primitives. + SharedMemoryHandle& operator=(const SharedMemoryHandle& handle); + + // Comparison operators. + bool operator==(const SharedMemoryHandle& handle) const; + bool operator!=(const SharedMemoryHandle& handle) const; + + // Closes the underlying OS resources. + void Close() const; + + // Whether the underlying OS primitive is valid. + bool IsValid() const; + + // Whether |pid_| is the same as the current process's id. + bool BelongsToCurrentProcess() const; + + // Whether handle_ needs to be duplicated into the destination process when + // an instance of this class is passed over a Chrome IPC channel. + bool NeedsBrokering() const; + + void SetOwnershipPassesToIPC(bool ownership_passes); + bool OwnershipPassesToIPC() const; + + HANDLE GetHandle() const; + base::ProcessId GetPID() const; + + private: + HANDLE handle_; + + // The process in which |handle_| is valid and can be used. If |handle_| is + // invalid, this will be kNullProcessId. + base::ProcessId pid_; + + // Whether passing this object as a parameter to an IPC message passes + // ownership of |handle_| to the IPC stack. This is meant to mimic the + // behavior of the |auto_close| parameter of FileDescriptor. This member only + // affects attachment-brokered SharedMemoryHandles. + // Defaults to |false|. + bool ownership_passes_to_ipc_; +}; +#else +class BASE_EXPORT SharedMemoryHandle { + public: + // The values of these enums must not change, as they are used by the + // histogram OSX.SharedMemory.Mechanism. + enum Type { + // The SharedMemoryHandle is backed by a POSIX fd. + POSIX, + // The SharedMemoryHandle is backed by the Mach primitive "memory object". + MACH, + }; + static const int TypeMax = 2; + + // The format that should be used to transmit |Type| over the wire. + typedef int TypeWireFormat; + + // The default constructor returns an invalid SharedMemoryHandle. + SharedMemoryHandle(); + + // Constructs a SharedMemoryHandle backed by the components of a + // FileDescriptor. The newly created instance has the same ownership semantics + // as base::FileDescriptor. This typically means that the SharedMemoryHandle + // takes ownership of the |fd| if |auto_close| is true. Unfortunately, it's + // common for existing code to make shallow copies of SharedMemoryHandle, and + // the one that is finally passed into a base::SharedMemory is the one that + // "consumes" the fd. + explicit SharedMemoryHandle(const base::FileDescriptor& file_descriptor); + SharedMemoryHandle(int fd, bool auto_close); + + // Makes a Mach-based SharedMemoryHandle of the given size. On error, + // subsequent calls to IsValid() return false. + explicit SharedMemoryHandle(mach_vm_size_t size); + + // Makes a Mach-based SharedMemoryHandle from |memory_object|, a named entry + // in the task with process id |pid|. The memory region has size |size|. + SharedMemoryHandle(mach_port_t memory_object, + mach_vm_size_t size, + base::ProcessId pid); + + // Standard copy constructor. The new instance shares the underlying OS + // primitives. + SharedMemoryHandle(const SharedMemoryHandle& handle); + + // Standard assignment operator. The updated instance shares the underlying + // OS primitives. + SharedMemoryHandle& operator=(const SharedMemoryHandle& handle); + + // Duplicates the underlying OS resources. + SharedMemoryHandle Duplicate() const; + + // Comparison operators. + bool operator==(const SharedMemoryHandle& handle) const; + bool operator!=(const SharedMemoryHandle& handle) const; + + // Returns the type. + Type GetType() const; + + // Whether the underlying OS primitive is valid. Once the SharedMemoryHandle + // is backed by a valid OS primitive, it becomes immutable. + bool IsValid() const; + + // Sets the POSIX fd backing the SharedMemoryHandle. Requires that the + // SharedMemoryHandle be backed by a POSIX fd. + void SetFileHandle(int fd, bool auto_close); + + // This method assumes that the SharedMemoryHandle is backed by a POSIX fd. + // This is eventually no longer going to be true, so please avoid adding new + // uses of this method. + const FileDescriptor GetFileDescriptor() const; + + // Exposed so that the SharedMemoryHandle can be transported between + // processes. + mach_port_t GetMemoryObject() const; + + // Returns false on a failure to determine the size. On success, populates the + // output variable |size|. + bool GetSize(size_t* size) const; + + // The SharedMemoryHandle must be valid. + // Returns whether the SharedMemoryHandle was successfully mapped into memory. + // On success, |memory| is an output variable that contains the start of the + // mapped memory. + bool MapAt(off_t offset, size_t bytes, void** memory, bool read_only); + + // Closes the underlying OS primitive. + void Close() const; + + void SetOwnershipPassesToIPC(bool ownership_passes); + bool OwnershipPassesToIPC() const; + + private: + // Shared code between copy constructor and operator=. + void CopyRelevantData(const SharedMemoryHandle& handle); + + Type type_; + + // Each instance of a SharedMemoryHandle is backed either by a POSIX fd or a + // mach port. |type_| determines the backing member. + union { + FileDescriptor file_descriptor_; + + struct { + mach_port_t memory_object_; + + // The size of the shared memory region when |type_| is MACH. Only + // relevant if |memory_object_| is not |MACH_PORT_NULL|. + mach_vm_size_t size_; + + // The pid of the process in which |memory_object_| is usable. Only + // relevant if |memory_object_| is not |MACH_PORT_NULL|. + base::ProcessId pid_; + + // Whether passing this object as a parameter to an IPC message passes + // ownership of |memory_object_| to the IPC stack. This is meant to mimic + // the behavior of the |auto_close| parameter of FileDescriptor. + // Defaults to |false|. + bool ownership_passes_to_ipc_; + }; + }; +}; +#endif + +} // namespace base + +#endif // BASE_MEMORY_SHARED_MEMORY_HANDLE_H_ diff --git a/src/base/memory/weak_ptr.cc b/src/base/memory/weak_ptr.cc index d9ce86ad..16d3dff1 100644 --- a/src/base/memory/weak_ptr.cc +++ b/src/base/memory/weak_ptr.cc @@ -34,6 +34,8 @@ WeakReference::Flag::~Flag() { WeakReference::WeakReference() { } +WeakReference::WeakReference(const WeakReference& other) = default; + WeakReference::WeakReference(const Flag* flag) : flag_(flag) { } diff --git a/src/base/memory/weak_ptr.h b/src/base/memory/weak_ptr.h index 33d1e473..4e50c277 100644 --- a/src/base/memory/weak_ptr.h +++ b/src/base/memory/weak_ptr.h @@ -107,6 +107,7 @@ class BASE_EXPORT WeakReference { }; WeakReference(); + WeakReference(const WeakReference& other); explicit WeakReference(const Flag* flag); ~WeakReference(); @@ -218,27 +219,38 @@ class WeakPtr : public internal::WeakPtrBase { return get(); } - // Allow WeakPtr to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). + void reset() { + ref_ = internal::WeakReference(); + ptr_ = NULL; + } + + // Implement "Safe Bool Idiom" + // https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Safe_bool + // + // Allow WeakPtr to be used in boolean expressions such as + // if (weak_ptr_instance) + // But do not become convertible to a real bool (which is dangerous). + // Implementation requires: + // typedef Testable + // operator Testable() const + // operator== + // operator!= // - // Note that this trick is only safe when the == and != operators - // are declared explicitly, as otherwise "weak_ptr1 == weak_ptr2" - // will compile but do the wrong thing (i.e., convert to Testable - // and then do the comparison). + // == and != operators must be declared explicitly or dissallowed, as + // otherwise "ptr1 == ptr2" will compile but do the wrong thing (i.e., convert + // to Testable and then do the comparison). + // + // C++11 provides for "explicit operator bool()", however it is currently + // banned due to MSVS2013. https://chromium-cpp.appspot.com/#core-blacklist private: typedef T* WeakPtr::*Testable; public: operator Testable() const { return get() ? &WeakPtr::ptr_ : NULL; } - void reset() { - ref_ = internal::WeakReference(); - ptr_ = NULL; - } - private: - // Explicitly declare comparison operators as required by the bool - // trick, but keep them private. + // Explicitly declare comparison operators as required by the "Safe Bool + // Idiom", but keep them private. template bool operator==(WeakPtr const&) const; template bool operator!=(WeakPtr const&) const; diff --git a/src/base/metrics/histogram.cc b/src/base/metrics/histogram.cc index 62c2bc80..e85c217f 100644 --- a/src/base/metrics/histogram.cc +++ b/src/base/metrics/histogram.cc @@ -19,7 +19,9 @@ #include "base/debug/alias.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" +#include "base/metrics/histogram_persistence.h" #include "base/metrics/metrics_hashes.h" +#include "base/metrics/persistent_memory_allocator.h" #include "base/metrics/sample_vector.h" #include "base/metrics/statistics_recorder.h" #include "base/pickle.h" @@ -37,13 +39,13 @@ bool ReadHistogramArguments(PickleIterator* iter, int* flags, int* declared_min, int* declared_max, - size_t* bucket_count, + uint32_t* bucket_count, uint32_t* range_checksum) { if (!iter->ReadString(histogram_name) || !iter->ReadInt(flags) || !iter->ReadInt(declared_min) || !iter->ReadInt(declared_max) || - !iter->ReadSizeT(bucket_count) || + !iter->ReadUInt32(bucket_count) || !iter->ReadUInt32(range_checksum)) { DLOG(ERROR) << "Pickle error decoding Histogram: " << *histogram_name; return false; @@ -82,51 +84,209 @@ typedef HistogramBase::Count Count; typedef HistogramBase::Sample Sample; // static -const size_t Histogram::kBucketCount_MAX = 16384u; +const uint32_t Histogram::kBucketCount_MAX = 16384u; + +class Histogram::Factory { + public: + Factory(const std::string& name, + HistogramBase::Sample minimum, + HistogramBase::Sample maximum, + uint32_t bucket_count, + int32_t flags) + : Factory(name, HISTOGRAM, minimum, maximum, bucket_count, flags) {} + + // Create histogram based on construction parameters. Caller takes + // ownership of the returned object. + HistogramBase* Build(); + + protected: + Factory(const std::string& name, + HistogramType histogram_type, + HistogramBase::Sample minimum, + HistogramBase::Sample maximum, + uint32_t bucket_count, + int32_t flags) + : name_(name), + histogram_type_(histogram_type), + minimum_(minimum), + maximum_(maximum), + bucket_count_(bucket_count), + flags_(flags) {} + + // Create a BucketRanges structure appropriate for this histogram. + virtual BucketRanges* CreateRanges() { + BucketRanges* ranges = new BucketRanges(bucket_count_ + 1); + Histogram::InitializeBucketRanges(minimum_, maximum_, ranges); + return ranges; + } -HistogramBase* Histogram::FactoryGet(const std::string& name, - Sample minimum, - Sample maximum, - size_t bucket_count, - int32_t flags) { - bool valid_arguments = - InspectConstructionArguments(name, &minimum, &maximum, &bucket_count); - DCHECK(valid_arguments); + // Allocate the correct Histogram object off the heap (in case persistent + // memory is not available). + virtual HistogramBase* HeapAlloc(const BucketRanges* ranges) { + return new Histogram(name_, minimum_, maximum_, ranges); + } + + // Perform any required datafill on the just-created histogram. If + // overridden, be sure to call the "super" version. + virtual void FillHistogram(HistogramBase* histogram) { + histogram->SetFlags(flags_); + } + + // These values are protected (instead of private) because they need to + // be accessible to methods of sub-classes in order to avoid passing + // unnecessary parameters everywhere. + const std::string& name_; + const HistogramType histogram_type_; + HistogramBase::Sample minimum_; + HistogramBase::Sample maximum_; + uint32_t bucket_count_; + int32_t flags_; + + private: + DISALLOW_COPY_AND_ASSIGN(Factory); +}; + +HistogramBase* Histogram::Factory::Build() { + // Import histograms from known persistent storage. Histograms could have + // been added by other processes and they must be fetched and recognized + // locally in order to be found by FindHistograms() below. If the persistent + // memory segment is not shared between processes, this call does nothing. + ImportPersistentHistograms(); + + HistogramBase* histogram = StatisticsRecorder::FindHistogram(name_); + + // crbug.com/588946 debugging. See comment at end of function. + const BucketRanges* created_ranges = + reinterpret_cast(0xDEADBEEF); + const BucketRanges* registered_ranges = + reinterpret_cast(0xDEADBEEF); + HistogramBase* tentative_histogram = + reinterpret_cast(0xDEADBEEF); + PersistentMemoryAllocator* allocator = + reinterpret_cast(0xDEADBEEF); + PersistentMemoryAllocator::Reference histogram_ref = 0xDEADBEEF; + bool bad_args = false; - HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); if (!histogram) { // To avoid racy destruction at shutdown, the following will be leaked. - BucketRanges* ranges = new BucketRanges(bucket_count + 1); - InitializeBucketRanges(minimum, maximum, ranges); - const BucketRanges* registered_ranges = - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); + created_ranges = CreateRanges(); + registered_ranges = + StatisticsRecorder::RegisterOrDeleteDuplicateRanges(created_ranges); + + // In most cases, the bucket-count, minimum, and maximum values are known + // when the code is written and so are passed in explicitly. In other + // cases (such as with a CustomHistogram), they are calculated dynamically + // at run-time. In the latter case, those ctor parameters are zero and + // the results extracted from the result of CreateRanges(). + if (bucket_count_ == 0) { + bucket_count_ = static_cast(registered_ranges->bucket_count()); + minimum_ = registered_ranges->range(1); + maximum_ = registered_ranges->range(bucket_count_ - 1); + } - Histogram* tentative_histogram = - new Histogram(name, minimum, maximum, registered_ranges); + // Try to create the histogram using a "persistent" allocator. As of + // 2015-01-14, the availability of such is controlled by a base::Feature + // that is off by default. If the allocator doesn't exist or if + // allocating from it fails, code below will allocate the histogram from + // the process heap. + histogram_ref = 0; + tentative_histogram = nullptr; + allocator = + GetPersistentHistogramMemoryAllocator(); + if (allocator) { + flags_ |= HistogramBase::kIsPersistent; + tentative_histogram = AllocatePersistentHistogram( + allocator, + histogram_type_, + name_, + minimum_, + maximum_, + registered_ranges, + flags_, + &histogram_ref); + } - tentative_histogram->SetFlags(flags); + // Handle the case where no persistent allocator is present or the + // persistent allocation fails (perhaps because it is full). + if (!tentative_histogram) { + DCHECK(!histogram_ref); // Should never have been set. + DCHECK(!allocator); // Shouldn't have failed. + flags_ &= ~HistogramBase::kIsPersistent; + tentative_histogram = HeapAlloc(registered_ranges); + } + + FillHistogram(tentative_histogram); histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); + + // Persistent histograms need some follow-up processing. + if (histogram_ref) { + FinalizePersistentHistogram(histogram_ref, + histogram == tentative_histogram); + } } - DCHECK_EQ(HISTOGRAM, histogram->GetHistogramType()); - if (!histogram->HasConstructionArguments(minimum, maximum, bucket_count)) { + DCHECK_EQ(histogram_type_, histogram->GetHistogramType()); + if (bucket_count_ != 0 && + !histogram->HasConstructionArguments(minimum_, maximum_, bucket_count_)) { // The construction arguments do not match the existing histogram. This can // come about if an extension updates in the middle of a chrome run and has // changed one of them, or simply by bad code within Chrome itself. We // return NULL here with the expectation that bad code in Chrome will crash // on dereference, but extension/Pepper APIs will guard against NULL and not // crash. - DLOG(ERROR) << "Histogram " << name << " has bad construction arguments"; - return NULL; + DLOG(ERROR) << "Histogram " << name_ << " has bad construction arguments"; + bad_args = true; + histogram = nullptr; } + +#if !DCHECK_IS_ON() // Don't affect tests, only release builds. + // For the moment, crash here so that collected crash reports have access + // to the construction values in order to figure out why this is failing. + // TODO(bcwhite): Remove this once crbug.com/588946 is resolved. Also remove + // from beta-branch because we don't want crashes due to misbehaving + // extensions (see comment above). + if (!histogram) { + HistogramType histogram_type = histogram_type_; + HistogramBase::Sample minimum = minimum_; + HistogramBase::Sample maximum = maximum_; + uint32_t bucket_count = bucket_count_; + int32_t flags = flags_; + CHECK(histogram) << name_ << ": bad-args=" << bad_args; + base::debug::Alias(&histogram_type); + base::debug::Alias(&minimum); + base::debug::Alias(&maximum); + base::debug::Alias(&bucket_count); + base::debug::Alias(&flags); + base::debug::Alias(&created_ranges); + base::debug::Alias(®istered_ranges); + base::debug::Alias(&histogram_ref); + base::debug::Alias(&tentative_histogram); + base::debug::Alias(&allocator); + base::debug::Alias(&tentative_histogram); + } +#endif + + base::debug::Alias(&bad_args); // Down here so var is always "used". return histogram; } +HistogramBase* Histogram::FactoryGet(const std::string& name, + Sample minimum, + Sample maximum, + uint32_t bucket_count, + int32_t flags) { + bool valid_arguments = + InspectConstructionArguments(name, &minimum, &maximum, &bucket_count); + DCHECK(valid_arguments); + + return Factory(name, minimum, maximum, bucket_count, flags).Build(); +} + HistogramBase* Histogram::FactoryTimeGet(const std::string& name, TimeDelta minimum, TimeDelta maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags) { return FactoryGet(name, static_cast(minimum.InMilliseconds()), static_cast(maximum.InMilliseconds()), bucket_count, @@ -136,7 +296,7 @@ HistogramBase* Histogram::FactoryTimeGet(const std::string& name, HistogramBase* Histogram::FactoryGet(const char* name, Sample minimum, Sample maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags) { return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags); } @@ -144,12 +304,26 @@ HistogramBase* Histogram::FactoryGet(const char* name, HistogramBase* Histogram::FactoryTimeGet(const char* name, TimeDelta minimum, TimeDelta maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags) { return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count, flags); } +HistogramBase* Histogram::PersistentGet( + const std::string& name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) { + return new Histogram(name, minimum, maximum, ranges, counts, logged_counts, + counts_size, meta, logged_meta); +} + // Calculate what range of values are held in each bucket. // We have to be careful that we don't pick a ratio between starting points in // consecutive buckets that is sooo small, that the integer bounds are the same @@ -192,10 +366,10 @@ void Histogram::InitializeBucketRanges(Sample minimum, // static const int Histogram::kCommonRaceBasedCountMismatch = 5; -int Histogram::FindCorruption(const HistogramSamples& samples) const { +uint32_t Histogram::FindCorruption(const HistogramSamples& samples) const { int inconsistencies = NO_INCONSISTENCIES; Sample previous_range = -1; // Bottom range is always 0. - for (size_t index = 0; index < bucket_count(); ++index) { + for (uint32_t index = 0; index < bucket_count(); ++index) { int new_range = ranges(index); if (previous_range >= new_range) inconsistencies |= BUCKET_ORDER_ERROR; @@ -224,19 +398,19 @@ int Histogram::FindCorruption(const HistogramSamples& samples) const { return inconsistencies; } -Sample Histogram::ranges(size_t i) const { +Sample Histogram::ranges(uint32_t i) const { return bucket_ranges_->range(i); } -size_t Histogram::bucket_count() const { - return bucket_ranges_->bucket_count(); +uint32_t Histogram::bucket_count() const { + return static_cast(bucket_ranges_->bucket_count()); } // static bool Histogram::InspectConstructionArguments(const std::string& name, Sample* minimum, Sample* maximum, - size_t* bucket_count) { + uint32_t* bucket_count) { // Defensive code for backward compatibility. if (*minimum < 1) { DVLOG(1) << "Histogram: " << name << " has bad minimum: " << *minimum; @@ -256,7 +430,7 @@ bool Histogram::InspectConstructionArguments(const std::string& name, return false; if (*bucket_count < 3) return false; - if (*bucket_count > static_cast(*maximum - *minimum + 2)) + if (*bucket_count > static_cast(*maximum - *minimum + 2)) return false; return true; } @@ -271,7 +445,7 @@ HistogramType Histogram::GetHistogramType() const { bool Histogram::HasConstructionArguments(Sample expected_minimum, Sample expected_maximum, - size_t expected_bucket_count) const { + uint32_t expected_bucket_count) const { return ((expected_minimum == declared_min_) && (expected_maximum == declared_max_) && (expected_bucket_count == bucket_count())); @@ -302,6 +476,21 @@ scoped_ptr Histogram::SnapshotSamples() const { return SnapshotSampleVector(); } +scoped_ptr Histogram::SnapshotDelta() { + scoped_ptr snapshot = SnapshotSampleVector(); + if (!logged_samples_) { + // If nothing has been previously logged, save this one as + // |logged_samples_| and gather another snapshot to return. + logged_samples_.swap(snapshot); + return SnapshotSampleVector(); + } + + // Subtract what was previously logged and update that information. + snapshot->Subtract(*logged_samples_); + logged_samples_->Add(*snapshot); + return snapshot; +} + void Histogram::AddSamples(const HistogramSamples& samples) { samples_->Add(samples); } @@ -328,7 +517,7 @@ bool Histogram::SerializeInfoImpl(Pickle* pickle) const { pickle->WriteInt(flags()) && pickle->WriteInt(declared_min()) && pickle->WriteInt(declared_max()) && - pickle->WriteSizeT(bucket_count()) && + pickle->WriteUInt32(bucket_count()) && pickle->WriteUInt32(bucket_ranges()->checksum()); } @@ -344,10 +533,31 @@ Histogram::Histogram(const std::string& name, samples_.reset(new SampleVector(HashMetricName(name), ranges)); } +Histogram::Histogram(const std::string& name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) + : HistogramBase(name), + bucket_ranges_(ranges), + declared_min_(minimum), + declared_max_(maximum) { + if (ranges) { + samples_.reset(new SampleVector(HashMetricName(name), + counts, counts_size, meta, ranges)); + logged_samples_.reset(new SampleVector(samples_->id(), logged_counts, + counts_size, logged_meta, ranges)); + } +} + Histogram::~Histogram() { } -bool Histogram::PrintEmptyBucket(size_t index) const { +bool Histogram::PrintEmptyBucket(uint32_t index) const { return true; } @@ -356,7 +566,7 @@ bool Histogram::PrintEmptyBucket(size_t index) const { // get so big so fast (and we don't expect to see a lot of entries in the large // buckets), so we need this to make it possible to see what is going on and // not have 0-graphical-height buckets. -double Histogram::GetBucketSize(Count current, size_t i) const { +double Histogram::GetBucketSize(Count current, uint32_t i) const { DCHECK_GT(ranges(i + 1), ranges(i)); static const double kTransitionWidth = 5; double denominator = ranges(i + 1) - ranges(i); @@ -365,7 +575,7 @@ double Histogram::GetBucketSize(Count current, size_t i) const { return current/denominator; } -const std::string Histogram::GetAsciiBucketRange(size_t i) const { +const std::string Histogram::GetAsciiBucketRange(uint32_t i) const { return GetSimpleAsciiBucketRange(ranges(i)); } @@ -378,7 +588,7 @@ HistogramBase* Histogram::DeserializeInfoImpl(PickleIterator* iter) { int flags; int declared_min; int declared_max; - size_t bucket_count; + uint32_t bucket_count; uint32_t range_checksum; if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, @@ -422,7 +632,7 @@ void Histogram::WriteAsciiImpl(bool graph_it, // Calculate space needed to print bucket range numbers. Leave room to print // nearly the largest bucket range without sliding over the histogram. - size_t largest_non_empty_bucket = bucket_count() - 1; + uint32_t largest_non_empty_bucket = bucket_count() - 1; while (0 == snapshot->GetCountAtIndex(largest_non_empty_bucket)) { if (0 == largest_non_empty_bucket) break; // All buckets are empty. @@ -431,7 +641,7 @@ void Histogram::WriteAsciiImpl(bool graph_it, // Calculate largest print width needed for any of our bucket range displays. size_t print_width = 1; - for (size_t i = 0; i < bucket_count(); ++i) { + for (uint32_t i = 0; i < bucket_count(); ++i) { if (snapshot->GetCountAtIndex(i)) { size_t width = GetAsciiBucketRange(i).size() + 1; if (width > print_width) @@ -442,7 +652,7 @@ void Histogram::WriteAsciiImpl(bool graph_it, int64_t remaining = sample_count; int64_t past = 0; // Output the actual histogram graph. - for (size_t i = 0; i < bucket_count(); ++i) { + for (uint32_t i = 0; i < bucket_count(); ++i) { Count current = snapshot->GetCountAtIndex(i); if (!current && !PrintEmptyBucket(i)) continue; @@ -473,7 +683,7 @@ void Histogram::WriteAsciiImpl(bool graph_it, double Histogram::GetPeakBucketSize(const SampleVector& samples) const { double max = 0; - for (size_t i = 0; i < bucket_count() ; ++i) { + for (uint32_t i = 0; i < bucket_count() ; ++i) { double current_size = GetBucketSize(samples.GetCountAtIndex(i), i); if (current_size > max) max = current_size; @@ -502,7 +712,7 @@ void Histogram::WriteAsciiHeader(const SampleVector& samples, void Histogram::WriteAsciiBucketContext(const int64_t past, const Count current, const int64_t remaining, - const size_t i, + const uint32_t i, std::string* output) const { double scaled_sum = (past + current + remaining) / 100.0; WriteAsciiBucketValue(current, scaled_sum, output); @@ -525,8 +735,8 @@ void Histogram::GetCountAndBucketData(Count* count, scoped_ptr snapshot = SnapshotSampleVector(); *count = snapshot->TotalCount(); *sum = snapshot->sum(); - size_t index = 0; - for (size_t i = 0; i < bucket_count(); ++i) { + uint32_t index = 0; + for (uint32_t i = 0; i < bucket_count(); ++i) { Sample count_at_index = snapshot->GetCountAtIndex(i); if (count_at_index > 0) { scoped_ptr bucket_value(new DictionaryValue()); @@ -545,12 +755,54 @@ void Histogram::GetCountAndBucketData(Count* count, // buckets. //------------------------------------------------------------------------------ +class LinearHistogram::Factory : public Histogram::Factory { + public: + Factory(const std::string& name, + HistogramBase::Sample minimum, + HistogramBase::Sample maximum, + uint32_t bucket_count, + int32_t flags, + const DescriptionPair* descriptions) + : Histogram::Factory(name, LINEAR_HISTOGRAM, minimum, maximum, + bucket_count, flags) { + descriptions_ = descriptions; + } + + protected: + BucketRanges* CreateRanges() override { + BucketRanges* ranges = new BucketRanges(bucket_count_ + 1); + LinearHistogram::InitializeBucketRanges(minimum_, maximum_, ranges); + return ranges; + } + + HistogramBase* HeapAlloc(const BucketRanges* ranges) override { + return new LinearHistogram(name_, minimum_, maximum_, ranges); + } + + void FillHistogram(HistogramBase* base_histogram) override { + Histogram::Factory::FillHistogram(base_histogram); + LinearHistogram* histogram = static_cast(base_histogram); + // Set range descriptions. + if (descriptions_) { + for (int i = 0; descriptions_[i].description; ++i) { + histogram->bucket_description_[descriptions_[i].sample] = + descriptions_[i].description; + } + } + } + + private: + const DescriptionPair* descriptions_; + + DISALLOW_COPY_AND_ASSIGN(Factory); +}; + LinearHistogram::~LinearHistogram() {} HistogramBase* LinearHistogram::FactoryGet(const std::string& name, Sample minimum, Sample maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags) { return FactoryGetWithRangeDescription( name, minimum, maximum, bucket_count, flags, NULL); @@ -559,7 +811,7 @@ HistogramBase* LinearHistogram::FactoryGet(const std::string& name, HistogramBase* LinearHistogram::FactoryTimeGet(const std::string& name, TimeDelta minimum, TimeDelta maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags) { return FactoryGet(name, static_cast(minimum.InMilliseconds()), static_cast(maximum.InMilliseconds()), bucket_count, @@ -569,7 +821,7 @@ HistogramBase* LinearHistogram::FactoryTimeGet(const std::string& name, HistogramBase* LinearHistogram::FactoryGet(const char* name, Sample minimum, Sample maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags) { return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags); } @@ -577,59 +829,39 @@ HistogramBase* LinearHistogram::FactoryGet(const char* name, HistogramBase* LinearHistogram::FactoryTimeGet(const char* name, TimeDelta minimum, TimeDelta maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags) { return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count, flags); } +HistogramBase* LinearHistogram::PersistentGet( + const std::string& name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) { + return new LinearHistogram(name, minimum, maximum, ranges, counts, + logged_counts, counts_size, meta, logged_meta); +} + HistogramBase* LinearHistogram::FactoryGetWithRangeDescription( const std::string& name, Sample minimum, Sample maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags, const DescriptionPair descriptions[]) { bool valid_arguments = Histogram::InspectConstructionArguments( name, &minimum, &maximum, &bucket_count); DCHECK(valid_arguments); - HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); - if (!histogram) { - // To avoid racy destruction at shutdown, the following will be leaked. - BucketRanges* ranges = new BucketRanges(bucket_count + 1); - InitializeBucketRanges(minimum, maximum, ranges); - const BucketRanges* registered_ranges = - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); - - LinearHistogram* tentative_histogram = - new LinearHistogram(name, minimum, maximum, registered_ranges); - - // Set range descriptions. - if (descriptions) { - for (int i = 0; descriptions[i].description; ++i) { - tentative_histogram->bucket_description_[descriptions[i].sample] = - descriptions[i].description; - } - } - - tentative_histogram->SetFlags(flags); - histogram = - StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); - } - - DCHECK_EQ(LINEAR_HISTOGRAM, histogram->GetHistogramType()); - if (!histogram->HasConstructionArguments(minimum, maximum, bucket_count)) { - // The construction arguments do not match the existing histogram. This can - // come about if an extension updates in the middle of a chrome run and has - // changed one of them, or simply by bad code within Chrome itself. We - // return NULL here with the expectation that bad code in Chrome will crash - // on dereference, but extension/Pepper APIs will guard against NULL and not - // crash. - DLOG(ERROR) << "Histogram " << name << " has bad construction arguments"; - return NULL; - } - return histogram; + return Factory(name, minimum, maximum, bucket_count, flags, descriptions) + .Build(); } HistogramType LinearHistogram::GetHistogramType() const { @@ -643,7 +875,19 @@ LinearHistogram::LinearHistogram(const std::string& name, : Histogram(name, minimum, maximum, ranges) { } -double LinearHistogram::GetBucketSize(Count current, size_t i) const { +LinearHistogram::LinearHistogram(const std::string& name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) + : Histogram(name, minimum, maximum, ranges, counts, logged_counts, + counts_size, meta, logged_meta) {} + +double LinearHistogram::GetBucketSize(Count current, uint32_t i) const { DCHECK_GT(ranges(i + 1), ranges(i)); // Adjacent buckets with different widths would have "surprisingly" many (few) // samples in a histogram if we didn't normalize this way. @@ -651,7 +895,7 @@ double LinearHistogram::GetBucketSize(Count current, size_t i) const { return current/denominator; } -const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { +const std::string LinearHistogram::GetAsciiBucketRange(uint32_t i) const { int range = ranges(i); BucketDescriptionMap::const_iterator it = bucket_description_.find(range); if (it == bucket_description_.end()) @@ -659,7 +903,7 @@ const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { return it->second; } -bool LinearHistogram::PrintEmptyBucket(size_t index) const { +bool LinearHistogram::PrintEmptyBucket(uint32_t index) const { return bucket_description_.find(ranges(index)) == bucket_description_.end(); } @@ -674,6 +918,8 @@ void LinearHistogram::InitializeBucketRanges(Sample minimum, double linear_range = (min * (bucket_count - 1 - i) + max * (i - 1)) / (bucket_count - 2); ranges->set_range(i, static_cast(linear_range + 0.5)); + // TODO(bcwhite): Remove once crbug/586622 is fixed. + base::debug::Alias(&linear_range); } ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX); ranges->ResetChecksum(); @@ -685,7 +931,7 @@ HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) { int flags; int declared_min; int declared_max; - size_t bucket_count; + uint32_t bucket_count; uint32_t range_checksum; if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, @@ -706,32 +952,46 @@ HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) { // This section provides implementation for BooleanHistogram. //------------------------------------------------------------------------------ -HistogramBase* BooleanHistogram::FactoryGet(const std::string& name, - int32_t flags) { - HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); - if (!histogram) { - // To avoid racy destruction at shutdown, the following will be leaked. - BucketRanges* ranges = new BucketRanges(4); - LinearHistogram::InitializeBucketRanges(1, 2, ranges); - const BucketRanges* registered_ranges = - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); +class BooleanHistogram::Factory : public Histogram::Factory { + public: + Factory(const std::string& name, int32_t flags) + : Histogram::Factory(name, BOOLEAN_HISTOGRAM, 1, 2, 3, flags) {} - BooleanHistogram* tentative_histogram = - new BooleanHistogram(name, registered_ranges); + protected: + BucketRanges* CreateRanges() override { + BucketRanges* ranges = new BucketRanges(3 + 1); + LinearHistogram::InitializeBucketRanges(1, 2, ranges); + return ranges; + } - tentative_histogram->SetFlags(flags); - histogram = - StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); + HistogramBase* HeapAlloc(const BucketRanges* ranges) override { + return new BooleanHistogram(name_, ranges); } - DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->GetHistogramType()); - return histogram; + private: + DISALLOW_COPY_AND_ASSIGN(Factory); +}; + +HistogramBase* BooleanHistogram::FactoryGet(const std::string& name, + int32_t flags) { + return Factory(name, flags).Build(); } HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32_t flags) { return FactoryGet(std::string(name), flags); } +HistogramBase* BooleanHistogram::PersistentGet( + const std::string& name, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) { + return new BooleanHistogram(name, ranges, counts, logged_counts, meta, + logged_meta); +} + HistogramType BooleanHistogram::GetHistogramType() const { return BOOLEAN_HISTOGRAM; } @@ -740,12 +1000,21 @@ BooleanHistogram::BooleanHistogram(const std::string& name, const BucketRanges* ranges) : LinearHistogram(name, 1, 2, ranges) {} +BooleanHistogram::BooleanHistogram(const std::string& name, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) + : LinearHistogram(name, 1, 2, ranges, counts, logged_counts, 2, meta, + logged_meta) {} + HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) { std::string histogram_name; int flags; int declared_min; int declared_max; - size_t bucket_count; + uint32_t bucket_count; uint32_t range_checksum; if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, @@ -766,30 +1035,49 @@ HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) { // CustomHistogram: //------------------------------------------------------------------------------ +class CustomHistogram::Factory : public Histogram::Factory { + public: + Factory(const std::string& name, + const std::vector* custom_ranges, + int32_t flags) + : Histogram::Factory(name, CUSTOM_HISTOGRAM, 0, 0, 0, flags) { + custom_ranges_ = custom_ranges; + } + + protected: + BucketRanges* CreateRanges() override { + // Remove the duplicates in the custom ranges array. + std::vector ranges = *custom_ranges_; + ranges.push_back(0); // Ensure we have a zero value. + ranges.push_back(HistogramBase::kSampleType_MAX); + std::sort(ranges.begin(), ranges.end()); + ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end()); + + BucketRanges* bucket_ranges = new BucketRanges(ranges.size()); + for (uint32_t i = 0; i < ranges.size(); i++) { + bucket_ranges->set_range(i, ranges[i]); + } + bucket_ranges->ResetChecksum(); + return bucket_ranges; + } + + HistogramBase* HeapAlloc(const BucketRanges* ranges) override { + return new CustomHistogram(name_, ranges); + } + + private: + const std::vector* custom_ranges_; + + DISALLOW_COPY_AND_ASSIGN(Factory); +}; + HistogramBase* CustomHistogram::FactoryGet( const std::string& name, const std::vector& custom_ranges, int32_t flags) { CHECK(ValidateCustomRanges(custom_ranges)); - HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); - if (!histogram) { - BucketRanges* ranges = CreateBucketRangesFromCustomRanges(custom_ranges); - const BucketRanges* registered_ranges = - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); - - // To avoid racy destruction at shutdown, the following will be leaked. - CustomHistogram* tentative_histogram = - new CustomHistogram(name, registered_ranges); - - tentative_histogram->SetFlags(flags); - - histogram = - StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); - } - - DCHECK_EQ(histogram->GetHistogramType(), CUSTOM_HISTOGRAM); - return histogram; + return Factory(name, &custom_ranges, flags).Build(); } HistogramBase* CustomHistogram::FactoryGet( @@ -799,15 +1087,27 @@ HistogramBase* CustomHistogram::FactoryGet( return FactoryGet(std::string(name), custom_ranges, flags); } +HistogramBase* CustomHistogram::PersistentGet( + const std::string& name, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) { + return new CustomHistogram(name, ranges, counts, logged_counts, counts_size, + meta, logged_meta); +} + HistogramType CustomHistogram::GetHistogramType() const { return CUSTOM_HISTOGRAM; } // static std::vector CustomHistogram::ArrayToCustomRanges( - const Sample* values, size_t num_values) { + const Sample* values, uint32_t num_values) { std::vector all_values; - for (size_t i = 0; i < num_values; ++i) { + for (uint32_t i = 0; i < num_values; ++i) { Sample value = values[i]; all_values.push_back(value); @@ -825,20 +1125,37 @@ CustomHistogram::CustomHistogram(const std::string& name, ranges->range(ranges->bucket_count() - 1), ranges) {} +CustomHistogram::CustomHistogram(const std::string& name, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) + : Histogram(name, + ranges->range(1), + ranges->range(ranges->bucket_count() - 1), + ranges, + counts, + logged_counts, + counts_size, + meta, + logged_meta) {} + bool CustomHistogram::SerializeInfoImpl(Pickle* pickle) const { if (!Histogram::SerializeInfoImpl(pickle)) return false; // Serialize ranges. First and last ranges are alwasy 0 and INT_MAX, so don't // write them. - for (size_t i = 1; i < bucket_ranges()->bucket_count(); ++i) { + for (uint32_t i = 1; i < bucket_ranges()->bucket_count(); ++i) { if (!pickle->WriteInt(bucket_ranges()->range(i))) return false; } return true; } -double CustomHistogram::GetBucketSize(Count current, size_t i) const { +double CustomHistogram::GetBucketSize(Count current, uint32_t i) const { return 1; } @@ -848,7 +1165,7 @@ HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) { int flags; int declared_min; int declared_max; - size_t bucket_count; + uint32_t bucket_count; uint32_t range_checksum; if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, @@ -859,7 +1176,7 @@ HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) { // First and last ranges are not serialized. std::vector sample_ranges(bucket_count - 1); - for (size_t i = 0; i < sample_ranges.size(); ++i) { + for (uint32_t i = 0; i < sample_ranges.size(); ++i) { if (!iter->ReadInt(&sample_ranges[i])) return NULL; } @@ -877,7 +1194,7 @@ HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) { bool CustomHistogram::ValidateCustomRanges( const std::vector& custom_ranges) { bool has_valid_range = false; - for (size_t i = 0; i < custom_ranges.size(); i++) { + for (uint32_t i = 0; i < custom_ranges.size(); i++) { Sample sample = custom_ranges[i]; if (sample < 0 || sample > HistogramBase::kSampleType_MAX - 1) return false; @@ -887,22 +1204,4 @@ bool CustomHistogram::ValidateCustomRanges( return has_valid_range; } -// static -BucketRanges* CustomHistogram::CreateBucketRangesFromCustomRanges( - const std::vector& custom_ranges) { - // Remove the duplicates in the custom ranges array. - std::vector ranges = custom_ranges; - ranges.push_back(0); // Ensure we have a zero value. - ranges.push_back(HistogramBase::kSampleType_MAX); - std::sort(ranges.begin(), ranges.end()); - ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end()); - - BucketRanges* bucket_ranges = new BucketRanges(ranges.size()); - for (size_t i = 0; i < ranges.size(); i++) { - bucket_ranges->set_range(i, ranges[i]); - } - bucket_ranges->ResetChecksum(); - return bucket_ranges; -} - } // namespace base diff --git a/src/base/metrics/histogram.h b/src/base/metrics/histogram.h index 28bb29b6..ea59de64 100644 --- a/src/base/metrics/histogram.h +++ b/src/base/metrics/histogram.h @@ -92,6 +92,7 @@ class BooleanHistogram; class CustomHistogram; class Histogram; class LinearHistogram; +class PersistentMemoryAllocator; class Pickle; class PickleIterator; class SampleVector; @@ -99,7 +100,7 @@ class SampleVector; class BASE_EXPORT Histogram : public HistogramBase { public: // Initialize maximum number of buckets in histograms as 16,384. - static const size_t kBucketCount_MAX; + static const uint32_t kBucketCount_MAX; typedef std::vector Counts; @@ -116,12 +117,12 @@ class BASE_EXPORT Histogram : public HistogramBase { static HistogramBase* FactoryGet(const std::string& name, Sample minimum, Sample maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags); static HistogramBase* FactoryTimeGet(const std::string& name, base::TimeDelta minimum, base::TimeDelta maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags); // Overloads of the above two functions that take a const char* |name| param, @@ -130,14 +131,25 @@ class BASE_EXPORT Histogram : public HistogramBase { static HistogramBase* FactoryGet(const char* name, Sample minimum, Sample maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags); static HistogramBase* FactoryTimeGet(const char* name, base::TimeDelta minimum, base::TimeDelta maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags); + // Get a histogram using data in persistent storage. + static HistogramBase* PersistentGet(const std::string& name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + static void InitializeBucketRanges(Sample minimum, Sample maximum, BucketRanges* ranges); @@ -155,16 +167,17 @@ class BASE_EXPORT Histogram : public HistogramBase { // consistent with the bucket ranges and checksums in our histogram. This can // produce a false-alarm if a race occurred in the reading of the data during // a SnapShot process, but should otherwise be false at all times (unless we - // have memory over-writes, or DRAM failures). - int FindCorruption(const HistogramSamples& samples) const override; + // have memory over-writes, or DRAM failures). Flag definitions are located + // under "enum Inconsistency" in base/metrics/histogram_base.h. + uint32_t FindCorruption(const HistogramSamples& samples) const override; //---------------------------------------------------------------------------- // Accessors for factory construction, serialization and testing. //---------------------------------------------------------------------------- Sample declared_min() const { return declared_min_; } Sample declared_max() const { return declared_max_; } - virtual Sample ranges(size_t i) const; - virtual size_t bucket_count() const; + virtual Sample ranges(uint32_t i) const; + virtual uint32_t bucket_count() const; const BucketRanges* bucket_ranges() const { return bucket_ranges_; } // This function validates histogram construction arguments. It returns false @@ -176,23 +189,31 @@ class BASE_EXPORT Histogram : public HistogramBase { static bool InspectConstructionArguments(const std::string& name, Sample* minimum, Sample* maximum, - size_t* bucket_count); + uint32_t* bucket_count); // HistogramBase implementation: uint64_t name_hash() const override; HistogramType GetHistogramType() const override; bool HasConstructionArguments(Sample expected_minimum, Sample expected_maximum, - size_t expected_bucket_count) const override; + uint32_t expected_bucket_count) const override; void Add(Sample value) override; void AddCount(Sample value, int count) override; scoped_ptr SnapshotSamples() const override; + scoped_ptr SnapshotDelta() override; void AddSamples(const HistogramSamples& samples) override; bool AddSamplesFromPickle(base::PickleIterator* iter) override; void WriteHTMLGraph(std::string* output) const override; void WriteAscii(std::string* output) const override; protected: + // This class, defined entirely within the .cc file, contains all the + // common logic for building a Histogram and can be overridden by more + // specific types to alter details of how the creation is done. It is + // defined as an embedded class (rather than an anonymous one) so it + // can access the protected constructors. + class Factory; + // |ranges| should contain the underflow and overflow buckets. See top // comments for example. Histogram(const std::string& name, @@ -200,30 +221,43 @@ class BASE_EXPORT Histogram : public HistogramBase { Sample maximum, const BucketRanges* ranges); + // Traditionally, histograms allocate their own memory for the bucket + // vector but "shared" histograms use memory regions allocated from a + // special memory segment that is passed in here. It is assumed that + // the life of this memory is managed externally and exceeds the lifetime + // of this object. Practically, this memory is never released until the + // process exits and the OS cleans it up. + Histogram(const std::string& name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + ~Histogram() override; // HistogramBase implementation: bool SerializeInfoImpl(base::Pickle* pickle) const override; // Method to override to skip the display of the i'th bucket if it's empty. - virtual bool PrintEmptyBucket(size_t index) const; + virtual bool PrintEmptyBucket(uint32_t index) const; // Get normalized size, relative to the ranges(i). - virtual double GetBucketSize(Count current, size_t i) const; + virtual double GetBucketSize(Count current, uint32_t i) const; // Return a string description of what goes in a given bucket. // Most commonly this is the numeric value, but in derived classes it may // be a name (or string description) given to the bucket. - virtual const std::string GetAsciiBucketRange(size_t it) const; + virtual const std::string GetAsciiBucketRange(uint32_t it) const; private: // Allow tests to corrupt our innards for testing purposes. FRIEND_TEST_ALL_PREFIXES(HistogramTest, BoundsTest); FRIEND_TEST_ALL_PREFIXES(HistogramTest, BucketPlacementTest); - FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptBucketBounds); FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts); - FRIEND_TEST_ALL_PREFIXES(HistogramTest, NameMatchTest); - FRIEND_TEST_ALL_PREFIXES(HistogramTest, AddCountTest); friend class StatisticsRecorder; // To allow it to delete duplicates. friend class StatisticsRecorderTest; @@ -255,7 +289,7 @@ class BASE_EXPORT Histogram : public HistogramBase { void WriteAsciiBucketContext(const int64_t past, const Count current, const int64_t remaining, - const size_t i, + const uint32_t i, std::string* output) const; // WriteJSON calls these. @@ -275,6 +309,9 @@ class BASE_EXPORT Histogram : public HistogramBase { // sample. scoped_ptr samples_; + // Also keep a previous uploaded state for calculating deltas. + scoped_ptr logged_samples_; + DISALLOW_COPY_AND_ASSIGN(Histogram); }; @@ -291,12 +328,12 @@ class BASE_EXPORT LinearHistogram : public Histogram { static HistogramBase* FactoryGet(const std::string& name, Sample minimum, Sample maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags); static HistogramBase* FactoryTimeGet(const std::string& name, TimeDelta minimum, TimeDelta maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags); // Overloads of the above two functions that take a const char* |name| param, @@ -305,14 +342,25 @@ class BASE_EXPORT LinearHistogram : public Histogram { static HistogramBase* FactoryGet(const char* name, Sample minimum, Sample maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags); static HistogramBase* FactoryTimeGet(const char* name, TimeDelta minimum, TimeDelta maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags); + // Get a histogram using data in persistent storage. + static HistogramBase* PersistentGet(const std::string& name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + struct DescriptionPair { Sample sample; const char* description; // Null means end of a list of pairs. @@ -327,7 +375,7 @@ class BASE_EXPORT LinearHistogram : public Histogram { const std::string& name, Sample minimum, Sample maximum, - size_t bucket_count, + uint32_t bucket_count, int32_t flags, const DescriptionPair descriptions[]); @@ -339,20 +387,32 @@ class BASE_EXPORT LinearHistogram : public Histogram { HistogramType GetHistogramType() const override; protected: + class Factory; + LinearHistogram(const std::string& name, Sample minimum, Sample maximum, const BucketRanges* ranges); - double GetBucketSize(Count current, size_t i) const override; + LinearHistogram(const std::string& name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + + double GetBucketSize(Count current, uint32_t i) const override; // If we have a description for a bucket, then return that. Otherwise // let parent class provide a (numeric) description. - const std::string GetAsciiBucketRange(size_t i) const override; + const std::string GetAsciiBucketRange(uint32_t i) const override; // Skip printing of name for numeric range if we have a name (and if this is // an empty bucket). - bool PrintEmptyBucket(size_t index) const override; + bool PrintEmptyBucket(uint32_t index) const override; private: friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( @@ -380,10 +440,27 @@ class BASE_EXPORT BooleanHistogram : public LinearHistogram { // call sites. static HistogramBase* FactoryGet(const char* name, int32_t flags); + // Get a histogram using data in persistent storage. + static HistogramBase* PersistentGet(const std::string& name, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + HistogramType GetHistogramType() const override; + protected: + class Factory; + private: BooleanHistogram(const std::string& name, const BucketRanges* ranges); + BooleanHistogram(const std::string& name, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( base::PickleIterator* iter); @@ -412,6 +489,15 @@ class BASE_EXPORT CustomHistogram : public Histogram { const std::vector& custom_ranges, int32_t flags); + // Get a histogram using data in persistent storage. + static HistogramBase* PersistentGet(const std::string& name, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + // Overridden from Histogram: HistogramType GetHistogramType() const override; @@ -422,15 +508,25 @@ class BASE_EXPORT CustomHistogram : public Histogram { // so that invalid samples never fall into the same bucket as valid samples. // TODO(kaiwang): Change name to ArrayToCustomEnumRanges. static std::vector ArrayToCustomRanges(const Sample* values, - size_t num_values); + uint32_t num_values); protected: + class Factory; + CustomHistogram(const std::string& name, const BucketRanges* ranges); + CustomHistogram(const std::string& name, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); + // HistogramBase implementation: bool SerializeInfoImpl(base::Pickle* pickle) const override; - double GetBucketSize(Count current, size_t i) const override; + double GetBucketSize(Count current, uint32_t i) const override; private: friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( @@ -438,8 +534,6 @@ class BASE_EXPORT CustomHistogram : public Histogram { static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); static bool ValidateCustomRanges(const std::vector& custom_ranges); - static BucketRanges* CreateBucketRangesFromCustomRanges( - const std::vector& custom_ranges); DISALLOW_COPY_AND_ASSIGN(CustomHistogram); }; diff --git a/src/base/metrics/histogram_base.cc b/src/base/metrics/histogram_base.cc index 412f1438..3a40e681 100644 --- a/src/base/metrics/histogram_base.cc +++ b/src/base/metrics/histogram_base.cc @@ -97,7 +97,7 @@ bool HistogramBase::SerializeInfo(Pickle* pickle) const { return SerializeInfoImpl(pickle); } -int HistogramBase::FindCorruption(const HistogramSamples& samples) const { +uint32_t HistogramBase::FindCorruption(const HistogramSamples& samples) const { // Not supported by default. return NO_INCONSISTENCIES; } diff --git a/src/base/metrics/histogram_base.h b/src/base/metrics/histogram_base.h index 4fa07c64..6817629b 100644 --- a/src/base/metrics/histogram_base.h +++ b/src/base/metrics/histogram_base.h @@ -21,6 +21,7 @@ namespace base { +class BucketRanges; class DictionaryValue; class HistogramBase; class HistogramSamples; @@ -81,19 +82,30 @@ class BASE_EXPORT HistogramBase { // to shortcut looking up the callback if it doesn't exist. kCallbackExists = 0x20, + // Indicates that the histogram is held in "persistent" memory and may + // be accessible between processes. This is only possible if such a + // memory segment has been created/attached, used to create a Persistent- + // MemoryAllocator, and that loaded into the Histogram module before this + // histogram is created. + kIsPersistent = 0x40, + // Only for Histogram and its sub classes: fancy bucket-naming support. kHexRangePrintingFlag = 0x8000, }; // Histogram data inconsistency types. - enum Inconsistency { + enum Inconsistency : uint32_t { NO_INCONSISTENCIES = 0x0, RANGE_CHECKSUM_ERROR = 0x1, BUCKET_ORDER_ERROR = 0x2, COUNT_HIGH_ERROR = 0x4, COUNT_LOW_ERROR = 0x8, - NEVER_EXCEEDED_VALUE = 0x10 + NEVER_EXCEEDED_VALUE = 0x10, + + // This value is used only in HistogramSnapshotManager for marking + // internally when new inconsistencies are found. + NEW_INCONSISTENCY_FOUND = 0x8000000 }; explicit HistogramBase(const std::string& name); @@ -119,9 +131,10 @@ class BASE_EXPORT HistogramBase { // Whether the histogram has construction arguments as parameters specified. // For histograms that don't have the concept of minimum, maximum or // bucket_count, this function always returns false. - virtual bool HasConstructionArguments(Sample expected_minimum, - Sample expected_maximum, - size_t expected_bucket_count) const = 0; + virtual bool HasConstructionArguments( + Sample expected_minimum, + Sample expected_maximum, + uint32_t expected_bucket_count) const = 0; virtual void Add(Sample value) = 0; @@ -145,12 +158,17 @@ class BASE_EXPORT HistogramBase { // Try to find out data corruption from histogram and the samples. // The returned value is a combination of Inconsistency enum. - virtual int FindCorruption(const HistogramSamples& samples) const; + virtual uint32_t FindCorruption(const HistogramSamples& samples) const; // Snapshot the current complete set of sample data. // Override with atomic/locked snapshot if needed. virtual scoped_ptr SnapshotSamples() const = 0; + // Calculate the change (delta) in histogram counts since the previous call + // to this method. Each successive call will return only those counts + // changed since the last call. + virtual scoped_ptr SnapshotDelta() = 0; + // The following methods provide graphical histogram displays. virtual void WriteHTMLGraph(std::string* output) const = 0; virtual void WriteAscii(std::string* output) const = 0; diff --git a/src/base/metrics/histogram_macros.h b/src/base/metrics/histogram_macros.h index 0492f0c9..fa23bea7 100644 --- a/src/base/metrics/histogram_macros.h +++ b/src/base/metrics/histogram_macros.h @@ -10,6 +10,11 @@ #include "base/metrics/histogram.h" #include "base/time/time.h" +// Macros for efficient use of histograms. See documentation in histogram.h. +// +// UMA_HISTOGRAM_SPARSE_SLOWLY is defined in sparse_histogram.h as it has +// different #include dependencies. + //------------------------------------------------------------------------------ // Histograms are often put in areas where they are called many many times, and // performance is critical. As a result, they are designed to have a very low @@ -67,18 +72,24 @@ // a macro argument here. The name is only used in a DCHECK, to assure that // callers don't try to vary the name of the histogram (which would tend to be // ignored by the one-time initialization of the histogtram_pointer). -#define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \ - histogram_add_method_invocation, \ - histogram_factory_get_invocation) \ + +// In some cases (integration into 3rd party code), it's useful to seperate the +// definition of |atomic_histogram_poiner| from its use. To achieve this we +// define HISTOGRAM_POINTER_USE, which uses an |atomic_histogram_pointer|, and +// STATIC_HISTOGRAM_POINTER_BLOCK, which defines an |atomic_histogram_pointer| +// and forwards to HISTOGRAM_POINTER_USE. +#define HISTOGRAM_POINTER_USE(atomic_histogram_pointer, \ + constant_histogram_name, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation) \ do { \ - static base::subtle::AtomicWord atomic_histogram_pointer = 0; \ base::HistogramBase* histogram_pointer( \ reinterpret_cast( \ - base::subtle::Acquire_Load(&atomic_histogram_pointer))); \ + base::subtle::Acquire_Load(atomic_histogram_pointer))); \ if (!histogram_pointer) { \ histogram_pointer = histogram_factory_get_invocation; \ base::subtle::Release_Store( \ - &atomic_histogram_pointer, \ + atomic_histogram_pointer, \ reinterpret_cast(histogram_pointer)); \ } \ if (DCHECK_IS_ON()) \ @@ -86,6 +97,18 @@ histogram_pointer->histogram_add_method_invocation; \ } while (0) +// Defines the static |atomic_histogram_pointer| and forwards to +// HISTOGRAM_POINTER_USE. +#define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation) \ + do { \ + static base::subtle::AtomicWord atomic_histogram_pointer = 0; \ + HISTOGRAM_POINTER_USE(&atomic_histogram_pointer, constant_histogram_name, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation); \ + } while (0) + //------------------------------------------------------------------------------ // Provide easy general purpose histogram in a macro, just like stats counters. // The first four macros use 50 buckets. diff --git a/src/base/metrics/histogram_persistence.cc b/src/base/metrics/histogram_persistence.cc new file mode 100644 index 00000000..f18d1752 --- /dev/null +++ b/src/base/metrics/histogram_persistence.cc @@ -0,0 +1,516 @@ +// Copyright (c) 2015 The Chromium 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/metrics/histogram_persistence.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_base.h" +#include "base/metrics/histogram_samples.h" +#include "base/metrics/statistics_recorder.h" +#include "base/synchronization/lock.h" + +namespace base { + +namespace { + +// Enumerate possible creation results for reporting. +enum CreateHistogramResultType { + // Everything was fine. + CREATE_HISTOGRAM_SUCCESS = 0, + + // Pointer to metadata was not valid. + CREATE_HISTOGRAM_INVALID_METADATA_POINTER, + + // Histogram metadata was not valid. + CREATE_HISTOGRAM_INVALID_METADATA, + + // Ranges information was not valid. + CREATE_HISTOGRAM_INVALID_RANGES_ARRAY, + + // Counts information was not valid. + CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY, + + // Could not allocate histogram memory due to corruption. + CREATE_HISTOGRAM_ALLOCATOR_CORRUPT, + + // Could not allocate histogram memory due to lack of space. + CREATE_HISTOGRAM_ALLOCATOR_FULL, + + // Could not allocate histogram memory due to unknown error. + CREATE_HISTOGRAM_ALLOCATOR_ERROR, + + // Histogram was of unknown type. + CREATE_HISTOGRAM_UNKNOWN_TYPE, + + // Instance has detected a corrupt allocator (recorded only once). + CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT, + + // Always keep this at the end. + CREATE_HISTOGRAM_MAX +}; + +// Name of histogram for storing results of local operations. +const char kResultHistogram[] = "UMA.CreatePersistentHistogram.Result"; + +// Type identifiers used when storing in persistent memory so they can be +// identified during extraction; the first 4 bytes of the SHA1 of the name +// is used as a unique integer. A "version number" is added to the base +// so that, if the structure of that object changes, stored older versions +// will be safely ignored. +enum : uint32_t { + kTypeIdHistogram = 0xF1645910 + 2, // SHA1(Histogram) v2 + kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1 + kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1 +}; + +// This data must be held in persistent memory in order for processes to +// locate and use histograms created elsewhere. All elements must be of a +// fixed width to ensure 32/64-bit interoperability. +struct PersistentHistogramData { + int32_t histogram_type; + int32_t flags; + int32_t minimum; + int32_t maximum; + uint32_t bucket_count; + PersistentMemoryAllocator::Reference ranges_ref; + uint32_t ranges_checksum; + PersistentMemoryAllocator::Reference counts_ref; + HistogramSamples::Metadata samples_metadata; + HistogramSamples::Metadata logged_metadata; + + // Space for the histogram name will be added during the actual allocation + // request. This must be the last field of the structure. A zero-size array + // or a "flexible" array would be preferred but is not (yet) valid C++. + char name[1]; +}; + +// The object held here will obviously not be destructed at process exit +// but that's okay since PersistentMemoryAllocator objects are explicitly +// forbidden from doing anything essential at exit anyway due to the fact +// that they depend on data managed elsewhere and which could be destructed +// first. +PersistentMemoryAllocator* g_allocator = nullptr; + +// Take an array of range boundaries and create a proper BucketRanges object +// which is returned to the caller. A return of nullptr indicates that the +// passed boundaries are invalid. +BucketRanges* CreateRangesFromData(HistogramBase::Sample* ranges_data, + uint32_t ranges_checksum, + size_t count) { + scoped_ptr ranges(new BucketRanges(count)); + DCHECK_EQ(count, ranges->size()); + for (size_t i = 0; i < count; ++i) { + if (i > 0 && ranges_data[i] <= ranges_data[i - 1]) + return nullptr; + ranges->set_range(i, ranges_data[i]); + } + + ranges->ResetChecksum(); + if (ranges->checksum() != ranges_checksum) + return nullptr; + + return ranges.release(); +} + +// Calculate the number of bytes required to store all of a histogram's +// "counts". This will return zero (0) if |bucket_count| is not valid. +size_t CalculateRequiredCountsBytes(size_t bucket_count) { + // 2 because each "sample count" also requires a backup "logged count" + // used for calculating the delta during snapshot operations. + const unsigned kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount); + + // If the |bucket_count| is such that it would overflow the return type, + // perhaps as the result of a malicious actor, then return zero to + // indicate the problem to the caller. + if (bucket_count > std::numeric_limits::max() / kBytesPerBucket) + return 0; + + return bucket_count * kBytesPerBucket; +} + +} // namespace + +const Feature kPersistentHistogramsFeature{ + "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT +}; + +// Get the histogram in which create results are stored. This is copied almost +// exactly from the STATIC_HISTOGRAM_POINTER_BLOCK macro but with added code +// to prevent recursion (a likely occurance because the creation of a new +// histogram can end up calling this.) +HistogramBase* GetCreateHistogramResultHistogram() { + static base::subtle::AtomicWord atomic_histogram_pointer = 0; + HistogramBase* histogram_pointer( + reinterpret_cast( + base::subtle::Acquire_Load(&atomic_histogram_pointer))); + if (!histogram_pointer) { + // It's possible for multiple threads to make it here in parallel but + // they'll always return the same result as there is a mutex in the Get. + // The purpose of the "initialized" variable is just to ensure that + // the same thread doesn't recurse which is also why it doesn't have + // to be atomic. + static bool initialized = false; + if (!initialized) { + initialized = true; + if (g_allocator) { + DLOG(WARNING) << "Creating the results-histogram inside persistent" + << " memory can cause future allocations to crash if" + << " that memory is ever released (for testing)."; + } + + histogram_pointer = LinearHistogram::FactoryGet( + kResultHistogram, 1, CREATE_HISTOGRAM_MAX, CREATE_HISTOGRAM_MAX + 1, + HistogramBase::kUmaTargetedHistogramFlag); + base::subtle::Release_Store( + &atomic_histogram_pointer, + reinterpret_cast(histogram_pointer)); + } + } + return histogram_pointer; +} + +// Record the result of a histogram creation. +void RecordCreateHistogramResult(CreateHistogramResultType result) { + HistogramBase* result_histogram = GetCreateHistogramResultHistogram(); + if (result_histogram) + result_histogram->Add(result); +} + +void SetPersistentHistogramMemoryAllocator( + PersistentMemoryAllocator* allocator) { + // Releasing or changing an allocator is extremely dangerous because it + // likely has histograms stored within it. If the backing memory is also + // also released, future accesses to those histograms will seg-fault. + CHECK(!g_allocator); + g_allocator = allocator; +} + +PersistentMemoryAllocator* GetPersistentHistogramMemoryAllocator() { + return g_allocator; +} + +PersistentMemoryAllocator* +ReleasePersistentHistogramMemoryAllocatorForTesting() { + PersistentMemoryAllocator* allocator = g_allocator; + if (!allocator) + return nullptr; + + // Before releasing the memory, it's necessary to have the Statistics- + // Recorder forget about the histograms contained therein; otherwise, + // some operations will try to access them and the released memory. + PersistentMemoryAllocator::Iterator iter; + PersistentMemoryAllocator::Reference ref; + uint32_t type_id; + allocator->CreateIterator(&iter); + while ((ref = allocator->GetNextIterable(&iter, &type_id)) != 0) { + if (type_id == kTypeIdHistogram) { + PersistentHistogramData* histogram_data = + allocator->GetAsObject( + ref, kTypeIdHistogram); + DCHECK(histogram_data); + StatisticsRecorder::ForgetHistogramForTesting(histogram_data->name); + + // If a test breaks here then a memory region containing a histogram + // actively used by this code is being released back to the test. + // If that memory segment were to be deleted, future calls to create + // persistent histograms would crash. To avoid this, have the test call + // the method GetCreateHistogramResultHistogram() *before* setting the + // (temporary) memory allocator via SetPersistentMemoryAllocator() so + // that the histogram is instead allocated from the process heap. + DCHECK_NE(kResultHistogram, histogram_data->name); + } + } + + g_allocator = nullptr; + return allocator; +}; + +HistogramBase* CreatePersistentHistogram( + PersistentMemoryAllocator* allocator, + PersistentHistogramData* histogram_data_ptr) { + if (!histogram_data_ptr) { + RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA_POINTER); + NOTREACHED(); + return nullptr; + } + + // Copy the histogram_data to local storage because anything in persistent + // memory cannot be trusted as it could be changed at any moment by a + // malicious actor that shares access. The contents of histogram_data are + // validated below; the local copy is to ensure that the contents cannot + // be externally changed between validation and use. + PersistentHistogramData histogram_data = *histogram_data_ptr; + + HistogramBase::Sample* ranges_data = + allocator->GetAsObject(histogram_data.ranges_ref, + kTypeIdRangesArray); + if (!ranges_data || histogram_data.bucket_count < 2 || + histogram_data.bucket_count + 1 > + std::numeric_limits::max() / + sizeof(HistogramBase::Sample) || + allocator->GetAllocSize(histogram_data.ranges_ref) < + (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample)) { + RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); + NOTREACHED(); + return nullptr; + } + // To avoid racy destruction at shutdown, the following will be leaked. + const BucketRanges* ranges = CreateRangesFromData( + ranges_data, + histogram_data.ranges_checksum, + histogram_data.bucket_count + 1); + if (!ranges) { + RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); + NOTREACHED(); + return nullptr; + } + ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); + + HistogramBase::AtomicCount* counts_data = + allocator->GetAsObject( + histogram_data.counts_ref, kTypeIdCountsArray); + size_t counts_bytes = + CalculateRequiredCountsBytes(histogram_data.bucket_count); + if (!counts_data || !counts_bytes || + allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) { + RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); + NOTREACHED(); + return nullptr; + } + + // After the main "counts" array is a second array using for storing what + // was previously logged. This is used to calculate the "delta" during + // snapshot operations. + HistogramBase::AtomicCount* logged_data = + counts_data + histogram_data.bucket_count; + + std::string name(histogram_data_ptr->name); + HistogramBase* histogram = nullptr; + switch (histogram_data.histogram_type) { + case HISTOGRAM: + histogram = Histogram::PersistentGet( + name, + histogram_data.minimum, + histogram_data.maximum, + ranges, + counts_data, + logged_data, + histogram_data.bucket_count, + &histogram_data_ptr->samples_metadata, + &histogram_data_ptr->logged_metadata); + DCHECK(histogram); + break; + case LINEAR_HISTOGRAM: + histogram = LinearHistogram::PersistentGet( + name, + histogram_data.minimum, + histogram_data.maximum, + ranges, + counts_data, + logged_data, + histogram_data.bucket_count, + &histogram_data_ptr->samples_metadata, + &histogram_data_ptr->logged_metadata); + DCHECK(histogram); + break; + case BOOLEAN_HISTOGRAM: + histogram = BooleanHistogram::PersistentGet( + name, + ranges, + counts_data, + logged_data, + &histogram_data_ptr->samples_metadata, + &histogram_data_ptr->logged_metadata); + DCHECK(histogram); + break; + case CUSTOM_HISTOGRAM: + histogram = CustomHistogram::PersistentGet( + name, + ranges, + counts_data, + logged_data, + histogram_data.bucket_count, + &histogram_data_ptr->samples_metadata, + &histogram_data_ptr->logged_metadata); + DCHECK(histogram); + break; + default: + NOTREACHED(); + } + + if (histogram) { + DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType()); + histogram->SetFlags(histogram_data.flags); + RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS); + } else { + RecordCreateHistogramResult(CREATE_HISTOGRAM_UNKNOWN_TYPE); + } + + return histogram; +} + +HistogramBase* GetPersistentHistogram( + PersistentMemoryAllocator* allocator, + int32_t ref) { + // Unfortunately, the above "pickle" methods cannot be used as part of the + // persistance because the deserialization methods always create local + // count data (these must referenced the persistent counts) and always add + // it to the local list of known histograms (these may be simple references + // to histograms in other processes). + PersistentHistogramData* histogram_data = + allocator->GetAsObject(ref, kTypeIdHistogram); + size_t length = allocator->GetAllocSize(ref); + if (!histogram_data || + reinterpret_cast(histogram_data)[length - 1] != '\0') { + RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA); + NOTREACHED(); + return nullptr; + } + return CreatePersistentHistogram(allocator, histogram_data); +} + +HistogramBase* GetNextPersistentHistogram( + PersistentMemoryAllocator* allocator, + PersistentMemoryAllocator::Iterator* iter) { + PersistentMemoryAllocator::Reference ref; + uint32_t type_id; + while ((ref = allocator->GetNextIterable(iter, &type_id)) != 0) { + if (type_id == kTypeIdHistogram) + return GetPersistentHistogram(allocator, ref); + } + return nullptr; +} + +void FinalizePersistentHistogram(PersistentMemoryAllocator::Reference ref, + bool registered) { + // If the created persistent histogram was registered then it needs to + // be marked as "iterable" in order to be found by other processes. + if (registered) + GetPersistentHistogramMemoryAllocator()->MakeIterable(ref); + // If it wasn't registered then a race condition must have caused + // two to be created. The allocator does not support releasing the + // acquired memory so just change the type to be empty. + else + GetPersistentHistogramMemoryAllocator()->SetType(ref, 0); +} + +HistogramBase* AllocatePersistentHistogram( + PersistentMemoryAllocator* allocator, + HistogramType histogram_type, + const std::string& name, + int minimum, + int maximum, + const BucketRanges* bucket_ranges, + int32_t flags, + PersistentMemoryAllocator::Reference* ref_ptr) { + if (!allocator) + return nullptr; + + // If the allocator is corrupt, don't waste time trying anything else. + // This also allows differentiating on the dashboard between allocations + // failed due to a corrupt allocator and the number of process instances + // with one, the latter being idicated by "newly corrupt", below. + if (allocator->IsCorrupt()) { + RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT); + return nullptr; + } + + // If CalculateRequiredCountsBytes() returns zero then the bucket_count + // was not valid. + size_t bucket_count = bucket_ranges->bucket_count(); + size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); + if (!counts_bytes) { + NOTREACHED(); + return nullptr; + } + + size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample); + PersistentMemoryAllocator::Reference ranges_ref = + allocator->Allocate(ranges_bytes, kTypeIdRangesArray); + PersistentMemoryAllocator::Reference counts_ref = + allocator->Allocate(counts_bytes, kTypeIdCountsArray); + PersistentMemoryAllocator::Reference histogram_ref = + allocator->Allocate(offsetof(PersistentHistogramData, name) + + name.length() + 1, kTypeIdHistogram); + HistogramBase::Sample* ranges_data = + allocator->GetAsObject(ranges_ref, + kTypeIdRangesArray); + PersistentHistogramData* histogram_data = + allocator->GetAsObject(histogram_ref, + kTypeIdHistogram); + + // Only continue here if all allocations were successful. If they weren't + // there is no way to free the space but that's not really a problem since + // the allocations only fail because the space is full and so any future + // attempts will also fail. + if (counts_ref && ranges_data && histogram_data) { + strcpy(histogram_data->name, name.c_str()); + for (size_t i = 0; i < bucket_ranges->size(); ++i) + ranges_data[i] = bucket_ranges->range(i); + + histogram_data->histogram_type = histogram_type; + histogram_data->flags = flags; + histogram_data->minimum = minimum; + histogram_data->maximum = maximum; + histogram_data->bucket_count = static_cast(bucket_count); + histogram_data->ranges_ref = ranges_ref; + histogram_data->ranges_checksum = bucket_ranges->checksum(); + histogram_data->counts_ref = counts_ref; + + // Create the histogram using resources in persistent memory. This ends up + // resolving the "ref" values stored in histogram_data instad of just + // using what is already known above but avoids duplicating the switch + // statement here and serves as a double-check that everything is + // correct before commiting the new histogram to persistent space. + HistogramBase* histogram = + CreatePersistentHistogram(allocator, histogram_data); + DCHECK(histogram); + if (ref_ptr != nullptr) + *ref_ptr = histogram_ref; + return histogram; + } + + CreateHistogramResultType result; + if (allocator->IsCorrupt()) { + RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT); + result = CREATE_HISTOGRAM_ALLOCATOR_CORRUPT; + } else if (allocator->IsFull()) { + result = CREATE_HISTOGRAM_ALLOCATOR_FULL; + } else { + result = CREATE_HISTOGRAM_ALLOCATOR_ERROR; + } + RecordCreateHistogramResult(result); + NOTREACHED() << "error=" << result; + + return nullptr; +} + +void ImportPersistentHistograms() { + // The lock protects against concurrent access to the iterator and is created + // in a thread-safe manner when needed. + static base::LazyInstance::Leaky lock = LAZY_INSTANCE_INITIALIZER; + + if (g_allocator) { + base::AutoLock auto_lock(lock.Get()); + + // Each call resumes from where it last left off so need persistant + // iterator. This class has a constructor so even the definition has + // to be protected by the lock in order to be thread-safe. + static PersistentMemoryAllocator::Iterator iter; + if (iter.is_clear()) + g_allocator->CreateIterator(&iter); + + while (true) { + HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); + if (!histogram) + break; + StatisticsRecorder::RegisterOrDeleteDuplicate(histogram); + } + } +} + +} // namespace base diff --git a/src/base/metrics/histogram_persistence.h b/src/base/metrics/histogram_persistence.h new file mode 100644 index 00000000..95f48784 --- /dev/null +++ b/src/base/metrics/histogram_persistence.h @@ -0,0 +1,91 @@ +// Copyright (c) 2015 The Chromium 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 BASE_METRICS_HISTOGRAM_PERSISTENCE_H_ +#define BASE_METRICS_HISTOGRAM_PERSISTENCE_H_ + +#include "base/base_export.h" +#include "base/feature_list.h" +#include "base/memory/scoped_ptr.h" +#include "base/metrics/histogram_base.h" +#include "base/metrics/persistent_memory_allocator.h" + +namespace base { + +// Feature definition for enabling histogram persistence. +BASE_EXPORT extern const Feature kPersistentHistogramsFeature; + +// Histogram containing creation results. Visible for testing. +BASE_EXPORT HistogramBase* GetCreateHistogramResultHistogram(); + +// Access a PersistentMemoryAllocator for storing histograms in space that +// can be persisted or shared between processes. There is only ever one +// allocator for all such histograms created by a single process though one +// process may access the histograms created by other processes if it has a +// handle on its memory segment. This takes ownership of the object and +// should not be changed without great care as it is likely that there will +// be pointers to data held in that space. It should be called as soon as +// possible during startup to capture as many histograms as possible and +// while operating single-threaded so there are no race-conditions. +BASE_EXPORT void SetPersistentHistogramMemoryAllocator( + PersistentMemoryAllocator* allocator); +BASE_EXPORT PersistentMemoryAllocator* GetPersistentHistogramMemoryAllocator(); + +// This access to the persistent allocator is only for testing; it extracts +// the current allocator completely. This allows easy creation of histograms +// within persistent memory segments which can then be extracted and used +// in other ways. +BASE_EXPORT PersistentMemoryAllocator* +ReleasePersistentHistogramMemoryAllocatorForTesting(); + +// Recreate a Histogram from data held in persistent memory. Though this +// object will be local to the current process, the sample data will be +// shared with all other threads referencing it. This method takes a |ref| +// to the top- level histogram data and the |allocator| on which it is found. +// This method will return nullptr if any problem is detected with the data. +// The |allocator| may or may not be the same as the PersistentMemoryAllocator +// set for general use so that this method can be used to extract Histograms +// from persistent memory segments other than the default place that this +// process is creating its own histograms. The caller must take ownership of +// the returned object and destroy it when no longer needed. +BASE_EXPORT HistogramBase* GetPersistentHistogram( + PersistentMemoryAllocator* allocator, + int32_t ref); + +// Get the next histogram in persistent data based on iterator. The caller +// must take ownership of the returned object and destroy it when no longer +// needed. +BASE_EXPORT HistogramBase* GetNextPersistentHistogram( + PersistentMemoryAllocator* allocator, + PersistentMemoryAllocator::Iterator* iter); + +// Finalize the creation of the histogram, making it available to other +// processes if it is the registered instance. +void FinalizePersistentHistogram(PersistentMemoryAllocator::Reference ref, + bool register); + +// Allocate a new persistent histogram. This does *not* make the object +// iterable in the allocator; call MakeIterable(ref) directly if that is +// desired. +BASE_EXPORT HistogramBase* AllocatePersistentHistogram( + PersistentMemoryAllocator* allocator, + HistogramType histogram_type, + const std::string& name, + int minimum, + int maximum, + const BucketRanges* bucket_ranges, + int32_t flags, + PersistentMemoryAllocator::Reference* ref_ptr); + +// Import new histograms from attached PersistentMemoryAllocator. It's +// possible for other processes to create histograms in the attached memory +// segment; this adds those to the internal list of known histograms to +// avoid creating duplicates that would have to merged during reporting. +// Every call to this method resumes from the last entry it saw so it costs +// nothing if nothing new has been added. +void ImportPersistentHistograms(); + +} // namespace base + +#endif // BASE_METRICS_HISTOGRAM_PERSISTENCE_H_ diff --git a/src/base/metrics/histogram_samples.cc b/src/base/metrics/histogram_samples.cc index 4cd8e556..246a0204 100644 --- a/src/base/metrics/histogram_samples.cc +++ b/src/base/metrics/histogram_samples.cc @@ -73,7 +73,11 @@ HistogramSamples::HistogramSamples(uint64_t id) HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta) : meta_(meta) { DCHECK(meta_->id == 0 || meta_->id == id); - meta_->id = id; + + // It's possible that |meta| is contained in initialized, read-only memory + // so it's essential that no write be done in that case. + if (!meta_->id) + meta_->id = id; } HistogramSamples::~HistogramSamples() {} diff --git a/src/base/metrics/histogram_samples.h b/src/base/metrics/histogram_samples.h index 3da3e2d8..29fbe17a 100644 --- a/src/base/metrics/histogram_samples.h +++ b/src/base/metrics/histogram_samples.h @@ -19,7 +19,10 @@ class Pickle; class PickleIterator; class SampleCountIterator; -// HistogramSamples is a container storing all samples of a histogram. +// HistogramSamples is a container storing all samples of a histogram. All +// elements must be of a fixed width to ensure 32/64-bit interoperability. +// If this structure changes, bump the version number for kTypeIdHistogram +// in histogram_persistence.cc. class BASE_EXPORT HistogramSamples { public: struct Metadata { diff --git a/src/base/metrics/metrics_hashes.cc b/src/base/metrics/metrics_hashes.cc index 73bce2e0..5672b06d 100644 --- a/src/base/metrics/metrics_hashes.cc +++ b/src/base/metrics/metrics_hashes.cc @@ -22,9 +22,9 @@ inline uint64_t DigestToUInt64(const base::MD5Digest& digest) { } // namespace -uint64_t HashMetricName(const std::string& name) { +uint64_t HashMetricName(base::StringPiece name) { base::MD5Digest digest; - base::MD5Sum(name.c_str(), name.size(), &digest); + base::MD5Sum(name.data(), name.size(), &digest); return DigestToUInt64(digest); } diff --git a/src/base/metrics/metrics_hashes.h b/src/base/metrics/metrics_hashes.h index bd040173..d05c4ba9 100644 --- a/src/base/metrics/metrics_hashes.h +++ b/src/base/metrics/metrics_hashes.h @@ -6,15 +6,15 @@ #define BASE_METRICS_METRICS_HASHES_H_ #include -#include #include "base/base_export.h" +#include "base/strings/string_piece.h" namespace base { // Computes a uint64_t hash of a given string based on its MD5 hash. Suitable // for metric names. -BASE_EXPORT uint64_t HashMetricName(const std::string& name); +BASE_EXPORT uint64_t HashMetricName(base::StringPiece name); } // namespace metrics diff --git a/src/base/metrics/persistent_memory_allocator.cc b/src/base/metrics/persistent_memory_allocator.cc new file mode 100644 index 00000000..e2ed44d2 --- /dev/null +++ b/src/base/metrics/persistent_memory_allocator.cc @@ -0,0 +1,712 @@ +// Copyright (c) 2015 The Chromium 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/metrics/persistent_memory_allocator.h" + +#include +#include + +#if 0 +#include "base/files/memory_mapped_file.h" +#endif +#include "base/logging.h" +#if 0 +#include "base/memory/shared_memory.h" +#endif +#include "base/metrics/histogram_macros.h" + +namespace { + +// Required range of memory segment sizes. It has to fit in an unsigned 32-bit +// number and should be a power of 2 in order to accomodate almost any page +// size. +const uint32_t kSegmentMinSize = 1 << 10; // 1 KiB +const uint32_t kSegmentMaxSize = 1 << 30; // 1 GiB + +// A constant (random) value placed in the shared metadata to identify +// an already initialized memory segment. +const uint32_t kGlobalCookie = 0x408305DC; + +// The current version of the metadata. If updates are made that change +// the metadata, the version number can be queried to operate in a backward- +// compatible manner until the memory segment is completely re-initalized. +const uint32_t kGlobalVersion = 1; + +// Constant values placed in the block headers to indicate its state. +const uint32_t kBlockCookieFree = 0; +const uint32_t kBlockCookieQueue = 1; +const uint32_t kBlockCookieWasted = (uint32_t)-1; +const uint32_t kBlockCookieAllocated = 0xC8799269; + +// TODO(bcwhite): When acceptable, consider moving flags to std::atomic +// types rather than combined bitfield. + +// Flags stored in the flags_ field of the SharedMetaData structure below. +enum : int { + kFlagCorrupt = 1 << 0, + kFlagFull = 1 << 1 +}; + +bool CheckFlag(const volatile std::atomic* flags, int flag) { + uint32_t loaded_flags = flags->load(); + return (loaded_flags & flag) != 0; +} + +void SetFlag(volatile std::atomic* flags, int flag) { + uint32_t loaded_flags = flags->load(); + for (;;) { + uint32_t new_flags = (loaded_flags & ~flag) | flag; + // In the failue case, actual "flags" value stored in loaded_flags. + if (flags->compare_exchange_weak(loaded_flags, new_flags)) + break; + } +} + +} // namespace + +namespace base { + +// All allocations and data-structures must be aligned to this byte boundary. +// Alignment as large as the physical bus between CPU and RAM is _required_ +// for some architectures, is simply more efficient on other CPUs, and +// generally a Good Idea(tm) for all platforms as it reduces/eliminates the +// chance that a type will span cache lines. Alignment mustn't be less +// than 8 to ensure proper alignment for all types. The rest is a balance +// between reducing spans across multiple cache lines and wasted space spent +// padding out allocations. An alignment of 16 would ensure that the block +// header structure always sits in a single cache line. An average of about +// 1/2 this value will be wasted with every allocation. +const uint32_t PersistentMemoryAllocator::kAllocAlignment = 8; + +// The block-header is placed at the top of every allocation within the +// segment to describe the data that follows it. +struct PersistentMemoryAllocator::BlockHeader { + uint32_t size; // Number of bytes in this block, including header. + uint32_t cookie; // Constant value indicating completed allocation. + uint32_t type_id; // A number provided by caller indicating data type. + std::atomic next; // Pointer to the next block when iterating. +}; + +// The shared metadata exists once at the top of the memory segment to +// describe the state of the allocator to all processes. +struct PersistentMemoryAllocator::SharedMetadata { + uint32_t cookie; // Some value that indicates complete initialization. + uint32_t size; // Total size of memory segment. + uint32_t page_size; // Paging size within memory segment. + uint32_t version; // Version code so upgrades don't break. + uint64_t id; // Arbitrary ID number given by creator. + uint32_t name; // Reference to stored name string. + + // Above is read-only after first construction. Below may be changed and + // so must be marked "volatile" to provide correct inter-process behavior. + + // Bitfield of information flags. Access to this should be done through + // the CheckFlag() and SetFlag() methods defined above. + volatile std::atomic flags; + + // Offset/reference to first free space in segment. + volatile std::atomic freeptr; + + // The "iterable" queue is an M&S Queue as described here, append-only: + // https://www.research.ibm.com/people/m/michael/podc-1996.pdf + volatile std::atomic tailptr; // Last block of iteration queue. + volatile BlockHeader queue; // Empty block for linked-list head/tail. +}; + +// The "queue" block header is used to detect "last node" so that zero/null +// can be used to indicate that it hasn't been added at all. It is part of +// the SharedMetadata structure which itself is always located at offset zero. +const PersistentMemoryAllocator::Reference + PersistentMemoryAllocator::kReferenceQueue = + offsetof(SharedMetadata, queue); +const PersistentMemoryAllocator::Reference + PersistentMemoryAllocator::kReferenceNull = 0; + + +// static +bool PersistentMemoryAllocator::IsMemoryAcceptable(const void* base, + size_t size, + size_t page_size, + bool readonly) { + return ((base && reinterpret_cast(base) % kAllocAlignment == 0) && + (size >= sizeof(SharedMetadata) && size <= kSegmentMaxSize) && + (size >= kSegmentMinSize || readonly) && + (size % kAllocAlignment == 0 || readonly) && + (page_size == 0 || size % page_size == 0 || readonly)); +} + +PersistentMemoryAllocator::PersistentMemoryAllocator( + void* base, + size_t size, + size_t page_size, + uint64_t id, + base::StringPiece name, + bool readonly) + : mem_base_(static_cast(base)), + mem_size_(static_cast(size)), + mem_page_(static_cast((page_size ? page_size : size))), + readonly_(readonly), + corrupt_(0), + allocs_histogram_(nullptr), + used_histogram_(nullptr) { + static_assert(sizeof(BlockHeader) % kAllocAlignment == 0, + "BlockHeader is not a multiple of kAllocAlignment"); + static_assert(sizeof(SharedMetadata) % kAllocAlignment == 0, + "SharedMetadata is not a multiple of kAllocAlignment"); + static_assert(kReferenceQueue % kAllocAlignment == 0, + "\"queue\" is not aligned properly; must be at end of struct"); + + // Ensure that memory segment is of acceptable size. + CHECK(IsMemoryAcceptable(base, size, page_size, readonly)); + + // These atomics operate inter-process and so must be lock-free. The local + // casts are to make sure it can be evaluated at compile time to a constant. + CHECK(((SharedMetadata*)0)->freeptr.is_lock_free()); + CHECK(((SharedMetadata*)0)->flags.is_lock_free()); + CHECK(((BlockHeader*)0)->next.is_lock_free()); + CHECK(corrupt_.is_lock_free()); + + if (shared_meta()->cookie != kGlobalCookie) { + if (readonly) { + SetCorrupt(); + return; + } + + // This block is only executed when a completely new memory segment is + // being initialized. It's unshared and single-threaded... + volatile BlockHeader* const first_block = + reinterpret_cast(mem_base_ + + sizeof(SharedMetadata)); + if (shared_meta()->cookie != 0 || + shared_meta()->size != 0 || + shared_meta()->version != 0 || + shared_meta()->freeptr.load() != 0 || + shared_meta()->flags.load() != 0 || + shared_meta()->id != 0 || + shared_meta()->name != 0 || + shared_meta()->tailptr != 0 || + shared_meta()->queue.cookie != 0 || + shared_meta()->queue.next.load() != 0 || + first_block->size != 0 || + first_block->cookie != 0 || + first_block->type_id != 0 || + first_block->next != 0) { + // ...or something malicious has been playing with the metadata. + NOTREACHED(); + SetCorrupt(); + } + + // This is still safe to do even if corruption has been detected. + shared_meta()->cookie = kGlobalCookie; + shared_meta()->size = mem_size_; + shared_meta()->page_size = mem_page_; + shared_meta()->version = kGlobalVersion; + shared_meta()->id = id; + shared_meta()->freeptr.store(sizeof(SharedMetadata)); + + // Set up the queue of iterable allocations. + shared_meta()->queue.size = sizeof(BlockHeader); + shared_meta()->queue.cookie = kBlockCookieQueue; + shared_meta()->queue.next.store(kReferenceQueue); + shared_meta()->tailptr.store(kReferenceQueue); + + // Allocate space for the name so other processes can learn it. + if (!name.empty()) { + const size_t name_length = name.length() + 1; + shared_meta()->name = Allocate(name_length, 0); + char* name_cstr = GetAsObject(shared_meta()->name, 0); + if (name_cstr) + memcpy(name_cstr, name.data(), name.length()); + } + } else { + if (shared_meta()->size == 0 || + shared_meta()->version == 0 || + shared_meta()->freeptr.load() == 0 || + shared_meta()->tailptr == 0 || + shared_meta()->queue.cookie == 0 || + shared_meta()->queue.next.load() == 0) { + SetCorrupt(); + } + if (!readonly) { + // The allocator is attaching to a previously initialized segment of + // memory. Make sure the embedded data matches what has been passed. + if (shared_meta()->size != mem_size_ || + shared_meta()->page_size != mem_page_) { + NOTREACHED(); + SetCorrupt(); + } + } + } +} + +PersistentMemoryAllocator::~PersistentMemoryAllocator() { + // It's strictly forbidden to do any memory access here in case there is + // some issue with the underlying memory segment. The "Local" allocator + // makes use of this to allow deletion of the segment on the heap from + // within its destructor. +} + +uint64_t PersistentMemoryAllocator::Id() const { + return shared_meta()->id; +} + +const char* PersistentMemoryAllocator::Name() const { + Reference name_ref = shared_meta()->name; + const char* name_cstr = GetAsObject(name_ref, 0); + if (!name_cstr) + return ""; + + size_t name_length = GetAllocSize(name_ref); + if (name_cstr[name_length - 1] != '\0') { + NOTREACHED(); + SetCorrupt(); + return ""; + } + + return name_cstr; +} + +void PersistentMemoryAllocator::CreateTrackingHistograms( + base::StringPiece name) { + if (name.empty() || readonly_) + return; + + std::string name_string = name.as_string(); + DCHECK(!used_histogram_); + used_histogram_ = LinearHistogram::FactoryGet( + "UMA.PersistentAllocator." + name_string + ".UsedPct", 1, 101, 21, + HistogramBase::kUmaTargetedHistogramFlag); + + DCHECK(!allocs_histogram_); + allocs_histogram_ = Histogram::FactoryGet( + "UMA.PersistentAllocator." + name_string + ".Allocs", 1, 10000, 50, + HistogramBase::kUmaTargetedHistogramFlag); +} + +size_t PersistentMemoryAllocator::used() const { + return std::min(shared_meta()->freeptr.load(), mem_size_); +} + +size_t PersistentMemoryAllocator::GetAllocSize(Reference ref) const { + const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); + if (!block) + return 0; + uint32_t size = block->size; + // Header was verified by GetBlock() but a malicious actor could change + // the value between there and here. Check it again. + if (size <= sizeof(BlockHeader) || ref + size > mem_size_) { + SetCorrupt(); + return 0; + } + return size - sizeof(BlockHeader); +} + +uint32_t PersistentMemoryAllocator::GetType(Reference ref) const { + const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); + if (!block) + return 0; + return block->type_id; +} + +void PersistentMemoryAllocator::SetType(Reference ref, uint32_t type_id) { + DCHECK(!readonly_); + volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); + if (!block) + return; + block->type_id = type_id; +} + +PersistentMemoryAllocator::Reference PersistentMemoryAllocator::Allocate( + size_t req_size, + uint32_t type_id) { + Reference ref = AllocateImpl(req_size, type_id); + if (ref) { + // Success: Record this allocation in usage stats (if active). + if (allocs_histogram_) + allocs_histogram_->Add(static_cast(req_size)); + } else { + // Failure: Record an allocation of zero for tracking. + if (allocs_histogram_) + allocs_histogram_->Add(0); + } + return ref; +} + +PersistentMemoryAllocator::Reference PersistentMemoryAllocator::AllocateImpl( + size_t req_size, + uint32_t type_id) { + DCHECK(!readonly_); + + // Validate req_size to ensure it won't overflow when used as 32-bit value. + if (req_size > kSegmentMaxSize - sizeof(BlockHeader)) { + NOTREACHED(); + return kReferenceNull; + } + + // Round up the requested size, plus header, to the next allocation alignment. + uint32_t size = static_cast(req_size + sizeof(BlockHeader)); + size = (size + (kAllocAlignment - 1)) & ~(kAllocAlignment - 1); + if (size <= sizeof(BlockHeader) || size > mem_page_) { + NOTREACHED(); + return kReferenceNull; + } + + // Get the current start of unallocated memory. Other threads may + // update this at any time and cause us to retry these operations. + // This value should be treated as "const" to avoid confusion through + // the code below but recognize that any failed compare-exchange operation + // involving it will cause it to be loaded with a more recent value. The + // code should either exit or restart the loop in that case. + /* const */ uint32_t freeptr = shared_meta()->freeptr.load(); + + // Allocation is lockless so we do all our caculation and then, if saving + // indicates a change has occurred since we started, scrap everything and + // start over. + for (;;) { + if (IsCorrupt()) + return kReferenceNull; + + if (freeptr + size > mem_size_) { + SetFlag(&shared_meta()->flags, kFlagFull); + return kReferenceNull; + } + + // Get pointer to the "free" block. If something has been allocated since + // the load of freeptr above, it is still safe as nothing will be written + // to that location until after the compare-exchange below. + volatile BlockHeader* const block = GetBlock(freeptr, 0, 0, false, true); + if (!block) { + SetCorrupt(); + return kReferenceNull; + } + + // An allocation cannot cross page boundaries. If it would, create a + // "wasted" block and begin again at the top of the next page. This + // area could just be left empty but we fill in the block header just + // for completeness sake. + const uint32_t page_free = mem_page_ - freeptr % mem_page_; + if (size > page_free) { + if (page_free <= sizeof(BlockHeader)) { + SetCorrupt(); + return kReferenceNull; + } + const uint32_t new_freeptr = freeptr + page_free; + if (shared_meta()->freeptr.compare_exchange_strong(freeptr, + new_freeptr)) { + block->size = page_free; + block->cookie = kBlockCookieWasted; + } + continue; + } + + // Don't leave a slice at the end of a page too small for anything. This + // can result in an allocation up to two alignment-sizes greater than the + // minimum required by requested-size + header + alignment. + if (page_free - size < sizeof(BlockHeader) + kAllocAlignment) + size = page_free; + + const uint32_t new_freeptr = freeptr + size; + if (new_freeptr > mem_size_) { + SetCorrupt(); + return kReferenceNull; + } + + // Save our work. Try again if another thread has completed an allocation + // while we were processing. A "weak" exchange would be permissable here + // because the code will just loop and try again but the above processing + // is significant so make the extra effort of a "strong" exchange. + if (!shared_meta()->freeptr.compare_exchange_strong(freeptr, new_freeptr)) + continue; + + // Given that all memory was zeroed before ever being given to an instance + // of this class and given that we only allocate in a monotomic fashion + // going forward, it must be that the newly allocated block is completely + // full of zeros. If we find anything in the block header that is NOT a + // zero then something must have previously run amuck through memory, + // writing beyond the allocated space and into unallocated space. + if (block->size != 0 || + block->cookie != kBlockCookieFree || + block->type_id != 0 || + block->next.load() != 0) { + SetCorrupt(); + return kReferenceNull; + } + + block->size = size; + block->cookie = kBlockCookieAllocated; + block->type_id = type_id; + return freeptr; + } +} + +void PersistentMemoryAllocator::GetMemoryInfo(MemoryInfo* meminfo) const { + uint32_t remaining = std::max(mem_size_ - shared_meta()->freeptr.load(), + (uint32_t)sizeof(BlockHeader)); + meminfo->total = mem_size_; + meminfo->free = IsCorrupt() ? 0 : remaining - sizeof(BlockHeader); +} + +void PersistentMemoryAllocator::MakeIterable(Reference ref) { + DCHECK(!readonly_); + if (IsCorrupt()) + return; + volatile BlockHeader* block = GetBlock(ref, 0, 0, false, false); + if (!block) // invalid reference + return; + if (block->next.load(std::memory_order_acquire) != 0) // Already iterable. + return; + block->next.store(kReferenceQueue, std::memory_order_release); // New tail. + + // Try to add this block to the tail of the queue. May take multiple tries. + // If so, tail will be automatically updated with a more recent value during + // compare-exchange operations. + uint32_t tail = shared_meta()->tailptr.load(std::memory_order_acquire); + for (;;) { + // Acquire the current tail-pointer released by previous call to this + // method and validate it. + block = GetBlock(tail, 0, 0, true, false); + if (!block) { + SetCorrupt(); + return; + } + + // Try to insert the block at the tail of the queue. The tail node always + // has an existing value of kReferenceQueue; if that is somehow not the + // existing value then another thread has acted in the meantime. A "strong" + // exchange is necessary so the "else" block does not get executed when + // that is not actually the case (which can happen with a "weak" exchange). + uint32_t next = kReferenceQueue; // Will get replaced with existing value. + if (block->next.compare_exchange_strong(next, ref, + std::memory_order_acq_rel, + std::memory_order_acquire)) { + // Update the tail pointer to the new offset. If the "else" clause did + // not exist, then this could be a simple Release_Store to set the new + // value but because it does, it's possible that other threads could add + // one or more nodes at the tail before reaching this point. We don't + // have to check the return value because it either operates correctly + // or the exact same operation has already been done (by the "else" + // clause) on some other thread. + shared_meta()->tailptr.compare_exchange_strong(tail, ref, + std::memory_order_release, + std::memory_order_relaxed); + return; + } else { + // In the unlikely case that a thread crashed or was killed between the + // update of "next" and the update of "tailptr", it is necessary to + // perform the operation that would have been done. There's no explicit + // check for crash/kill which means that this operation may also happen + // even when the other thread is in perfect working order which is what + // necessitates the CompareAndSwap above. + shared_meta()->tailptr.compare_exchange_strong(tail, next, + std::memory_order_acq_rel, + std::memory_order_acquire); + } + } +} + +void PersistentMemoryAllocator::CreateIterator(Iterator* state, + Reference starting_after) const { + if (starting_after) { + // Ensure that the starting point is a valid, iterable block. + const volatile BlockHeader* block = + GetBlock(starting_after, 0, 0, false, false); + if (!block || !block->next.load()) { + NOTREACHED(); + starting_after = kReferenceQueue; + } + } else { + // A zero beginning is really the Queue reference. + starting_after = kReferenceQueue; + } + + state->last = starting_after; + state->niter = 0; +} + +PersistentMemoryAllocator::Reference PersistentMemoryAllocator::GetNextIterable( + Iterator* state, + uint32_t* type_id) const { + const volatile BlockHeader* block = GetBlock(state->last, 0, 0, true, false); + if (!block) // invalid iterator state + return kReferenceNull; + + // The compiler and CPU can freely reorder all memory accesses on which + // there are no dependencies. It could, for example, move the load of + // "freeptr" above this point because there are no explicit dependencies + // between it and "next". If it did, however, then another block could + // be queued after that but before the following load meaning there is + // one more queued block than the future "detect loop by having more + // blocks that could fit before freeptr" will allow. + // + // By "acquiring" the "next" value here, it's synchronized to the enqueue + // of the node which in turn is synchronized to the allocation (which sets + // freeptr). Thus, the scenario above cannot happen. + uint32_t next = block->next.load(std::memory_order_acquire); + block = GetBlock(next, 0, 0, false, false); + if (!block) // no next allocation in queue + return kReferenceNull; + + // Memory corruption could cause a loop in the list. We need to detect + // that so as to not cause an infinite loop in the caller. We do this + // simply by making sure we don't iterate more than the absolute maximum + // number of allocations that could have been made. Callers are likely + // to loop multiple times before it is detected but at least it stops. + uint32_t freeptr = std::min( + shared_meta()->freeptr.load(std::memory_order_acquire), + mem_size_); + if (state->niter > freeptr / (sizeof(BlockHeader) + kAllocAlignment)) { + SetCorrupt(); + return kReferenceNull; + } + + state->last = next; + state->niter++; + *type_id = block->type_id; + + return next; +} + +// The "corrupted" state is held both locally and globally (shared). The +// shared flag can't be trusted since a malicious actor could overwrite it. +// Because corruption can be detected during read-only operations such as +// iteration, this method may be called by other "const" methods. In this +// case, it's safe to discard the constness and modify the local flag and +// maybe even the shared flag if the underlying data isn't actually read-only. +void PersistentMemoryAllocator::SetCorrupt() const { + LOG(ERROR) << "Corruption detected in shared-memory segment."; + const_cast*>(&corrupt_)->store(true); + if (!readonly_) { + SetFlag(const_cast*>(&shared_meta()->flags), + kFlagCorrupt); + } +} + +bool PersistentMemoryAllocator::IsCorrupt() const { + if (corrupt_.load() || CheckFlag(&shared_meta()->flags, kFlagCorrupt)) { + SetCorrupt(); // Make sure all indicators are set. + return true; + } + return false; +} + +bool PersistentMemoryAllocator::IsFull() const { + return CheckFlag(&shared_meta()->flags, kFlagFull); +} + +// Dereference a block |ref| and ensure that it's valid for the desired +// |type_id| and |size|. |special| indicates that we may try to access block +// headers not available to callers but still accessed by this module. By +// having internal dereferences go through this same function, the allocator +// is hardened against corruption. +const volatile PersistentMemoryAllocator::BlockHeader* +PersistentMemoryAllocator::GetBlock(Reference ref, uint32_t type_id, + uint32_t size, bool queue_ok, + bool free_ok) const { + // Validation of parameters. + if (ref % kAllocAlignment != 0) + return nullptr; + if (ref < (queue_ok ? kReferenceQueue : sizeof(SharedMetadata))) + return nullptr; + size += sizeof(BlockHeader); + if (ref + size > mem_size_) + return nullptr; + + // Validation of referenced block-header. + if (!free_ok) { + uint32_t freeptr = shared_meta()->freeptr.load(); + if (ref + size > freeptr) + return nullptr; + const volatile BlockHeader* const block = + reinterpret_cast(mem_base_ + ref); + if (block->size < size) + return nullptr; + if (ref != kReferenceQueue && block->cookie != kBlockCookieAllocated) + return nullptr; + if (type_id != 0 && block->type_id != type_id) + return nullptr; + } + + // Return pointer to block data. + return reinterpret_cast(mem_base_ + ref); +} + +const volatile void* PersistentMemoryAllocator::GetBlockData( + Reference ref, + uint32_t type_id, + uint32_t size) const { + DCHECK(size > 0); + const volatile BlockHeader* block = + GetBlock(ref, type_id, size, false, false); + if (!block) + return nullptr; + return reinterpret_cast(block) + sizeof(BlockHeader); +} + +void PersistentMemoryAllocator::UpdateTrackingHistograms() { + DCHECK(!readonly_); + if (used_histogram_) { + MemoryInfo meminfo; + GetMemoryInfo(&meminfo); + HistogramBase::Sample used_percent = static_cast( + ((meminfo.total - meminfo.free) * 100ULL / meminfo.total)); + used_histogram_->Add(used_percent); + } +} + + +//----- LocalPersistentMemoryAllocator ----------------------------------------- + +LocalPersistentMemoryAllocator::LocalPersistentMemoryAllocator( + size_t size, + uint64_t id, + base::StringPiece name) + : PersistentMemoryAllocator(memset(new char[size], 0, size), + size, 0, id, name, false) {} + +LocalPersistentMemoryAllocator::~LocalPersistentMemoryAllocator() { + delete [] mem_base_; +} + + +//----- SharedPersistentMemoryAllocator ---------------------------------------- +#if 0 + +SharedPersistentMemoryAllocator::SharedPersistentMemoryAllocator( + scoped_ptr memory, + uint64_t id, + base::StringPiece name, + bool read_only) + : PersistentMemoryAllocator(static_cast(memory->memory()), + memory->mapped_size(), 0, id, name, read_only), + shared_memory_(std::move(memory)) {} + +SharedPersistentMemoryAllocator::~SharedPersistentMemoryAllocator() {} + +// static +bool SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable( + const SharedMemory& memory) { + return IsMemoryAcceptable(memory.memory(), memory.mapped_size(), 0, true); +} + + +//----- FilePersistentMemoryAllocator ------------------------------------------ + +FilePersistentMemoryAllocator::FilePersistentMemoryAllocator( + scoped_ptr file, + uint64_t id, + base::StringPiece name) + : PersistentMemoryAllocator(const_cast(file->data()), + file->length(), 0, id, name, true), + mapped_file_(std::move(file)) {} + +FilePersistentMemoryAllocator::~FilePersistentMemoryAllocator() {} + +// static +bool FilePersistentMemoryAllocator::IsFileAcceptable( + const MemoryMappedFile& file) { + return IsMemoryAcceptable(file.data(), file.length(), 0, true); +} +#endif + +} // namespace base diff --git a/src/base/metrics/persistent_memory_allocator.h b/src/base/metrics/persistent_memory_allocator.h new file mode 100644 index 00000000..f75b1c0b --- /dev/null +++ b/src/base/metrics/persistent_memory_allocator.h @@ -0,0 +1,367 @@ +// Copyright (c) 2015 The Chromium 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 BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_ +#define BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_ + +#include +#include + +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" + +namespace base { + +class HistogramBase; +class MemoryMappedFile; +class SharedMemory; + +// Simple allocator for pieces of a memory block that may be persistent +// to some storage or shared across multiple processes. This class resides +// under base/metrics because it was written for that purpose. It is, +// however, fully general-purpose and can be freely moved to base/memory +// if other uses are found. +// +// This class provides for thread-secure (i.e. safe against other threads +// or processes that may be compromised and thus have malicious intent) +// allocation of memory within a designated block and also a mechanism by +// which other threads can learn of these allocations. +// +// There is (currently) no way to release an allocated block of data because +// doing so would risk invalidating pointers held by other processes and +// greatly complicate the allocation algorithm. +// +// Construction of this object can accept new, clean (i.e. zeroed) memory +// or previously initialized memory. In the first case, construction must +// be allowed to complete before letting other allocators attach to the same +// segment. In other words, don't share the segment until at least one +// allocator has been attached to it. +// +// Note that memory not in active use is not accessed so it is possible to +// use virtual memory, including memory-mapped files, as backing storage with +// the OS "pinning" new (zeroed) physical RAM pages only as they are needed. +class BASE_EXPORT PersistentMemoryAllocator { + public: + typedef uint32_t Reference; + + // Internal state information when iterating over memory allocations. + class Iterator { + public: + Iterator() : last(0) {} + + bool operator==(const Iterator& rhs) const { return last == rhs.last; } + bool operator!=(const Iterator& rhs) const { return last != rhs.last; } + + void clear() { last = 0; } + bool is_clear() const { return last == 0; } + + private: + friend class PersistentMemoryAllocator; + + Reference last; + uint32_t niter; + }; + + // Returned information about the internal state of the heap. + struct MemoryInfo { + size_t total; + size_t free; + }; + + enum : uint32_t { + kTypeIdAny = 0 // Match any type-id inside GetAsObject(). + }; + + // The allocator operates on any arbitrary block of memory. Creation and + // persisting or sharing of that block with another process is the + // responsibility of the caller. The allocator needs to know only the + // block's |base| address, the total |size| of the block, and any internal + // |page| size (zero if not paged) across which allocations should not span. + // The |id| is an arbitrary value the caller can use to identify a + // particular memory segment. It will only be loaded during the initial + // creation of the segment and can be checked by the caller for consistency. + // The |name|, if provided, is used to distinguish histograms for this + // allocator. Only the primary owner of the segment should define this value; + // other processes can learn it from the shared state. If the underlying + // memory is |readonly| then no changes will be made to it. The resulting + // object should be stored as a "const" pointer. + // + // PersistentMemoryAllocator does NOT take ownership of the memory block. + // The caller must manage it and ensure it stays available throughout the + // lifetime of this object. + // + // Memory segments for sharing must have had an allocator attached to them + // before actually being shared. If the memory segment was just created, it + // should be zeroed before being passed here. If it was an existing segment, + // the values here will be compared to copies stored in the shared segment + // as a guard against corruption. + // + // Make sure that the memory segment is acceptable (see IsMemoryAcceptable() + // method below) before construction if the definition of the segment can + // vary in any way at run-time. Invalid memory segments will cause a crash. + PersistentMemoryAllocator(void* base, size_t size, size_t page_size, + uint64_t id, base::StringPiece name, + bool readonly); + virtual ~PersistentMemoryAllocator(); + + // Check if memory segment is acceptable for creation of an Allocator. This + // doesn't do any analysis of the data and so doesn't guarantee that the + // contents are valid, just that the paramaters won't cause the program to + // abort. The IsCorrupt() method will report detection of data problems + // found during construction and general operation. + static bool IsMemoryAcceptable(const void* data, size_t size, + size_t page_size, bool readonly); + + // Get the internal identifier for this persistent memory segment. + uint64_t Id() const; + + // Get the internal name of this allocator (possibly an empty string). + const char* Name() const; + + // Is this segment open only for read? + bool IsReadonly() { return readonly_; } + + // Create internal histograms for tracking memory use and allocation sizes + // for allocator of |name| (which can simply be the result of Name()). This + // is done seperately from construction for situations such as when the + // histograms will be backed by memory provided by this very allocator. + // + // IMPORTANT: Callers must update tools/metrics/histograms/histograms.xml + // with the following histograms: + // UMA.PersistentAllocator.name.Allocs + // UMA.PersistentAllocator.name.UsedPct + void CreateTrackingHistograms(base::StringPiece name); + + // Direct access to underlying memory segment. If the segment is shared + // across threads or processes, reading data through these values does + // not guarantee consistency. Use with care. Do not write. + const void* data() const { return const_cast(mem_base_); } + size_t length() const { return mem_size_; } + size_t used() const; + + // Get an object referenced by a |ref|. For safety reasons, the |type_id| + // code and size-of(|T|) are compared to ensure the reference is valid + // and cannot return an object outside of the memory segment. A |type_id| of + // kTypeIdAny (zero) will match any though the size is still checked. NULL is + // returned if any problem is detected, such as corrupted storage or incorrect + // parameters. Callers MUST check that the returned value is not-null EVERY + // TIME before accessing it or risk crashing! Once dereferenced, the pointer + // is safe to reuse forever. + // + // NOTE: Though this method will guarantee that an object of the specified + // type can be accessed without going outside the bounds of the memory + // segment, it makes no guarantees of the validity of the data within the + // object itself. If it is expected that the contents of the segment could + // be compromised with malicious intent, the object must be hardened as well. + // + // Though the persistent data may be "volatile" if it is shared with + // other processes, such is not necessarily the case. The internal + // "volatile" designation is discarded so as to not propagate the viral + // nature of that keyword to the caller. It can add it back, if necessary, + // based on knowledge of how the allocator is being used. + template + T* GetAsObject(Reference ref, uint32_t type_id) { + static_assert(!std::is_polymorphic::value, "no polymorphic objects"); + return const_cast( + reinterpret_cast(GetBlockData(ref, type_id, sizeof(T)))); + } + template + const T* GetAsObject(Reference ref, uint32_t type_id) const { + static_assert(!std::is_polymorphic::value, "no polymorphic objects"); + return const_cast( + reinterpret_cast(GetBlockData( + ref, type_id, sizeof(T)))); + } + + // Get the number of bytes allocated to a block. This is useful when storing + // arrays in order to validate the ending boundary. The returned value will + // include any padding added to achieve the required alignment and so could + // be larger than given in the original Allocate() request. + size_t GetAllocSize(Reference ref) const; + + // Access the internal "type" of an object. This generally isn't necessary + // but can be used to "clear" the type and so effectively mark it as deleted + // even though the memory stays valid and allocated. + uint32_t GetType(Reference ref) const; + void SetType(Reference ref, uint32_t type_id); + + // Reserve space in the memory segment of the desired |size| and |type_id|. + // A return value of zero indicates the allocation failed, otherwise the + // returned reference can be used by any process to get a real pointer via + // the GetAsObject() call. + Reference Allocate(size_t size, uint32_t type_id); + + // Allocated objects can be added to an internal list that can then be + // iterated over by other processes. If an allocated object can be found + // another way, such as by having its reference within a different object + // that will be made iterable, then this call is not necessary. This always + // succeeds unless corruption is detected; check IsCorrupted() to find out. + // Once an object is made iterable, its position in iteration can never + // change; new iterable objects will always be added after it in the series. + void MakeIterable(Reference ref); + + // Get the information about the amount of free space in the allocator. The + // amount of free space should be treated as approximate due to extras from + // alignment and metadata. Concurrent allocations from other threads will + // also make the true amount less than what is reported. + void GetMemoryInfo(MemoryInfo* meminfo) const; + + // Iterating uses a |state| structure (initialized by CreateIterator) and + // returns both the reference to the object as well as the |type_id| of + // that object. A zero return value indicates there are currently no more + // objects to be found but future attempts can be made without having to + // reset the iterator to "first". Creating an iterator |starting_after| + // a known iterable object allows "resume" from that point with the next + // call to GetNextIterable returning the object after it. + void CreateIterator(Iterator* state) const { CreateIterator(state, 0); }; + void CreateIterator(Iterator* state, Reference starting_after) const; + Reference GetNextIterable(Iterator* state, uint32_t* type_id) const; + + // If there is some indication that the memory has become corrupted, + // calling this will attempt to prevent further damage by indicating to + // all processes that something is not as expected. + void SetCorrupt() const; + + // This can be called to determine if corruption has been detected in the + // segment, possibly my a malicious actor. Once detected, future allocations + // will fail and iteration may not locate all objects. + bool IsCorrupt() const; + + // Flag set if an allocation has failed because the memory segment was full. + bool IsFull() const; + + // Update those "tracking" histograms which do not get updates during regular + // operation, such as how much memory is currently used. This should be + // called before such information is to be displayed or uploaded. + void UpdateTrackingHistograms(); + + protected: + volatile char* const mem_base_; // Memory base. (char so sizeof guaranteed 1) + const uint32_t mem_size_; // Size of entire memory segment. + const uint32_t mem_page_; // Page size allocations shouldn't cross. + + private: + struct SharedMetadata; + struct BlockHeader; + static const uint32_t kAllocAlignment; + static const Reference kReferenceQueue; + static const Reference kReferenceNull; + + // The shared metadata is always located at the top of the memory segment. + // These convenience functions eliminate constant casting of the base + // pointer within the code. + const SharedMetadata* shared_meta() const { + return reinterpret_cast( + const_cast(mem_base_)); + } + SharedMetadata* shared_meta() { + return reinterpret_cast(const_cast(mem_base_)); + } + + // Actual method for doing the allocation. + Reference AllocateImpl(size_t size, uint32_t type_id); + + // Get the block header associated with a specific reference. + const volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id, + uint32_t size, bool queue_ok, + bool free_ok) const; + volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id, uint32_t size, + bool queue_ok, bool free_ok) { + return const_cast( + const_cast(this)->GetBlock( + ref, type_id, size, queue_ok, free_ok)); + } + + // Get the actual data within a block associated with a specific reference. + const volatile void* GetBlockData(Reference ref, uint32_t type_id, + uint32_t size) const; + volatile void* GetBlockData(Reference ref, uint32_t type_id, + uint32_t size) { + return const_cast( + const_cast(this)->GetBlockData( + ref, type_id, size)); + } + + const bool readonly_; // Indicates access to read-only memory. + std::atomic corrupt_; // Local version of "corrupted" flag. + + HistogramBase* allocs_histogram_; // Histogram recording allocs. + HistogramBase* used_histogram_; // Histogram recording used space. + + friend class PersistentMemoryAllocatorTest; + FRIEND_TEST_ALL_PREFIXES(PersistentMemoryAllocatorTest, AllocateAndIterate); + DISALLOW_COPY_AND_ASSIGN(PersistentMemoryAllocator); +}; + + +// This allocator uses a local memory block it allocates from the general +// heap. It is generally used when some kind of "death rattle" handler will +// save the contents to persistent storage during process shutdown. It is +// also useful for testing. +class BASE_EXPORT LocalPersistentMemoryAllocator + : public PersistentMemoryAllocator { + public: + LocalPersistentMemoryAllocator(size_t size, uint64_t id, + base::StringPiece name); + ~LocalPersistentMemoryAllocator() override; + + private: + DISALLOW_COPY_AND_ASSIGN(LocalPersistentMemoryAllocator); +}; + + +// This allocator takes a shared-memory object and performs allocation from +// it. The memory must be previously mapped via Map() or MapAt(). The allocator +// takes ownership of the memory object. +class BASE_EXPORT SharedPersistentMemoryAllocator + : public PersistentMemoryAllocator { + public: + SharedPersistentMemoryAllocator(scoped_ptr memory, uint64_t id, + base::StringPiece name, bool read_only); + ~SharedPersistentMemoryAllocator() override; + + SharedMemory* shared_memory() { return shared_memory_.get(); } + + // Ensure that the memory isn't so invalid that it won't crash when passing it + // to the allocator. This doesn't guarantee the data is valid, just that it + // won't cause the program to abort. The existing IsCorrupt() call will handle + // the rest. + static bool IsSharedMemoryAcceptable(const SharedMemory& memory); + + private: + scoped_ptr shared_memory_; + + DISALLOW_COPY_AND_ASSIGN(SharedPersistentMemoryAllocator); +}; + + +// This allocator takes a memory-mapped file object and performs allocation +// from it. The allocator takes ownership of the file object. Only read access +// is provided due to limitions of the MemoryMappedFile class. +class BASE_EXPORT FilePersistentMemoryAllocator + : public PersistentMemoryAllocator { + public: + FilePersistentMemoryAllocator(scoped_ptr file, uint64_t id, + base::StringPiece name); + ~FilePersistentMemoryAllocator() override; + + // Ensure that the file isn't so invalid that it won't crash when passing it + // to the allocator. This doesn't guarantee the file is valid, just that it + // won't cause the program to abort. The existing IsCorrupt() call will handle + // the rest. + static bool IsFileAcceptable(const MemoryMappedFile& file); + + private: + scoped_ptr mapped_file_; + + DISALLOW_COPY_AND_ASSIGN(FilePersistentMemoryAllocator); +}; + +} // namespace base + +#endif // BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_ diff --git a/src/base/metrics/sample_map.cc b/src/base/metrics/sample_map.cc index a691243f..e276b916 100644 --- a/src/base/metrics/sample_map.cc +++ b/src/base/metrics/sample_map.cc @@ -19,7 +19,7 @@ SampleMap::~SampleMap() {} void SampleMap::Accumulate(Sample value, Count count) { sample_counts_[value] += count; - IncreaseSum(count * value); + IncreaseSum(static_cast(count) * value); IncreaseRedundantCount(count); } diff --git a/src/base/metrics/sample_vector.cc b/src/base/metrics/sample_vector.cc index 46faef06..ccb94317 100644 --- a/src/base/metrics/sample_vector.cc +++ b/src/base/metrics/sample_vector.cc @@ -43,7 +43,7 @@ void SampleVector::Accumulate(Sample value, Count count) { size_t bucket_index = GetBucketIndex(value); subtle::NoBarrier_Store(&counts_[bucket_index], subtle::NoBarrier_Load(&counts_[bucket_index]) + count); - IncreaseSum(count * value); + IncreaseSum(static_cast(count) * value); IncreaseRedundantCount(count); } diff --git a/src/base/metrics/sample_vector.h b/src/base/metrics/sample_vector.h index 0317869f..86319ea0 100644 --- a/src/base/metrics/sample_vector.h +++ b/src/base/metrics/sample_vector.h @@ -54,6 +54,7 @@ class BASE_EXPORT SampleVector : public HistogramSamples { private: FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts); + FRIEND_TEST_ALL_PREFIXES(SharedHistogramTest, CorruptSampleCounts); // In the case where this class manages the memory, here it is. std::vector local_counts_; diff --git a/src/base/metrics/sparse_histogram.cc b/src/base/metrics/sparse_histogram.cc index 1ed1e786..5653456a 100644 --- a/src/base/metrics/sparse_histogram.cc +++ b/src/base/metrics/sparse_histogram.cc @@ -47,7 +47,7 @@ HistogramType SparseHistogram::GetHistogramType() const { bool SparseHistogram::HasConstructionArguments( Sample expected_minimum, Sample expected_maximum, - size_t expected_bucket_count) const { + uint32_t expected_bucket_count) const { // SparseHistogram never has min/max/bucket_count limit. return false; } @@ -77,6 +77,17 @@ scoped_ptr SparseHistogram::SnapshotSamples() const { return std::move(snapshot); } +scoped_ptr SparseHistogram::SnapshotDelta() { + scoped_ptr snapshot(new SampleMap(name_hash())); + base::AutoLock auto_lock(lock_); + snapshot->Add(samples_); + + // Subtract what was previously logged and update that information. + snapshot->Subtract(logged_samples_); + logged_samples_.Add(*snapshot); + return std::move(snapshot); +} + void SparseHistogram::AddSamples(const HistogramSamples& samples) { base::AutoLock auto_lock(lock_); samples_.Add(samples); diff --git a/src/base/metrics/sparse_histogram.h b/src/base/metrics/sparse_histogram.h index a77c020d..7f363133 100644 --- a/src/base/metrics/sparse_histogram.h +++ b/src/base/metrics/sparse_histogram.h @@ -22,6 +22,27 @@ namespace base { +// Sparse histograms are well suited for recording counts of exact sample values +// that are sparsely distributed over a large range. +// +// The implementation uses a lock and a map, whereas other histogram types use a +// vector and no lock. It is thus more costly to add values to, and each value +// stored has more overhead, compared to the other histogram types. However it +// may be more efficient in memory if the total number of sample values is small +// compared to the range of their values. +// +// UMA_HISTOGRAM_ENUMERATION would be better suited for a smaller range of +// enumerations that are (nearly) contiguous. Also for code that is expected to +// run often or in a tight loop. +// +// UMA_HISTOGRAM_SPARSE_SLOWLY is good for sparsely distributed and or +// infrequently recorded values. +// +// For instance, Sqlite.Version.* are SPARSE because for any given database, +// there's going to be exactly one version logged, meaning no gain to having a +// pre-allocated vector of slots once the fleet gets to version 4 or 5 or 10. +// Likewise Sqlite.Error.* are SPARSE, because most databases generate few or no +// errors and there are large gaps in the set of possible errors. #define UMA_HISTOGRAM_SPARSE_SLOWLY(name, sample) \ do { \ base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( \ @@ -44,12 +65,13 @@ class BASE_EXPORT SparseHistogram : public HistogramBase { HistogramType GetHistogramType() const override; bool HasConstructionArguments(Sample expected_minimum, Sample expected_maximum, - size_t expected_bucket_count) const override; + uint32_t expected_bucket_count) const override; void Add(Sample value) override; void AddCount(Sample value, int count) override; void AddSamples(const HistogramSamples& samples) override; bool AddSamplesFromPickle(base::PickleIterator* iter) override; scoped_ptr SnapshotSamples() const override; + scoped_ptr SnapshotDelta() override; void WriteHTMLGraph(std::string* output) const override; void WriteAscii(std::string* output) const override; @@ -86,6 +108,7 @@ class BASE_EXPORT SparseHistogram : public HistogramBase { mutable base::Lock lock_; SampleMap samples_; + SampleMap logged_samples_; DISALLOW_COPY_AND_ASSIGN(SparseHistogram); }; diff --git a/src/base/metrics/statistics_recorder.cc b/src/base/metrics/statistics_recorder.cc index 15e48d8a..d0fa2add 100644 --- a/src/base/metrics/statistics_recorder.cc +++ b/src/base/metrics/statistics_recorder.cc @@ -17,13 +17,56 @@ #include "base/values.h" namespace { + // Initialize histogram statistics gathering system. base::LazyInstance::Leaky g_statistics_recorder_ = LAZY_INSTANCE_INITIALIZER; + +bool HistogramNameLesser(const base::HistogramBase* a, + const base::HistogramBase* b) { + return a->histogram_name() < b->histogram_name(); +} + } // namespace namespace base { +StatisticsRecorder::HistogramIterator::HistogramIterator( + const HistogramMap::iterator& iter, bool include_persistent) + : iter_(iter), + include_persistent_(include_persistent) { +} + +StatisticsRecorder::HistogramIterator::HistogramIterator( + const HistogramIterator& rhs) + : iter_(rhs.iter_), + include_persistent_(rhs.include_persistent_) { +} + +StatisticsRecorder::HistogramIterator::~HistogramIterator() {} + +StatisticsRecorder::HistogramIterator& +StatisticsRecorder::HistogramIterator::operator++() { + const HistogramMap::iterator histograms_end = histograms_->end(); + if (iter_ == histograms_end || lock_ == NULL) + return *this; + + base::AutoLock auto_lock(*lock_); + + for (;;) { + ++iter_; + if (iter_ == histograms_end) + break; + if (!include_persistent_ && (iter_->second->flags() & + HistogramBase::kIsPersistent)) { + continue; + } + break; + } + + return *this; +} + // static void StatisticsRecorder::Initialize() { // Ensure that an instance of the StatisticsRecorder object is created. @@ -59,7 +102,8 @@ HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate( histogram_to_return = histogram; } else { const std::string& name = histogram->histogram_name(); - uint64_t name_hash = histogram->name_hash(); + const uint64_t name_hash = histogram->name_hash(); + DCHECK_NE(0U, name_hash); HistogramMap::iterator it = histograms_->find(name_hash); if (histograms_->end() == it) { (*histograms_)[name_hash] = histogram; @@ -142,6 +186,7 @@ void StatisticsRecorder::WriteHTMLGraph(const std::string& query, Histograms snapshot; GetSnapshot(query, &snapshot); + std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser); for (const HistogramBase* histogram : snapshot) { histogram->WriteHTMLGraph(output); output->append("


"); @@ -160,6 +205,7 @@ void StatisticsRecorder::WriteGraph(const std::string& query, Histograms snapshot; GetSnapshot(query, &snapshot); + std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser); for (const HistogramBase* histogram : snapshot) { histogram->WriteAscii(output); output->append("\n"); @@ -226,7 +272,7 @@ void StatisticsRecorder::GetBucketRanges( } // static -HistogramBase* StatisticsRecorder::FindHistogram(const std::string& name) { +HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) { if (lock_ == NULL) return NULL; base::AutoLock auto_lock(*lock_); @@ -240,6 +286,32 @@ HistogramBase* StatisticsRecorder::FindHistogram(const std::string& name) { return it->second; } +// static +StatisticsRecorder::HistogramIterator StatisticsRecorder::begin( + bool include_persistent) { + return HistogramIterator(histograms_->begin(), include_persistent); +} + +// static +StatisticsRecorder::HistogramIterator StatisticsRecorder::end() { + return HistogramIterator(histograms_->end(), true); +} + +// static +void StatisticsRecorder::GetSnapshot(const std::string& query, + Histograms* snapshot) { + if (lock_ == NULL) + return; + base::AutoLock auto_lock(*lock_); + if (histograms_ == NULL) + return; + + for (const auto& entry : *histograms_) { + if (entry.second->histogram_name().find(query) != std::string::npos) + snapshot->push_back(entry.second); + } +} + // static bool StatisticsRecorder::SetCallback( const std::string& name, @@ -255,7 +327,7 @@ bool StatisticsRecorder::SetCallback( return false; callbacks_->insert(std::make_pair(name, cb)); - HistogramMap::iterator it = histograms_->find(HashMetricName(name)); + auto it = histograms_->find(HashMetricName(name)); if (it != histograms_->end()) { DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; it->second->SetFlags(HistogramBase::kCallbackExists); @@ -275,7 +347,7 @@ void StatisticsRecorder::ClearCallback(const std::string& name) { callbacks_->erase(name); // We also clear the flag from the histogram (if it exists). - HistogramMap::iterator it = histograms_->find(HashMetricName(name)); + auto it = histograms_->find(HashMetricName(name)); if (it != histograms_->end()) { DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; it->second->ClearFlags(HistogramBase::kCallbackExists); @@ -296,19 +368,16 @@ StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback( : OnSampleCallback(); } -// private static -void StatisticsRecorder::GetSnapshot(const std::string& query, - Histograms* snapshot) { - if (lock_ == NULL) - return; - base::AutoLock auto_lock(*lock_); - if (histograms_ == NULL) - return; +// static +void StatisticsRecorder::ResetForTesting() { + // Just call the private version that is used also by the destructor. + Reset(); +} - for (const auto& entry : *histograms_) { - if (entry.second->histogram_name().find(query) != std::string::npos) - snapshot->push_back(entry.second); - } +// static +void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) { + if (histograms_) + histograms_->erase(HashMetricName(name.as_string())); } // This singleton instance should be started during the single threaded portion @@ -334,17 +403,19 @@ StatisticsRecorder::StatisticsRecorder() { AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this); } -// static -void StatisticsRecorder::DumpHistogramsToVlog(void* instance) { - std::string output; - StatisticsRecorder::WriteGraph(std::string(), &output); - VLOG(1) << output; -} - StatisticsRecorder::~StatisticsRecorder() { DCHECK(histograms_ && ranges_ && lock_); - // Clean up. + // Global clean up. + Reset(); +} + +// static +void StatisticsRecorder::Reset() { + // If there's no lock then there is nothing to reset. + if (!lock_) + return; + scoped_ptr histograms_deleter; scoped_ptr callbacks_deleter; scoped_ptr ranges_deleter; @@ -362,6 +433,13 @@ StatisticsRecorder::~StatisticsRecorder() { // We are going to leak the histograms and the ranges. } +// static +void StatisticsRecorder::DumpHistogramsToVlog(void* instance) { + std::string output; + StatisticsRecorder::WriteGraph(std::string(), &output); + VLOG(1) << output; +} + // static StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; diff --git a/src/base/metrics/statistics_recorder.h b/src/base/metrics/statistics_recorder.h index b1d182e9..36b2f308 100644 --- a/src/base/metrics/statistics_recorder.h +++ b/src/base/metrics/statistics_recorder.h @@ -23,6 +23,7 @@ #include "base/lazy_instance.h" #include "base/macros.h" #include "base/metrics/histogram_base.h" +#include "base/strings/string_piece.h" namespace base { @@ -31,8 +32,37 @@ class Lock; class BASE_EXPORT StatisticsRecorder { public: + typedef std::map HistogramMap; // Key is name-hash. typedef std::vector Histograms; + // A class for iterating over the histograms held within this global resource. + class BASE_EXPORT HistogramIterator { + public: + HistogramIterator(const HistogramMap::iterator& iter, + bool include_persistent); + HistogramIterator(const HistogramIterator& rhs); // Must be copyable. + ~HistogramIterator(); + + HistogramIterator& operator++(); + HistogramIterator operator++(int) { + HistogramIterator tmp(*this); + operator++(); + return tmp; + } + + bool operator==(const HistogramIterator& rhs) const { + return iter_ == rhs.iter_; + } + bool operator!=(const HistogramIterator& rhs) const { + return iter_ != rhs.iter_; + } + HistogramBase* operator*() { return iter_->second; } + + private: + HistogramMap::iterator iter_; + const bool include_persistent_; + }; + // Initializes the StatisticsRecorder system. Safe to call multiple times. static void Initialize(); @@ -70,7 +100,11 @@ class BASE_EXPORT StatisticsRecorder { // Find a histogram by name. It matches the exact name. This method is thread // safe. It returns NULL if a matching histogram is not found. - static HistogramBase* FindHistogram(const std::string& name); + static HistogramBase* FindHistogram(base::StringPiece name); + + // Support for iterating over known histograms. + static HistogramIterator begin(bool include_persistent); + static HistogramIterator end(); // GetSnapshot copies some of the pointers to registered histograms into the // caller supplied vector (Histograms). Only histograms which have |query| as @@ -96,11 +130,16 @@ class BASE_EXPORT StatisticsRecorder { // histogram. This method is thread safe. static OnSampleCallback FindCallback(const std::string& histogram_name); - private: - // We keep all registered histograms in a map, indexed by the hash of the - // name of the histogram. - typedef std::map HistogramMap; + // Clears all of the known histograms and resets static variables to a + // state that allows a new initialization. + static void ResetForTesting(); + // Removes a histogram from the internal set of known ones. This can be + // necessary during testing persistent histograms where the underlying + // memory is being released. + static void ForgetHistogramForTesting(base::StringPiece name); + + private: // We keep a map of callbacks to histograms, so that as histograms are // created, we can set the callback properly. typedef std::map CallbackMap; @@ -115,6 +154,7 @@ class BASE_EXPORT StatisticsRecorder { friend class HistogramSnapshotManagerTest; friend class HistogramTest; friend class JsonPrefStoreTest; + friend class SharedHistogramTest; friend class SparseHistogramTest; friend class StatisticsRecorderTest; FRIEND_TEST_ALL_PREFIXES(HistogramDeltaSerializationTest, @@ -126,6 +166,7 @@ class BASE_EXPORT StatisticsRecorder { StatisticsRecorder(); ~StatisticsRecorder(); + static void Reset(); static void DumpHistogramsToVlog(void* instance); static HistogramMap* histograms_; diff --git a/src/base/move.h b/src/base/move.h index 66d85212..42242b42 100644 --- a/src/base/move.h +++ b/src/base/move.h @@ -5,6 +5,7 @@ #ifndef BASE_MOVE_H_ #define BASE_MOVE_H_ +// TODO(dcheng): Remove this header. #include #include "base/compiler_specific.h" @@ -25,13 +26,11 @@ // into a scoped_ptr. The class must define a move constructor and move // assignment operator to make this work. // -// This version of the macro adds a Pass() function and a cryptic -// MoveOnlyTypeForCPP03 typedef for the base::Callback implementation to use. -// See IsMoveOnlyType template and its usage in base/callback_internal.h -// for more details. +// This version of the macro adds a cryptic MoveOnlyTypeForCPP03 typedef for the +// base::Callback implementation to use. See IsMoveOnlyType template and its +// usage in base/callback_internal.h for more details. // TODO(crbug.com/566182): Remove this macro and use DISALLOW_COPY_AND_ASSIGN // everywhere instead. -#if defined(OS_ANDROID) || (defined(OS_LINUX) && !defined(OS_CHROMEOS)) #define DISALLOW_COPY_AND_ASSIGN_WITH_MOVE_FOR_BIND(type) \ private: \ type(const type&) = delete; \ @@ -41,17 +40,5 @@ typedef void MoveOnlyTypeForCPP03; \ \ private: -#else -#define DISALLOW_COPY_AND_ASSIGN_WITH_MOVE_FOR_BIND(type) \ - private: \ - type(const type&) = delete; \ - void operator=(const type&) = delete; \ - \ - public: \ - type&& Pass() WARN_UNUSED_RESULT { return std::move(*this); } \ - typedef void MoveOnlyTypeForCPP03; \ - \ - private: -#endif #endif // BASE_MOVE_H_ diff --git a/src/base/numerics/safe_conversions_impl.h b/src/base/numerics/safe_conversions_impl.h index 02e68e25..4d260a07 100644 --- a/src/base/numerics/safe_conversions_impl.h +++ b/src/base/numerics/safe_conversions_impl.h @@ -8,10 +8,6 @@ #include #include -#include - -#include "base/template_util.h" - namespace base { namespace internal { diff --git a/src/base/numerics/safe_math.h b/src/base/numerics/safe_math.h index d169690a..9757f1c8 100644 --- a/src/base/numerics/safe_math.h +++ b/src/base/numerics/safe_math.h @@ -6,6 +6,7 @@ #define BASE_NUMERICS_SAFE_MATH_H_ #include +#include #include "base/numerics/safe_math_impl.h" @@ -190,7 +191,7 @@ class CheckedNumeric { template static CheckedNumeric cast( const CheckedNumeric& u, - typename std::enable_if::value, int>::type = 0) { + typename std::enable_if::value, int>::type = 0) { return u; } diff --git a/src/base/numerics/safe_math_impl.h b/src/base/numerics/safe_math_impl.h index 4fbcc045..103752d8 100644 --- a/src/base/numerics/safe_math_impl.h +++ b/src/base/numerics/safe_math_impl.h @@ -14,7 +14,6 @@ #include #include "base/numerics/safe_conversions.h" -#include "base/template_util.h" namespace base { namespace internal { diff --git a/src/base/os_compat_nacl.cc b/src/base/os_compat_nacl.cc deleted file mode 100644 index 58fe93e0..00000000 --- a/src/base/os_compat_nacl.cc +++ /dev/null @@ -1,30 +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/os_compat_nacl.h" - -#include -#include - -#if !defined (__GLIBC__) - -extern "C" { -// Native Client has no timegm(). -time_t timegm(struct tm* tm) { - time_t ret; - char* tz; - tz = getenv("TZ"); - setenv("TZ", "", 1); - tzset(); - ret = mktime(tm); - if (tz) - setenv("TZ", tz, 1); - else - unsetenv("TZ"); - tzset(); - return ret; -} -} // extern "C" - -#endif // !defined (__GLIBC__) diff --git a/src/base/os_compat_nacl.h b/src/base/os_compat_nacl.h deleted file mode 100644 index 13e0e3f8..00000000 --- a/src/base/os_compat_nacl.h +++ /dev/null @@ -1,16 +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 BASE_OS_COMPAT_NACL_H_ -#define BASE_OS_COMPAT_NACL_H_ - -#include - -#if !defined (__GLIBC__) -// NaCl has no timegm(). -extern "C" time_t timegm(struct tm* const t); -#endif // !defined (__GLIBC__) - -#endif // BASE_OS_COMPAT_NACL_H_ - diff --git a/src/base/pickle.cc b/src/base/pickle.cc index d83391bb..94f3b1c9 100644 --- a/src/base/pickle.cc +++ b/src/base/pickle.cc @@ -11,6 +11,7 @@ #include "base/bits.h" #include "base/macros.h" +#include "base/numerics/safe_conversions.h" #include "build/build_config.h" namespace base { @@ -89,7 +90,15 @@ bool PickleIterator::ReadInt(int* result) { } bool PickleIterator::ReadLong(long* result) { - return ReadBuiltinType(result); + // Always read long as a 64-bit value to ensure compatibility between 32-bit + // and 64-bit processes. + int64_t result_int64 = 0; + if (!ReadBuiltinType(&result_int64)) + return false; + // CHECK if the cast truncates the value so that we know to change this IPC + // parameter to use int64_t. + *result = base::checked_cast(result_int64); + return true; } bool PickleIterator::ReadUInt16(uint16_t* result) { @@ -108,16 +117,6 @@ bool PickleIterator::ReadUInt64(uint64_t* result) { return ReadBuiltinType(result); } -bool PickleIterator::ReadSizeT(size_t* result) { - // Always read size_t as a 64-bit value to ensure compatibility between 32-bit - // and 64-bit processes. - uint64_t result_uint64 = 0; - bool success = ReadBuiltinType(&result_uint64); - *result = static_cast(result_uint64); - // Fail if the cast above truncates the value. - return success && (*result == result_uint64); -} - bool PickleIterator::ReadFloat(float* result) { // crbug.com/315213 // The source data may not be properly aligned, and unaligned float reads @@ -208,6 +207,43 @@ bool PickleIterator::ReadBytes(const char** data, int length) { return true; } +PickleSizer::PickleSizer() {} + +PickleSizer::~PickleSizer() {} + +void PickleSizer::AddString(const StringPiece& value) { + AddInt(); + AddBytes(static_cast(value.size())); +} + +void PickleSizer::AddString16(const StringPiece16& value) { + AddInt(); + AddBytes(static_cast(value.size() * sizeof(char16))); +} + +void PickleSizer::AddData(int length) { + CHECK_GE(length, 0); + AddInt(); + AddBytes(length); +} + +void PickleSizer::AddBytes(int length) { + payload_size_ += bits::Align(length, sizeof(uint32_t)); +} + +template void PickleSizer::AddBytesStatic() { + DCHECK_LE(length, static_cast(std::numeric_limits::max())); + AddBytes(length); +} + +template void PickleSizer::AddBytesStatic<2>(); +template void PickleSizer::AddBytesStatic<4>(); +template void PickleSizer::AddBytesStatic<8>(); + +Pickle::Attachment::Attachment() {} + +Pickle::Attachment::~Attachment() {} + // Payload is uint32_t aligned. Pickle::Pickle() @@ -322,6 +358,19 @@ void Pickle::Reserve(size_t length) { Resize(capacity_after_header_ * 2 + new_size); } +bool Pickle::WriteAttachment(scoped_refptr attachment) { + return false; +} + +bool Pickle::ReadAttachment(base::PickleIterator* iter, + scoped_refptr* attachment) const { + return false; +} + +bool Pickle::HasAttachments() const { + return false; +} + void Pickle::Resize(size_t new_capacity) { CHECK_NE(capacity_after_header_, kCapacityReadOnly); capacity_after_header_ = bits::Align(new_capacity, kPayloadUnit); diff --git a/src/base/pickle.h b/src/base/pickle.h index 02bc432a..37b1dd7a 100644 --- a/src/base/pickle.h +++ b/src/base/pickle.h @@ -14,9 +14,16 @@ #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" #include "base/logging.h" +#include "base/memory/ref_counted.h" #include "base/strings/string16.h" #include "base/strings/string_piece.h" +#if 0 +#if defined(OS_POSIX) +#include "base/files/file.h" +#endif +#endif + namespace base { class Pickle; @@ -40,7 +47,6 @@ class BASE_EXPORT PickleIterator { bool ReadUInt32(uint32_t* result) WARN_UNUSED_RESULT; bool ReadInt64(int64_t* result) WARN_UNUSED_RESULT; bool ReadUInt64(uint64_t* result) WARN_UNUSED_RESULT; - bool ReadSizeT(size_t* result) WARN_UNUSED_RESULT; bool ReadFloat(float* result) WARN_UNUSED_RESULT; bool ReadDouble(double* result) WARN_UNUSED_RESULT; bool ReadString(std::string* result) WARN_UNUSED_RESULT; @@ -104,6 +110,41 @@ class BASE_EXPORT PickleIterator { FRIEND_TEST_ALL_PREFIXES(PickleTest, GetReadPointerAndAdvance); }; +// This class provides an interface analogous to base::Pickle's WriteFoo() +// methods and can be used to accurately compute the size of a hypothetical +// Pickle's payload without having to reference the Pickle implementation. +class BASE_EXPORT PickleSizer { + public: + PickleSizer(); + ~PickleSizer(); + + // Returns the computed size of the payload. + size_t payload_size() const { return payload_size_; } + + void AddBool() { return AddInt(); } + void AddInt() { AddPOD(); } + void AddLong() { AddPOD(); } + void AddUInt16() { return AddPOD(); } + void AddUInt32() { return AddPOD(); } + void AddInt64() { return AddPOD(); } + void AddUInt64() { return AddPOD(); } + void AddFloat() { return AddPOD(); } + void AddDouble() { return AddPOD(); } + void AddString(const StringPiece& value); + void AddString16(const StringPiece16& value); + void AddData(int length); + void AddBytes(int length); + + private: + // Just like AddBytes() but with a compile-time size for performance. + template void BASE_EXPORT AddBytesStatic(); + + template + void AddPOD() { AddBytesStatic(); } + + size_t payload_size_ = 0; +}; + // This class provides facilities for basic binary value packing and unpacking. // // The Pickle class supports appending primitive values (ints, strings, etc.) @@ -123,6 +164,21 @@ class BASE_EXPORT PickleIterator { // class BASE_EXPORT Pickle { public: + // Auxiliary data attached to a Pickle. Pickle must be subclassed along with + // this interface in order to provide a concrete implementation of support + // for attachments. The base Pickle implementation does not accept + // attachments. + class BASE_EXPORT Attachment : public RefCountedThreadSafe { + public: + Attachment(); + + protected: + friend class RefCountedThreadSafe; + virtual ~Attachment(); + + DISALLOW_COPY_AND_ASSIGN(Attachment); + }; + // Initialize a Pickle object using the default header size. Pickle(); @@ -173,23 +229,15 @@ class BASE_EXPORT Pickle { bool WriteInt(int value) { return WritePOD(value); } - // WARNING: DO NOT USE THIS METHOD IF PICKLES ARE PERSISTED IN ANY WAY. - // It will write whatever a "long" is on this architecture. On 32-bit - // platforms, it is 32 bits. On 64-bit platforms, it is 64 bits. If persisted - // pickles are still around after upgrading to 64-bit, or if they are copied - // between dissimilar systems, YOUR PICKLES WILL HAVE GONE BAD. - bool WriteLongUsingDangerousNonPortableLessPersistableForm(long value) { - return WritePOD(value); + bool WriteLong(long value) { + // Always write long as a 64-bit value to ensure compatibility between + // 32-bit and 64-bit processes. + return WritePOD(static_cast(value)); } bool WriteUInt16(uint16_t value) { return WritePOD(value); } bool WriteUInt32(uint32_t value) { return WritePOD(value); } bool WriteInt64(int64_t value) { return WritePOD(value); } bool WriteUInt64(uint64_t value) { return WritePOD(value); } - bool WriteSizeT(size_t value) { - // Always write size_t as a 64-bit value to ensure compatibility between - // 32-bit and 64-bit processes. - return WritePOD(static_cast(value)); - } bool WriteFloat(float value) { return WritePOD(value); } @@ -206,6 +254,19 @@ class BASE_EXPORT Pickle { // known size. See also WriteData. bool WriteBytes(const void* data, int length); + // WriteAttachment appends |attachment| to the pickle. It returns + // false iff the set is full or if the Pickle implementation does not support + // attachments. + virtual bool WriteAttachment(scoped_refptr attachment); + + // ReadAttachment parses an attachment given the parsing state |iter| and + // writes it to |*attachment|. It returns true on success. + virtual bool ReadAttachment(base::PickleIterator* iter, + scoped_refptr* attachment) const; + + // Indicates whether the pickle has any attachments. + virtual bool HasAttachments() const; + // Reserves space for upcoming writes when multiple writes will be made and // their sizes are computed in advance. It can be significantly faster to call // Reserve() before calling WriteFoo() multiple times. diff --git a/src/base/posix/safe_strerror.cc b/src/base/posix/safe_strerror.cc index e80e8f8b..798658e9 100644 --- a/src/base/posix/safe_strerror.cc +++ b/src/base/posix/safe_strerror.cc @@ -20,7 +20,11 @@ namespace base { -#define USE_HISTORICAL_STRERRO_R (defined(__GLIBC__) || defined(OS_NACL)) +#if defined(__GLIBC__) || defined(OS_NACL) +#define USE_HISTORICAL_STRERRO_R 1 +#else +#define USE_HISTORICAL_STRERRO_R 0 +#endif #if USE_HISTORICAL_STRERRO_R && defined(__GNUC__) // GCC will complain about the unused second wrap function unless we tell it diff --git a/src/base/process/process_metrics.h b/src/base/process/process_metrics.h index 8d8f7fc4..0d4d04a8 100644 --- a/src/base/process/process_metrics.h +++ b/src/base/process/process_metrics.h @@ -261,6 +261,7 @@ BASE_EXPORT void SetFdLimit(unsigned int max_descriptors); // Linux/Android/Chrome OS. Shmem/slab/gem_objects/gem_size are Chrome OS only. struct BASE_EXPORT SystemMemoryInfoKB { SystemMemoryInfoKB(); + SystemMemoryInfoKB(const SystemMemoryInfoKB& other); // Serializes the platform specific fields to value. scoped_ptr ToValue() const; @@ -336,6 +337,7 @@ BASE_EXPORT bool ParseProcVmstat(const std::string& input, // Data from /proc/diskstats about system-wide disk I/O. struct BASE_EXPORT SystemDiskInfo { SystemDiskInfo(); + SystemDiskInfo(const SystemDiskInfo& other); // Serializes the platform specific fields to value. scoped_ptr ToValue() const; diff --git a/src/base/rand_util_posix.cc b/src/base/rand_util_posix.cc index 70983997..9b4513ac 100644 --- a/src/base/rand_util_posix.cc +++ b/src/base/rand_util_posix.cc @@ -10,6 +10,11 @@ #include #include +#if 0 +#include "base/files/file_util.h" +#else +#include "base/posix/eintr_wrapper.h" +#endif #include "base/lazy_instance.h" #include "base/logging.h" @@ -50,7 +55,7 @@ bool ReadFromFD(int fd, char* buffer, size_t bytes) { size_t total_read = 0; while (total_read < bytes) { ssize_t bytes_read = - read(fd, buffer + total_read, bytes - total_read); + HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); if (bytes_read <= 0) break; total_read += bytes_read; diff --git a/src/base/strings/string16.h b/src/base/strings/string16.h index e47669c1..82dd0fab 100644 --- a/src/base/strings/string16.h +++ b/src/base/strings/string16.h @@ -29,6 +29,8 @@ #include #include #include + +#include #include #include "base/base_export.h" @@ -182,6 +184,21 @@ BASE_EXPORT extern void PrintTo(const string16& str, std::ostream* out); extern template class BASE_EXPORT std::basic_string; +// Specialize std::hash for base::string16. Although the style guide forbids +// this in general, it is necessary for consistency with WCHAR_T_IS_UTF16 +// platforms, where base::string16 is a type alias for std::wstring. +namespace std { +template <> +struct hash { + std::size_t operator()(const base::string16& s) const { + std::size_t result = 0; + for (base::char16 c : s) + result = (result * 131) + c; + return result; + } +}; +} // namespace std + #endif // WCHAR_T_IS_UTF32 #endif // BASE_STRINGS_STRING16_H_ diff --git a/src/base/strings/string_number_conversions.cc b/src/base/strings/string_number_conversions.cc index 07248501..f4cf6ec5 100644 --- a/src/base/strings/string_number_conversions.cc +++ b/src/base/strings/string_number_conversions.cc @@ -146,6 +146,7 @@ class IteratorRangeToNumber { if (begin != end && *begin == '-') { if (!std::numeric_limits::is_signed) { + *output = 0; valid = false; } else if (!Negative::Invoke(begin + 1, end, output)) { valid = false; diff --git a/src/base/strings/string_piece.h b/src/base/strings/string_piece.h index 31e7596d..eaec14de 100644 --- a/src/base/strings/string_piece.h +++ b/src/base/strings/string_piece.h @@ -28,7 +28,6 @@ #include #include "base/base_export.h" -#include "base/containers/hash_tables.h" #include "base/logging.h" #include "base/strings/string16.h" @@ -224,6 +223,8 @@ template class BasicStringPiece { } value_type operator[](size_type i) const { return ptr_[i]; } + value_type front() const { return ptr_[0]; } + value_type back() const { return ptr_[length_ - 1]; } void remove_prefix(size_type n) { ptr_ += n; @@ -432,38 +433,32 @@ inline bool operator>=(const StringPiece16& x, const StringPiece16& y) { BASE_EXPORT std::ostream& operator<<(std::ostream& o, const StringPiece& piece); -} // namespace base - // Hashing --------------------------------------------------------------------- // We provide appropriate hash functions so StringPiece and StringPiece16 can // be used as keys in hash sets and maps. -// This hash function is copied from base/containers/hash_tables.h. We don't -// use the ones already defined for string and string16 directly because it -// would require the string constructors to be called, which we don't want. -#define HASH_STRING_PIECE(StringPieceType, string_piece) \ - std::size_t result = 0; \ - for (StringPieceType::const_iterator i = string_piece.begin(); \ - i != string_piece.end(); ++i) \ - result = (result * 131) + *i; \ - return result; \ - -namespace BASE_HASH_NAMESPACE { - -template<> -struct hash { - std::size_t operator()(const base::StringPiece& sp) const { - HASH_STRING_PIECE(base::StringPiece, sp); +// This hash function is copied from base/strings/string16.h. We don't use the +// ones already defined for string and string16 directly because it would +// require the string constructors to be called, which we don't want. +#define HASH_STRING_PIECE(StringPieceType, string_piece) \ + std::size_t result = 0; \ + for (StringPieceType::const_iterator i = string_piece.begin(); \ + i != string_piece.end(); ++i) \ + result = (result * 131) + *i; \ + return result; + +struct StringPieceHash { + std::size_t operator()(const StringPiece& sp) const { + HASH_STRING_PIECE(StringPiece, sp); } }; -template<> -struct hash { - std::size_t operator()(const base::StringPiece16& sp16) const { - HASH_STRING_PIECE(base::StringPiece16, sp16); +struct StringPiece16Hash { + std::size_t operator()(const StringPiece16& sp16) const { + HASH_STRING_PIECE(StringPiece16, sp16); } }; -} // namespace BASE_HASH_NAMESPACE +} // namespace base #endif // BASE_STRINGS_STRING_PIECE_H_ diff --git a/src/base/strings/utf_offset_string_conversions.cc b/src/base/strings/utf_offset_string_conversions.cc new file mode 100644 index 00000000..322c7a28 --- /dev/null +++ b/src/base/strings/utf_offset_string_conversions.cc @@ -0,0 +1,262 @@ +// 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/utf_offset_string_conversions.h" + +#include + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "base/strings/utf_string_conversion_utils.h" + +namespace base { + +OffsetAdjuster::Adjustment::Adjustment(size_t original_offset, + size_t original_length, + size_t output_length) + : original_offset(original_offset), + original_length(original_length), + output_length(output_length) { +} + +// static +void OffsetAdjuster::AdjustOffsets( + const Adjustments& adjustments, + std::vector* offsets_for_adjustment) { + if (!offsets_for_adjustment || adjustments.empty()) + return; + for (std::vector::iterator i(offsets_for_adjustment->begin()); + i != offsets_for_adjustment->end(); ++i) + AdjustOffset(adjustments, &(*i)); +} + +// static +void OffsetAdjuster::AdjustOffset(const Adjustments& adjustments, + size_t* offset) { + if (*offset == string16::npos) + return; + int adjustment = 0; + for (Adjustments::const_iterator i = adjustments.begin(); + i != adjustments.end(); ++i) { + if (*offset <= i->original_offset) + break; + if (*offset < (i->original_offset + i->original_length)) { + *offset = string16::npos; + return; + } + adjustment += static_cast(i->original_length - i->output_length); + } + *offset -= adjustment; +} + +// static +void OffsetAdjuster::UnadjustOffsets( + const Adjustments& adjustments, + std::vector* offsets_for_unadjustment) { + if (!offsets_for_unadjustment || adjustments.empty()) + return; + for (std::vector::iterator i(offsets_for_unadjustment->begin()); + i != offsets_for_unadjustment->end(); ++i) + UnadjustOffset(adjustments, &(*i)); +} + +// static +void OffsetAdjuster::UnadjustOffset(const Adjustments& adjustments, + size_t* offset) { + if (*offset == string16::npos) + return; + int adjustment = 0; + for (Adjustments::const_iterator i = adjustments.begin(); + i != adjustments.end(); ++i) { + if (*offset + adjustment <= i->original_offset) + break; + adjustment += static_cast(i->original_length - i->output_length); + if ((*offset + adjustment) < + (i->original_offset + i->original_length)) { + *offset = string16::npos; + return; + } + } + *offset += adjustment; +} + +// static +void OffsetAdjuster::MergeSequentialAdjustments( + const Adjustments& first_adjustments, + Adjustments* adjustments_on_adjusted_string) { + Adjustments::iterator adjusted_iter = adjustments_on_adjusted_string->begin(); + Adjustments::const_iterator first_iter = first_adjustments.begin(); + // Simultaneously iterate over all |adjustments_on_adjusted_string| and + // |first_adjustments|, adding adjustments to or correcting the adjustments + // in |adjustments_on_adjusted_string| as we go. |shift| keeps track of the + // current number of characters collapsed by |first_adjustments| up to this + // point. |currently_collapsing| keeps track of the number of characters + // collapsed by |first_adjustments| into the current |adjusted_iter|'s + // length. These are characters that will change |shift| as soon as we're + // done processing the current |adjusted_iter|; they are not yet reflected in + // |shift|. + size_t shift = 0; + size_t currently_collapsing = 0; + while (adjusted_iter != adjustments_on_adjusted_string->end()) { + if ((first_iter == first_adjustments.end()) || + ((adjusted_iter->original_offset + shift + + adjusted_iter->original_length) <= first_iter->original_offset)) { + // Entire |adjusted_iter| (accounting for its shift and including its + // whole original length) comes before |first_iter|. + // + // Correct the offset at |adjusted_iter| and move onto the next + // adjustment that needs revising. + adjusted_iter->original_offset += shift; + shift += currently_collapsing; + currently_collapsing = 0; + ++adjusted_iter; + } else if ((adjusted_iter->original_offset + shift) > + first_iter->original_offset) { + // |first_iter| comes before the |adjusted_iter| (as adjusted by |shift|). + + // It's not possible for the adjustments to overlap. (It shouldn't + // be possible that we have an |adjusted_iter->original_offset| that, + // when adjusted by the computed |shift|, is in the middle of + // |first_iter|'s output's length. After all, that would mean the + // current adjustment_on_adjusted_string somehow points to an offset + // that was supposed to have been eliminated by the first set of + // adjustments.) + DCHECK_LE(first_iter->original_offset + first_iter->output_length, + adjusted_iter->original_offset + shift); + + // Add the |first_adjustment_iter| to the full set of adjustments while + // making sure |adjusted_iter| continues pointing to the same element. + // We do this by inserting the |first_adjustment_iter| right before + // |adjusted_iter|, then incrementing |adjusted_iter| so it points to + // the following element. + shift += first_iter->original_length - first_iter->output_length; + adjusted_iter = adjustments_on_adjusted_string->insert( + adjusted_iter, *first_iter); + ++adjusted_iter; + ++first_iter; + } else { + // The first adjustment adjusted something that then got further adjusted + // by the second set of adjustments. In other words, |first_iter| points + // to something in the range covered by |adjusted_iter|'s length (after + // accounting for |shift|). Precisely, + // adjusted_iter->original_offset + shift + // <= + // first_iter->original_offset + // <= + // adjusted_iter->original_offset + shift + + // adjusted_iter->original_length + + // Modify the current |adjusted_iter| to include whatever collapsing + // happened in |first_iter|, then advance to the next |first_adjustments| + // because we dealt with the current one. + const int collapse = static_cast(first_iter->original_length) - + static_cast(first_iter->output_length); + // This function does not know how to deal with a string that expands and + // then gets modified, only strings that collapse and then get modified. + DCHECK_GT(collapse, 0); + adjusted_iter->original_length += collapse; + currently_collapsing += collapse; + ++first_iter; + } + } + DCHECK_EQ(0u, currently_collapsing); + if (first_iter != first_adjustments.end()) { + // Only first adjustments are left. These do not need to be modified. + // (Their offsets are already correct with respect to the original string.) + // Append them all. + DCHECK(adjusted_iter == adjustments_on_adjusted_string->end()); + adjustments_on_adjusted_string->insert( + adjustments_on_adjusted_string->end(), first_iter, + first_adjustments.end()); + } +} + +// Converts the given source Unicode character type to the given destination +// Unicode character type as a STL string. The given input buffer and size +// determine the source, and the given output STL string will be replaced by +// the result. If non-NULL, |adjustments| is set to reflect the all the +// alterations to the string that are not one-character-to-one-character. +// It will always be sorted by increasing offset. +template +bool ConvertUnicode(const SrcChar* src, + size_t src_len, + DestStdString* output, + OffsetAdjuster::Adjustments* adjustments) { + if (adjustments) + adjustments->clear(); + // ICU requires 32-bit numbers. + bool success = true; + int32_t src_len32 = static_cast(src_len); + for (int32_t i = 0; i < src_len32; i++) { + uint32_t code_point; + size_t original_i = i; + size_t chars_written = 0; + if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) { + chars_written = WriteUnicodeCharacter(code_point, output); + } else { + chars_written = WriteUnicodeCharacter(0xFFFD, output); + success = false; + } + + // Only bother writing an adjustment if this modification changed the + // length of this character. + // NOTE: ReadUnicodeCharacter() adjusts |i| to point _at_ the last + // character read, not after it (so that incrementing it in the loop + // increment will place it at the right location), so we need to account + // for that in determining the amount that was read. + if (adjustments && ((i - original_i + 1) != chars_written)) { + adjustments->push_back(OffsetAdjuster::Adjustment( + original_i, i - original_i + 1, chars_written)); + } + } + return success; +} + +bool UTF8ToUTF16WithAdjustments( + const char* src, + size_t src_len, + string16* output, + base::OffsetAdjuster::Adjustments* adjustments) { + PrepareForUTF16Or32Output(src, src_len, output); + return ConvertUnicode(src, src_len, output, adjustments); +} + +string16 UTF8ToUTF16WithAdjustments( + const base::StringPiece& utf8, + base::OffsetAdjuster::Adjustments* adjustments) { + string16 result; + UTF8ToUTF16WithAdjustments(utf8.data(), utf8.length(), &result, adjustments); + return result; +} + +string16 UTF8ToUTF16AndAdjustOffsets( + const base::StringPiece& utf8, + std::vector* offsets_for_adjustment) { + std::for_each(offsets_for_adjustment->begin(), + offsets_for_adjustment->end(), + LimitOffset(utf8.length())); + OffsetAdjuster::Adjustments adjustments; + string16 result = UTF8ToUTF16WithAdjustments(utf8, &adjustments); + OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment); + return result; +} + +std::string UTF16ToUTF8AndAdjustOffsets( + const base::StringPiece16& utf16, + std::vector* offsets_for_adjustment) { + std::for_each(offsets_for_adjustment->begin(), + offsets_for_adjustment->end(), + LimitOffset(utf16.length())); + std::string result; + PrepareForUTF8Output(utf16.data(), utf16.length(), &result); + OffsetAdjuster::Adjustments adjustments; + ConvertUnicode(utf16.data(), utf16.length(), &result, &adjustments); + OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment); + return result; +} + +} // namespace base diff --git a/src/base/strings/utf_offset_string_conversions.h b/src/base/strings/utf_offset_string_conversions.h new file mode 100644 index 00000000..1844601f --- /dev/null +++ b/src/base/strings/utf_offset_string_conversions.h @@ -0,0 +1,128 @@ +// 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 BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_ +#define BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_ + +#include + +#include +#include + +#include "base/base_export.h" +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" + +namespace base { + +// A helper class and associated data structures to adjust offsets into a +// string in response to various adjustments one might do to that string +// (e.g., eliminating a range). For details on offsets, see the comments by +// the AdjustOffsets() function below. +class BASE_EXPORT OffsetAdjuster { + public: + struct BASE_EXPORT Adjustment { + Adjustment(size_t original_offset, + size_t original_length, + size_t output_length); + + size_t original_offset; + size_t original_length; + size_t output_length; + }; + typedef std::vector Adjustments; + + // Adjusts all offsets in |offsets_for_adjustment| to reflect the adjustments + // recorded in |adjustments|. + // + // Offsets represents insertion/selection points between characters: if |src| + // is "abcd", then 0 is before 'a', 2 is between 'b' and 'c', and 4 is at the + // end of the string. Valid input offsets range from 0 to |src_len|. On + // exit, each offset will have been modified to point at the same logical + // position in the output string. If an offset cannot be successfully + // adjusted (e.g., because it points into the middle of a multibyte sequence), + // it will be set to string16::npos. + static void AdjustOffsets(const Adjustments& adjustments, + std::vector* offsets_for_adjustment); + + // Adjusts the single |offset| to reflect the adjustments recorded in + // |adjustments|. + static void AdjustOffset(const Adjustments& adjustments, + size_t* offset); + + // Adjusts all offsets in |offsets_for_unadjustment| to reflect the reverse + // of the adjustments recorded in |adjustments|. In other words, the offsets + // provided represent offsets into an adjusted string and the caller wants + // to know the offsets they correspond to in the original string. If an + // offset cannot be successfully unadjusted (e.g., because it points into + // the middle of a multibyte sequence), it will be set to string16::npos. + static void UnadjustOffsets(const Adjustments& adjustments, + std::vector* offsets_for_unadjustment); + + // Adjusts the single |offset| to reflect the reverse of the adjustments + // recorded in |adjustments|. + static void UnadjustOffset(const Adjustments& adjustments, + size_t* offset); + + // Combines two sequential sets of adjustments, storing the combined revised + // adjustments in |adjustments_on_adjusted_string|. That is, suppose a + // string was altered in some way, with the alterations recorded as + // adjustments in |first_adjustments|. Then suppose the resulting string is + // further altered, with the alterations recorded as adjustments scored in + // |adjustments_on_adjusted_string|, with the offsets recorded in these + // adjustments being with respect to the intermediate string. This function + // combines the two sets of adjustments into one, storing the result in + // |adjustments_on_adjusted_string|, whose offsets are correct with respect + // to the original string. + // + // Assumes both parameters are sorted by increasing offset. + // + // WARNING: Only supports |first_adjustments| that involve collapsing ranges + // of text, not expanding ranges. + static void MergeSequentialAdjustments( + const Adjustments& first_adjustments, + Adjustments* adjustments_on_adjusted_string); +}; + +// Like the conversions in utf_string_conversions.h, but also fills in an +// |adjustments| parameter that reflects the alterations done to the string. +// It may be NULL. +BASE_EXPORT bool UTF8ToUTF16WithAdjustments( + const char* src, + size_t src_len, + string16* output, + base::OffsetAdjuster::Adjustments* adjustments); +BASE_EXPORT string16 UTF8ToUTF16WithAdjustments( + const base::StringPiece& utf8, + base::OffsetAdjuster::Adjustments* adjustments); +// As above, but instead internally examines the adjustments and applies them +// to |offsets_for_adjustment|. See comments by AdjustOffsets(). +BASE_EXPORT string16 UTF8ToUTF16AndAdjustOffsets( + const base::StringPiece& utf8, + std::vector* offsets_for_adjustment); + +BASE_EXPORT std::string UTF16ToUTF8AndAdjustOffsets( + const base::StringPiece16& utf16, + std::vector* offsets_for_adjustment); + +// Limiting function callable by std::for_each which will replace any value +// which is greater than |limit| with npos. Typically this is called with a +// string length to clamp offsets into the string to [0, length] (as opposed to +// [0, length); see comments above). +template +struct LimitOffset { + explicit LimitOffset(size_t limit) + : limit_(limit) {} + + void operator()(size_t& offset) { + if (offset > limit_) + offset = T::npos; + } + + size_t limit_; +}; + +} // namespace base + +#endif // BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_ diff --git a/src/base/sys_info.cc b/src/base/sys_info.cc new file mode 100644 index 00000000..cebb3630 --- /dev/null +++ b/src/base/sys_info.cc @@ -0,0 +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 "base/sys_info.h" + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/lazy_instance.h" +#include "base/metrics/field_trial.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/sys_info_internal.h" +#include "base/time/time.h" +#include "build/build_config.h" + +namespace base { + +#if !defined(OS_ANDROID) + +static const int kLowMemoryDeviceThresholdMB = 512; + +bool DetectLowEndDevice() { + CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kEnableLowEndDeviceMode)) + return true; + if (command_line->HasSwitch(switches::kDisableLowEndDeviceMode)) + return false; + + int ram_size_mb = SysInfo::AmountOfPhysicalMemoryMB(); + return (ram_size_mb > 0 && ram_size_mb < kLowMemoryDeviceThresholdMB); +} + +static LazyInstance< + internal::LazySysInfoValue >::Leaky + g_lazy_low_end_device = LAZY_INSTANCE_INITIALIZER; + +// static +bool SysInfo::IsLowEndDevice() { + const std::string group_name = + base::FieldTrialList::FindFullName("MemoryReduction"); + + // Low End Device Mode will be enabled if this client is assigned to + // one of those EnabledXXX groups. + if (StartsWith(group_name, "Enabled", CompareCase::SENSITIVE)) + return true; + + return g_lazy_low_end_device.Get().value(); +} +#endif + +#if (!defined(OS_MACOSX) || defined(OS_IOS)) && !defined(OS_ANDROID) +std::string SysInfo::HardwareModelName() { + return std::string(); +} +#endif + +// static +base::TimeDelta SysInfo::Uptime() { + // This code relies on an implementation detail of TimeTicks::Now() - that + // its return value happens to coincide with the system uptime value in + // microseconds, on Win/Mac/iOS/Linux/ChromeOS and Android. + int64_t uptime_in_microseconds = TimeTicks::Now().ToInternalValue(); + return base::TimeDelta::FromMicroseconds(uptime_in_microseconds); +} + +} // namespace base diff --git a/src/base/sys_info.h b/src/base/sys_info.h new file mode 100644 index 00000000..5686dcbb --- /dev/null +++ b/src/base/sys_info.h @@ -0,0 +1,146 @@ +// 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 BASE_SYS_INFO_H_ +#define BASE_SYS_INFO_H_ + +#include +#include + +#include +#include + +#include "base/base_export.h" +#include "base/files/file_path.h" +#include "base/time/time.h" +#include "build/build_config.h" + +namespace base { + +class BASE_EXPORT SysInfo { + public: + // Return the number of logical processors/cores on the current machine. + static int NumberOfProcessors(); + + // Return the number of bytes of physical memory on the current machine. + static int64_t AmountOfPhysicalMemory(); + + // Return the number of bytes of current available physical memory on the + // machine. + static int64_t AmountOfAvailablePhysicalMemory(); + + // Return the number of bytes of virtual memory of this process. A return + // value of zero means that there is no limit on the available virtual + // memory. + static int64_t AmountOfVirtualMemory(); + + // Return the number of megabytes of physical memory on the current machine. + static int AmountOfPhysicalMemoryMB() { + return static_cast(AmountOfPhysicalMemory() / 1024 / 1024); + } + + // Return the number of megabytes of available virtual memory, or zero if it + // is unlimited. + static int AmountOfVirtualMemoryMB() { + return static_cast(AmountOfVirtualMemory() / 1024 / 1024); + } + + // Return the available disk space in bytes on the volume containing |path|, + // or -1 on failure. + static int64_t AmountOfFreeDiskSpace(const FilePath& path); + + // Returns system uptime. + static TimeDelta Uptime(); + + // Returns a descriptive string for the current machine model or an empty + // string if the machine model is unknown or an error occured. + // e.g. "MacPro1,1" on Mac, or "Nexus 5" on Android. Only implemented on OS X, + // Android, and Chrome OS. This returns an empty string on other platforms. + static std::string HardwareModelName(); + + // Returns the name of the host operating system. + static std::string OperatingSystemName(); + + // Returns the version of the host operating system. + static std::string OperatingSystemVersion(); + + // Retrieves detailed numeric values for the OS version. + // TODO(port): Implement a Linux version of this method and enable the + // corresponding unit test. + // DON'T USE THIS ON THE MAC OR WINDOWS to determine the current OS release + // for OS version-specific feature checks and workarounds. If you must use + // an OS version check instead of a feature check, use the base::mac::IsOS* + // family from base/mac/mac_util.h, or base::win::GetVersion from + // base/win/windows_version.h. + static void OperatingSystemVersionNumbers(int32_t* major_version, + int32_t* minor_version, + int32_t* bugfix_version); + + // Returns the architecture of the running operating system. + // Exact return value may differ across platforms. + // e.g. a 32-bit x86 kernel on a 64-bit capable CPU will return "x86", + // whereas a x86-64 kernel on the same CPU will return "x86_64" + static std::string OperatingSystemArchitecture(); + + // Avoid using this. Use base/cpu.h to get information about the CPU instead. + // http://crbug.com/148884 + // Returns the CPU model name of the system. If it can not be figured out, + // an empty string is returned. + static std::string CPUModelName(); + + // Return the smallest amount of memory (in bytes) which the VM system will + // allocate. + static size_t VMAllocationGranularity(); + +#if defined(OS_POSIX) && !defined(OS_MACOSX) + // Returns the maximum SysV shared memory segment size, or zero if there is no + // limit. + static uint64_t MaxSharedMemorySize(); +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) + +#if defined(OS_CHROMEOS) + typedef std::map LsbReleaseMap; + + // Returns the contents of /etc/lsb-release as a map. + static const LsbReleaseMap& GetLsbReleaseMap(); + + // If |key| is present in the LsbReleaseMap, sets |value| and returns true. + static bool GetLsbReleaseValue(const std::string& key, std::string* value); + + // Convenience function for GetLsbReleaseValue("CHROMEOS_RELEASE_BOARD",...). + // Returns "unknown" if CHROMEOS_RELEASE_BOARD is not set. + static std::string GetLsbReleaseBoard(); + + // Returns the creation time of /etc/lsb-release. (Used to get the date and + // time of the Chrome OS build). + static Time GetLsbReleaseTime(); + + // Returns true when actually running in a Chrome OS environment. + static bool IsRunningOnChromeOS(); + + // Test method to force re-parsing of lsb-release. + static void SetChromeOSVersionInfoForTest(const std::string& lsb_release, + const Time& lsb_release_time); +#endif // defined(OS_CHROMEOS) + +#if defined(OS_ANDROID) + // Returns the Android build's codename. + static std::string GetAndroidBuildCodename(); + + // Returns the Android build ID. + static std::string GetAndroidBuildID(); + + static int DalvikHeapSizeMB(); + static int DalvikHeapGrowthLimitMB(); +#endif // defined(OS_ANDROID) + + // Returns true if this is a low-end device. + // Low-end device refers to devices having less than 512M memory in the + // current implementation. + static bool IsLowEndDevice(); +}; + +} // namespace base + +#endif // BASE_SYS_INFO_H_ diff --git a/src/base/sys_info_internal.h b/src/base/sys_info_internal.h new file mode 100644 index 00000000..a1792191 --- /dev/null +++ b/src/base/sys_info_internal.h @@ -0,0 +1,34 @@ +// 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 BASE_SYS_INFO_INTERNAL_H_ +#define BASE_SYS_INFO_INTERNAL_H_ + +#include "base/macros.h" + +namespace base { + +namespace internal { + +template +class LazySysInfoValue { + public: + LazySysInfoValue() + : value_(F()) { } + + ~LazySysInfoValue() { } + + T value() { return value_; } + + private: + const T value_; + + DISALLOW_COPY_AND_ASSIGN(LazySysInfoValue); +}; + +} // namespace internal + +} // namespace base + +#endif // BASE_SYS_INFO_INTERNAL_H_ diff --git a/src/base/third_party/nspr/prtime.cc b/src/base/third_party/nspr/prtime.cc index 177cc902..c125160d 100644 --- a/src/base/third_party/nspr/prtime.cc +++ b/src/base/third_party/nspr/prtime.cc @@ -53,8 +53,8 @@ * PR_NormalizeTime * PR_GMTParameters * PR_ImplodeTime - * This was modified to use the Win32 SYSTEMTIME/FILETIME structures - * and the timezone offsets are applied to the FILETIME structure. + * Upstream implementation from + * http://lxr.mozilla.org/nspr/source/pr/src/misc/prtime.c#221 * All types and macros are defined in the base/third_party/prtime.h file. * These have been copied from the following nspr files. We have only copied * over the types we need. @@ -71,135 +71,10 @@ #include "base/third_party/nspr/prtime.h" #include "build/build_config.h" -#if defined(OS_WIN) -#include -#elif defined(OS_MACOSX) -#include -#elif defined(OS_ANDROID) -#include -#include "base/os_compat_android.h" // For timegm() -#elif defined(OS_NACL) -#include "base/os_compat_nacl.h" // For timegm() -#endif #include /* for EINVAL */ #include -/* Implements the Unix localtime_r() function for windows */ -#if defined(OS_WIN) -static void localtime_r(const time_t* secs, struct tm* time) { - (void) localtime_s(time, secs); -} -#endif - /* - *------------------------------------------------------------------------ - * - * PR_ImplodeTime -- - * - * Cf. time_t mktime(struct tm *tp) - * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough. - * - *------------------------------------------------------------------------ - */ -PRTime -PR_ImplodeTime(const PRExplodedTime *exploded) -{ - // This is important, we want to make sure multiplications are - // done with the correct precision. - static const PRTime kSecondsToMicroseconds = static_cast(1000000); -#if defined(OS_WIN) - // Create the system struct representing our exploded time. - SYSTEMTIME st = {}; - FILETIME ft = {}; - ULARGE_INTEGER uli = {}; - - st.wYear = exploded->tm_year; - st.wMonth = static_cast(exploded->tm_month + 1); - st.wDayOfWeek = exploded->tm_wday; - st.wDay = static_cast(exploded->tm_mday); - st.wHour = static_cast(exploded->tm_hour); - st.wMinute = static_cast(exploded->tm_min); - st.wSecond = static_cast(exploded->tm_sec); - st.wMilliseconds = static_cast(exploded->tm_usec/1000); - // Convert to FILETIME. - if (!SystemTimeToFileTime(&st, &ft)) { - NOTREACHED() << "Unable to convert time"; - return 0; - } - // Apply offsets. - uli.LowPart = ft.dwLowDateTime; - uli.HighPart = ft.dwHighDateTime; - // Convert from Windows epoch to NSPR epoch, and 100-nanoseconds units - // to microsecond units. - PRTime result = - static_cast((uli.QuadPart / 10) - 11644473600000000i64); - // Adjust for time zone and dst. Convert from seconds to microseconds. - result -= (exploded->tm_params.tp_gmt_offset + - exploded->tm_params.tp_dst_offset) * kSecondsToMicroseconds; - // Add microseconds that cannot be represented in |st|. - result += exploded->tm_usec % 1000; - return result; -#elif defined(OS_MACOSX) - // Create the system struct representing our exploded time. - CFGregorianDate gregorian_date; - gregorian_date.year = exploded->tm_year; - gregorian_date.month = exploded->tm_month + 1; - gregorian_date.day = exploded->tm_mday; - gregorian_date.hour = exploded->tm_hour; - gregorian_date.minute = exploded->tm_min; - gregorian_date.second = exploded->tm_sec; - - // Compute |absolute_time| in seconds, correct for gmt and dst - // (note the combined offset will be negative when we need to add it), then - // convert to microseconds which is what PRTime expects. - CFAbsoluteTime absolute_time = - CFGregorianDateGetAbsoluteTime(gregorian_date, NULL); - PRTime result = static_cast(absolute_time); - result -= exploded->tm_params.tp_gmt_offset + - exploded->tm_params.tp_dst_offset; - result += kCFAbsoluteTimeIntervalSince1970; // PRTime epoch is 1970 - result *= kSecondsToMicroseconds; - result += exploded->tm_usec; - return result; -#elif defined(OS_POSIX) - struct tm exp_tm = {0}; - exp_tm.tm_sec = exploded->tm_sec; - exp_tm.tm_min = exploded->tm_min; - exp_tm.tm_hour = exploded->tm_hour; - exp_tm.tm_mday = exploded->tm_mday; - exp_tm.tm_mon = exploded->tm_month; - exp_tm.tm_year = exploded->tm_year - 1900; - - time_t absolute_time = timegm(&exp_tm); - - // If timegm returned -1. Since we don't pass it a time zone, the only - // valid case of returning -1 is 1 second before Epoch (Dec 31, 1969). - if (absolute_time == -1 && - !(exploded->tm_year == 1969 && exploded->tm_month == 11 && - exploded->tm_mday == 31 && exploded->tm_hour == 23 && - exploded->tm_min == 59 && exploded->tm_sec == 59)) { - // If we get here, time_t must be 32 bits. - // Date was possibly too far in the future and would overflow. Return - // the most future date possible (year 2038). - if (exploded->tm_year >= 1970) - return INT_MAX * kSecondsToMicroseconds; - // Date was possibly too far in the past and would underflow. Return - // the most past date possible (year 1901). - return INT_MIN * kSecondsToMicroseconds; - } - - PRTime result = static_cast(absolute_time); - result -= exploded->tm_params.tp_gmt_offset + - exploded->tm_params.tp_dst_offset; - result *= kSecondsToMicroseconds; - result += exploded->tm_usec; - return result; -#else -#error No PR_ImplodeTime implemented on your platform. -#endif -} - -/* * The COUNT_LEAPS macro counts the number of leap years passed by * till the start of the given year Y. At the start of the year 4 * A.D. the number of leap years passed by is 0, while at the start of @@ -214,9 +89,16 @@ PR_ImplodeTime(const PRExplodedTime *exploded) * midnight 00:00:00. */ -#define COUNT_LEAPS(Y) ( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 ) -#define COUNT_DAYS(Y) ( ((Y)-1)*365 + COUNT_LEAPS(Y) ) -#define DAYS_BETWEEN_YEARS(A, B) (COUNT_DAYS(B) - COUNT_DAYS(A)) +#define COUNT_LEAPS(Y) (((Y)-1) / 4 - ((Y)-1) / 100 + ((Y)-1) / 400) +#define COUNT_DAYS(Y) (((Y)-1) * 365 + COUNT_LEAPS(Y)) +#define DAYS_BETWEEN_YEARS(A, B) (COUNT_DAYS(B) - COUNT_DAYS(A)) + +/* Implements the Unix localtime_r() function for windows */ +#if defined(OS_WIN) +static void localtime_r(const time_t* secs, struct tm* time) { + (void) localtime_s(time, secs); +} +#endif /* * Static variables used by functions in this file @@ -241,6 +123,56 @@ static const PRInt8 nDays[2][12] = { {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; +/* + *------------------------------------------------------------------------ + * + * PR_ImplodeTime -- + * + * Cf. time_t mktime(struct tm *tp) + * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough. + * + *------------------------------------------------------------------------ + */ +PRTime +PR_ImplodeTime(const PRExplodedTime *exploded) +{ + PRExplodedTime copy; + PRTime retVal; + PRInt64 secPerDay, usecPerSec; + PRInt64 temp; + PRInt64 numSecs64; + PRInt32 numDays; + PRInt32 numSecs; + + /* Normalize first. Do this on our copy */ + copy = *exploded; + PR_NormalizeTime(©, PR_GMTParameters); + + numDays = DAYS_BETWEEN_YEARS(1970, copy.tm_year); + + numSecs = copy.tm_yday * 86400 + copy.tm_hour * 3600 + copy.tm_min * 60 + + copy.tm_sec; + + LL_I2L(temp, numDays); + LL_I2L(secPerDay, 86400); + LL_MUL(temp, temp, secPerDay); + LL_I2L(numSecs64, numSecs); + LL_ADD(numSecs64, numSecs64, temp); + + /* apply the GMT and DST offsets */ + LL_I2L(temp, copy.tm_params.tp_gmt_offset); + LL_SUB(numSecs64, numSecs64, temp); + LL_I2L(temp, copy.tm_params.tp_dst_offset); + LL_SUB(numSecs64, numSecs64, temp); + + LL_I2L(usecPerSec, 1000000L); + LL_MUL(temp, numSecs64, usecPerSec); + LL_I2L(retVal, copy.tm_usec); + LL_ADD(retVal, retVal, temp); + + return retVal; +} + /* *------------------------------------------------------------------------- * diff --git a/src/base/third_party/nspr/prtime.h b/src/base/third_party/nspr/prtime.h index 01a4e540..20bae388 100644 --- a/src/base/third_party/nspr/prtime.h +++ b/src/base/third_party/nspr/prtime.h @@ -73,6 +73,17 @@ typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus; #define PR_INT16_MAX 32767 #define NSPR_API(__type) extern __type +/* + * Long-long (64-bit signed integer type) support macros used by + * PR_ImplodeTime(). + * See http://lxr.mozilla.org/nspr/source/pr/include/prlong.h + */ + +#define LL_I2L(l, i) ((l) = (PRInt64)(i)) +#define LL_MUL(r, a, b) ((r) = (a) * (b)) +#define LL_ADD(r, a, b) ((r) = (a) + (b)) +#define LL_SUB(r, a, b) ((r) = (a) - (b)) + /**********************************************************************/ /************************* TYPES AND CONSTANTS ************************/ /**********************************************************************/ diff --git a/src/base/threading/platform_thread.h b/src/base/threading/platform_thread.h index e2b09bcb..e62eb2b2 100644 --- a/src/base/threading/platform_thread.h +++ b/src/base/threading/platform_thread.h @@ -182,7 +182,8 @@ class BASE_EXPORT PlatformThread { // Toggles the current thread's priority at runtime. A thread may not be able // to raise its priority back up after lowering it if the process does not - // have a proper permission, e.g. CAP_SYS_NICE on Linux. + // have a proper permission, e.g. CAP_SYS_NICE on Linux. A thread may not be + // able to lower its priority back down after raising it to REALTIME_AUDIO. // Since changing other threads' priority is not permitted in favor of // security, this interface is restricted to change only the current thread // priority (https://crbug.com/399473). diff --git a/src/base/threading/platform_thread_freebsd.cc b/src/base/threading/platform_thread_freebsd.cc index 0b35e5d2..bddb6acf 100644 --- a/src/base/threading/platform_thread_freebsd.cc +++ b/src/base/threading/platform_thread_freebsd.cc @@ -86,8 +86,6 @@ void PlatformThread::SetName(const std::string& name) { void InitThreading() {} -void InitOnThread() {} - void TerminateOnThread() {} size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { diff --git a/src/base/threading/platform_thread_linux.cc b/src/base/threading/platform_thread_linux.cc index 17eda640..054de872 100644 --- a/src/base/threading/platform_thread_linux.cc +++ b/src/base/threading/platform_thread_linux.cc @@ -31,30 +31,19 @@ namespace internal { namespace { #if !defined(OS_NACL) const struct sched_param kRealTimePrio = {8}; -const struct sched_param kResetPrio = {0}; #endif } // namespace const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = { {ThreadPriority::BACKGROUND, 10}, {ThreadPriority::NORMAL, 0}, - {ThreadPriority::DISPLAY, -6}, + {ThreadPriority::DISPLAY, -8}, {ThreadPriority::REALTIME_AUDIO, -10}, }; bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) { #if !defined(OS_NACL) - ThreadPriority current_priority; - if (priority != ThreadPriority::REALTIME_AUDIO && - GetCurrentThreadPriorityForPlatform(¤t_priority) && - current_priority == ThreadPriority::REALTIME_AUDIO) { - // If the pthread's round-robin scheduler is already enabled, and the new - // priority will use setpriority() instead, the pthread scheduler should be - // reset to use SCHED_OTHER so that setpriority() just works. - pthread_setschedparam(pthread_self(), SCHED_OTHER, &kResetPrio); - return false; - } - return priority == ThreadPriority::REALTIME_AUDIO && + return priority == ThreadPriority::REALTIME_AUDIO && pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0; #else return false; @@ -107,8 +96,6 @@ void PlatformThread::SetName(const std::string& name) { void InitThreading() {} -void InitOnThread() {} - void TerminateOnThread() {} size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { diff --git a/src/base/threading/platform_thread_mac.mm b/src/base/threading/platform_thread_mac.mm index 27288e78..53819bcf 100644 --- a/src/base/threading/platform_thread_mac.mm +++ b/src/base/threading/platform_thread_mac.mm @@ -220,9 +220,6 @@ size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { #endif } -void InitOnThread() { -} - void TerminateOnThread() { } diff --git a/src/base/threading/platform_thread_posix.cc b/src/base/threading/platform_thread_posix.cc index 39a00731..bd6ae2dc 100644 --- a/src/base/threading/platform_thread_posix.cc +++ b/src/base/threading/platform_thread_posix.cc @@ -29,7 +29,6 @@ namespace base { void InitThreading(); -void InitOnThread(); void TerminateOnThread(); size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes); @@ -45,8 +44,6 @@ struct ThreadParams { }; void* ThreadFunc(void* params) { - base::InitOnThread(); - PlatformThread::Delegate* delegate = nullptr; { @@ -56,8 +53,12 @@ void* ThreadFunc(void* params) { if (!thread_params->joinable) base::ThreadRestrictions::SetSingletonAllowed(false); - if (thread_params->priority != ThreadPriority::NORMAL) - PlatformThread::SetCurrentThreadPriority(thread_params->priority); +#if !defined(OS_NACL) + // Threads on linux/android may inherit their priority from the thread + // where they were created. This explicitly sets the priority of all new + // threads. + PlatformThread::SetCurrentThreadPriority(thread_params->priority); +#endif } ThreadIdNameManager::GetInstance()->RegisterThread( diff --git a/src/base/threading/thread_restrictions.h b/src/base/threading/thread_restrictions.h index f0d3faf6..1b79c03b 100644 --- a/src/base/threading/thread_restrictions.h +++ b/src/base/threading/thread_restrictions.h @@ -56,6 +56,15 @@ class InFlightIO; namespace gles2 { class CommandBufferClientImpl; } +namespace mojo { +namespace common { +class MessagePumpMojo; +} +} +namespace mus { +class CommandBufferLocal; +class GpuState; +} namespace net { class NetworkChangeNotifierMac; namespace internal { @@ -72,7 +81,7 @@ class WindowResizeHelperMac; } namespace views { -class WindowManagerConnection; +class ScreenMus; } namespace base { @@ -199,6 +208,9 @@ class BASE_EXPORT ThreadRestrictions { friend class PlatformThread; friend class android::JavaHandlerThread; friend class gles2::CommandBufferClientImpl; + friend class mojo::common::MessagePumpMojo; + friend class mus::CommandBufferLocal; + friend class mus::GpuState; // END ALLOWED USAGE. // BEGIN USAGE THAT NEEDS TO BE FIXED. @@ -221,7 +233,7 @@ class BASE_EXPORT ThreadRestrictions { #if !defined(OFFICIAL_BUILD) friend class content::SoftwareOutputDeviceMus; // Interim non-production code #endif - friend class views::WindowManagerConnection; + friend class views::ScreenMus; // END USAGE THAT NEEDS TO BE FIXED. #if ENABLE_THREAD_RESTRICTIONS diff --git a/src/base/time/time_mac.cc b/src/base/time/time_mac.cc index f2bc5ed8..f0c78044 100644 --- a/src/base/time/time_mac.cc +++ b/src/base/time/time_mac.cc @@ -167,19 +167,21 @@ Time Time::NowFromSystemTime() { // static Time Time::FromExploded(bool is_local, const Exploded& exploded) { - CFGregorianDate date; - date.second = exploded.second + - exploded.millisecond / static_cast(kMillisecondsPerSecond); - date.minute = exploded.minute; - date.hour = exploded.hour; - date.day = exploded.day_of_month; - date.month = exploded.month; - date.year = exploded.year; - base::ScopedCFTypeRef time_zone( - is_local ? CFTimeZoneCopySystem() : NULL); - CFAbsoluteTime seconds = CFGregorianDateGetAbsoluteTime(date, time_zone) + - kCFAbsoluteTimeIntervalSince1970; + is_local + ? CFTimeZoneCopySystem() + : CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, 0)); + base::ScopedCFTypeRef gregorian(CFCalendarCreateWithIdentifier( + kCFAllocatorDefault, kCFGregorianCalendar)); + CFCalendarSetTimeZone(gregorian, time_zone); + CFAbsoluteTime absolute_time; + // 'S' is not defined in componentDesc in Apple documentation, but can be + // found at http://www.opensource.apple.com/source/CF/CF-855.17/CFCalendar.c + CFCalendarComposeAbsoluteTime( + gregorian, &absolute_time, "yMdHmsS", exploded.year, exploded.month, + exploded.day_of_month, exploded.hour, exploded.minute, exploded.second, + exploded.millisecond); + CFAbsoluteTime seconds = absolute_time + kCFAbsoluteTimeIntervalSince1970; return Time(static_cast(seconds * kMicrosecondsPerSecond) + kWindowsEpochDeltaMicroseconds); } @@ -195,19 +197,25 @@ void Time::Explode(bool is_local, Exploded* exploded) const { kCFAbsoluteTimeIntervalSince1970; base::ScopedCFTypeRef time_zone( - is_local ? CFTimeZoneCopySystem() : NULL); - CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(seconds, time_zone); - // 1 = Monday, ..., 7 = Sunday. - int cf_day_of_week = CFAbsoluteTimeGetDayOfWeek(seconds, time_zone); - - exploded->year = date.year; - exploded->month = date.month; - exploded->day_of_week = cf_day_of_week % 7; - exploded->day_of_month = date.day; - exploded->hour = date.hour; - exploded->minute = date.minute; + is_local + ? CFTimeZoneCopySystem() + : CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, 0)); + base::ScopedCFTypeRef gregorian(CFCalendarCreateWithIdentifier( + kCFAllocatorDefault, kCFGregorianCalendar)); + CFCalendarSetTimeZone(gregorian, time_zone); + int second, day_of_week; + // 'E' sets the day of week, but is not defined in componentDesc in Apple + // documentation. It can be found in open source code here: + // http://www.opensource.apple.com/source/CF/CF-855.17/CFCalendar.c + CFCalendarDecomposeAbsoluteTime(gregorian, seconds, "yMdHmsE", + &exploded->year, &exploded->month, + &exploded->day_of_month, &exploded->hour, + &exploded->minute, &second, &day_of_week); // Make sure seconds are rounded down towards -infinity. - exploded->second = floor(date.second); + exploded->second = floor(second); + // |Exploded|'s convention for day of week is 0 = Sunday, i.e. different + // from CF's 1 = Sunday. + exploded->day_of_week = (day_of_week - 1) % 7; // Calculate milliseconds ourselves, since we rounded the |seconds|, making // sure to round towards -infinity. exploded->millisecond = diff --git a/src/base/tuple.h b/src/base/tuple.h index e5872cc4..8898fe0d 100644 --- a/src/base/tuple.h +++ b/src/base/tuple.h @@ -29,6 +29,7 @@ #define BASE_TUPLE_H_ #include +#include #include "base/bind_helpers.h" #include "build/build_config.h" @@ -145,60 +146,10 @@ struct TupleTraits { // want filled by the dispatchee, and the tuple is merely a container for that // output (a "tier"). See MakeRefTuple and its usages. -template -struct TupleBaseImpl; template -using TupleBase = TupleBaseImpl, Ts...>; -template -struct TupleLeaf; +using Tuple = std::tuple; -template -struct Tuple final : TupleBase { - Tuple() : TupleBase() {} - explicit Tuple(typename TupleTraits::ParamType... args) - : TupleBase(args...) {} -}; - -// Avoids ambiguity between Tuple's two constructors. -template <> -struct Tuple<> final {}; - -template -struct TupleBaseImpl, Ts...> : TupleLeaf... { - TupleBaseImpl() : TupleLeaf()... {} - explicit TupleBaseImpl(typename TupleTraits::ParamType... args) - : TupleLeaf(args)... {} -}; - -template -struct TupleLeaf { - TupleLeaf() {} - explicit TupleLeaf(typename TupleTraits::ParamType x) : x(x) {} - - T& get() { return x; } - const T& get() const { return x; } - - T x; -}; - -// Tuple getters -------------------------------------------------------------- -// -// Allows accessing an arbitrary tuple element by index. -// -// Example usage: -// base::Tuple t2; -// base::get<0>(t2) = 42; -// base::get<1>(t2) = 3.14; - -template -T& get(TupleLeaf& leaf) { - return leaf.get(); -} - -template -const T& get(const TupleLeaf& leaf) { - return leaf.get(); -} +using std::get; // Tuple types ---------------------------------------------------------------- // @@ -245,15 +196,15 @@ inline Tuple MakeRefTuple(Ts&... arg) { // Non-Static Dispatchers with no out params. template -inline void DispatchToMethodImpl(ObjT* obj, +inline void DispatchToMethodImpl(const ObjT& obj, Method method, const Tuple& arg, IndexSequence) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(get(arg))...); + (obj->*method)(internal::Unwrap(get(arg))...); } template -inline void DispatchToMethod(ObjT* obj, +inline void DispatchToMethod(const ObjT& obj, Method method, const Tuple& arg) { DispatchToMethodImpl(obj, method, arg, MakeIndexSequence()); @@ -265,7 +216,7 @@ template inline void DispatchToFunctionImpl(Function function, const Tuple& arg, IndexSequence) { - (*function)(base::internal::UnwrapTraits::Unwrap(get(arg))...); + (*function)(internal::Unwrap(get(arg))...); } template @@ -281,18 +232,17 @@ template -inline void DispatchToMethodImpl(ObjT* obj, +inline void DispatchToMethodImpl(const ObjT& obj, Method method, const Tuple& in, Tuple* out, IndexSequence, IndexSequence) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(get(in))..., - &get(*out)...); + (obj->*method)(internal::Unwrap(get(in))..., &get(*out)...); } template -inline void DispatchToMethod(ObjT* obj, +inline void DispatchToMethod(const ObjT& obj, Method method, const Tuple& in, Tuple* out) { diff --git a/src/base/values.cc b/src/base/values.cc index ab3c38a0..5a789e9a 100644 --- a/src/base/values.cc +++ b/src/base/values.cc @@ -837,6 +837,8 @@ DictionaryValue::Iterator::Iterator(const DictionaryValue& target) : target_(target), it_(target.dictionary_.begin()) {} +DictionaryValue::Iterator::Iterator(const Iterator& other) = default; + DictionaryValue::Iterator::~Iterator() {} DictionaryValue* DictionaryValue::DeepCopy() const { diff --git a/src/base/values.h b/src/base/values.h index 07e5b6c8..141ea934 100644 --- a/src/base/values.h +++ b/src/base/values.h @@ -360,6 +360,7 @@ class BASE_EXPORT DictionaryValue : public Value { class BASE_EXPORT Iterator { public: explicit Iterator(const DictionaryValue& target); + Iterator(const Iterator& other); ~Iterator(); bool IsAtEnd() const { return it_ == target_.dictionary_.end(); } diff --git a/src/crypto/ec_private_key.h b/src/crypto/ec_private_key.h index 9a8a02ac..1ee4aca8 100644 --- a/src/crypto/ec_private_key.h +++ b/src/crypto/ec_private_key.h @@ -95,8 +95,7 @@ class CRYPTO_EXPORT ECPrivateKey { // Exports private key data for testing. The format of data stored into output // doesn't matter other than that it is consistent for the same key. - bool ExportValue(std::vector* output); - bool ExportECParams(std::vector* output); + bool ExportValueForTesting(std::vector* output); private: // Constructor is private. Use one of the Create*() methods above instead. diff --git a/src/crypto/ec_private_key_nss.cc b/src/crypto/ec_private_key_nss.cc index b65de955..989b7ade 100644 --- a/src/crypto/ec_private_key_nss.cc +++ b/src/crypto/ec_private_key_nss.cc @@ -26,10 +26,9 @@ extern "C" { namespace { -// Copied from rsa_private_key_nss.cc. -static bool ReadAttribute(SECKEYPrivateKey* key, - CK_ATTRIBUTE_TYPE type, - std::vector* output) { +static bool AppendAttribute(SECKEYPrivateKey* key, + CK_ATTRIBUTE_TYPE type, + std::vector* output) { SECItem item; SECStatus rv; rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item); @@ -38,7 +37,7 @@ static bool ReadAttribute(SECKEYPrivateKey* key, return false; } - output->assign(item.data, item.data + item.len); + output->insert(output->end(), item.data, item.data + item.len); SECITEM_FreeItem(&item, PR_FALSE); return true; } @@ -311,12 +310,14 @@ bool ECPrivateKey::ExportRawPublicKey(std::string* output) { return true; } -bool ECPrivateKey::ExportValue(std::vector* output) { - return ReadAttribute(key_, CKA_VALUE, output); -} - -bool ECPrivateKey::ExportECParams(std::vector* output) { - return ReadAttribute(key_, CKA_EC_PARAMS, output); +bool ECPrivateKey::ExportValueForTesting(std::vector* output) { + // This serialization format is purely for testing equality, so just + // concatenate the raw private key (always 32 bytes for P-256) with the + // parameters. + output->clear(); + return AppendAttribute(key_, CKA_VALUE, output) && + output->size() == 32 && + AppendAttribute(key_, CKA_EC_PARAMS, output); } ECPrivateKey::ECPrivateKey() : key_(NULL), public_key_(NULL) {} diff --git a/src/crypto/ghash.cc b/src/crypto/ghash.cc deleted file mode 100644 index fcd513e6..00000000 --- a/src/crypto/ghash.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 "crypto/ghash.h" - -#include -#include - -#include - -#include "base/logging.h" -#include "base/sys_byteorder.h" - -namespace crypto { - -// GaloisHash is a polynomial authenticator that works in GF(2^128). -// -// Elements of the field are represented in `little-endian' order (which -// matches the description in the paper[1]), thus the most significant bit is -// the right-most bit. (This is backwards from the way that everybody else does -// it.) -// -// We store field elements in a pair of such `little-endian' uint64s. So the -// value one is represented by {low = 2**63, high = 0} and doubling a value -// involves a *right* shift. -// -// [1] http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf - -namespace { - -// Get64 reads a 64-bit, big-endian number from |bytes|. -uint64_t Get64(const uint8_t bytes[8]) { - uint64_t t; - memcpy(&t, bytes, sizeof(t)); - return base::NetToHost64(t); -} - -// Put64 writes |x| to |bytes| as a 64-bit, big-endian number. -void Put64(uint8_t bytes[8], uint64_t x) { - x = base::HostToNet64(x); - memcpy(bytes, &x, sizeof(x)); -} - -// Reverse reverses the order of the bits of 4-bit number in |i|. -int Reverse(int i) { - i = ((i << 2) & 0xc) | ((i >> 2) & 0x3); - i = ((i << 1) & 0xa) | ((i >> 1) & 0x5); - return i; -} - -} // namespace - -GaloisHash::GaloisHash(const uint8_t key[16]) { - Reset(); - - // We precompute 16 multiples of |key|. However, when we do lookups into this - // table we'll be using bits from a field element and therefore the bits will - // be in the reverse order. So normally one would expect, say, 4*key to be in - // index 4 of the table but due to this bit ordering it will actually be in - // index 0010 (base 2) = 2. - FieldElement x = {Get64(key), Get64(key+8)}; - product_table_[0].low = 0; - product_table_[0].hi = 0; - product_table_[Reverse(1)] = x; - - for (int i = 0; i < 16; i += 2) { - product_table_[Reverse(i)] = Double(product_table_[Reverse(i/2)]); - product_table_[Reverse(i+1)] = Add(product_table_[Reverse(i)], x); - } -} - -void GaloisHash::Reset() { - state_ = kHashingAdditionalData; - additional_bytes_ = 0; - ciphertext_bytes_ = 0; - buf_used_ = 0; - y_.low = 0; - y_.hi = 0; -} - -void GaloisHash::UpdateAdditional(const uint8_t* data, size_t length) { - DCHECK_EQ(state_, kHashingAdditionalData); - additional_bytes_ += length; - Update(data, length); -} - -void GaloisHash::UpdateCiphertext(const uint8_t* data, size_t length) { - if (state_ == kHashingAdditionalData) { - // If there's any remaining additional data it's zero padded to the next - // full block. - if (buf_used_ > 0) { - memset(&buf_[buf_used_], 0, sizeof(buf_)-buf_used_); - UpdateBlocks(buf_, 1); - buf_used_ = 0; - } - state_ = kHashingCiphertext; - } - - DCHECK_EQ(state_, kHashingCiphertext); - ciphertext_bytes_ += length; - Update(data, length); -} - -void GaloisHash::Finish(void* output, size_t len) { - DCHECK(state_ != kComplete); - - if (buf_used_ > 0) { - // If there's any remaining data (additional data or ciphertext), it's zero - // padded to the next full block. - memset(&buf_[buf_used_], 0, sizeof(buf_)-buf_used_); - UpdateBlocks(buf_, 1); - buf_used_ = 0; - } - - state_ = kComplete; - - // The lengths of the additional data and ciphertext are included as the last - // block. The lengths are the number of bits. - y_.low ^= additional_bytes_*8; - y_.hi ^= ciphertext_bytes_*8; - MulAfterPrecomputation(product_table_, &y_); - - uint8_t *result, result_tmp[16]; - if (len >= 16) { - result = reinterpret_cast(output); - } else { - result = result_tmp; - } - - Put64(result, y_.low); - Put64(result + 8, y_.hi); - - if (len < 16) - memcpy(output, result_tmp, len); -} - -// static -GaloisHash::FieldElement GaloisHash::Add( - const FieldElement& x, - const FieldElement& y) { - // Addition in a characteristic 2 field is just XOR. - FieldElement z = {x.low^y.low, x.hi^y.hi}; - return z; -} - -// static -GaloisHash::FieldElement GaloisHash::Double(const FieldElement& x) { - const bool msb_set = x.hi & 1; - - FieldElement xx; - // Because of the bit-ordering, doubling is actually a right shift. - xx.hi = x.hi >> 1; - xx.hi |= x.low << 63; - xx.low = x.low >> 1; - - // If the most-significant bit was set before shifting then it, conceptually, - // becomes a term of x^128. This is greater than the irreducible polynomial - // so the result has to be reduced. The irreducible polynomial is - // 1+x+x^2+x^7+x^128. We can subtract that to eliminate the term at x^128 - // which also means subtracting the other four terms. In characteristic 2 - // fields, subtraction == addition == XOR. - if (msb_set) - xx.low ^= 0xe100000000000000ULL; - - return xx; -} - -void GaloisHash::MulAfterPrecomputation(const FieldElement* table, - FieldElement* x) { - FieldElement z = {0, 0}; - - // In order to efficiently multiply, we use the precomputed table of i*key, - // for i in 0..15, to handle four bits at a time. We could obviously use - // larger tables for greater speedups but the next convenient table size is - // 4K, which is a little large. - // - // In other fields one would use bit positions spread out across the field in - // order to reduce the number of doublings required. However, in - // characteristic 2 fields, repeated doublings are exceptionally cheap and - // it's not worth spending more precomputation time to eliminate them. - for (unsigned i = 0; i < 2; i++) { - uint64_t word; - if (i == 0) { - word = x->hi; - } else { - word = x->low; - } - - for (unsigned j = 0; j < 64; j += 4) { - Mul16(&z); - // the values in |table| are ordered for little-endian bit positions. See - // the comment in the constructor. - const FieldElement& t = table[word & 0xf]; - z.low ^= t.low; - z.hi ^= t.hi; - word >>= 4; - } - } - - *x = z; -} - -// kReductionTable allows for rapid multiplications by 16. A multiplication by -// 16 is a right shift by four bits, which results in four bits at 2**128. -// These terms have to be eliminated by dividing by the irreducible polynomial. -// In GHASH, the polynomial is such that all the terms occur in the -// least-significant 8 bits, save for the term at x^128. Therefore we can -// precompute the value to be added to the field element for each of the 16 bit -// patterns at 2**128 and the values fit within 12 bits. -static const uint16_t kReductionTable[16] = { - 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, - 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0, -}; - -// static -void GaloisHash::Mul16(FieldElement* x) { - const unsigned msw = x->hi & 0xf; - x->hi >>= 4; - x->hi |= x->low << 60; - x->low >>= 4; - x->low ^= static_cast(kReductionTable[msw]) << 48; -} - -void GaloisHash::UpdateBlocks(const uint8_t* bytes, size_t num_blocks) { - for (size_t i = 0; i < num_blocks; i++) { - y_.low ^= Get64(bytes); - bytes += 8; - y_.hi ^= Get64(bytes); - bytes += 8; - MulAfterPrecomputation(product_table_, &y_); - } -} - -void GaloisHash::Update(const uint8_t* data, size_t length) { - if (buf_used_ > 0) { - const size_t n = std::min(length, sizeof(buf_) - buf_used_); - memcpy(&buf_[buf_used_], data, n); - buf_used_ += n; - length -= n; - data += n; - - if (buf_used_ == sizeof(buf_)) { - UpdateBlocks(buf_, 1); - buf_used_ = 0; - } - } - - if (length >= 16) { - const size_t n = length / 16; - UpdateBlocks(data, n); - length -= n*16; - data += n*16; - } - - if (length > 0) { - memcpy(buf_, data, length); - buf_used_ = length; - } -} - -} // namespace crypto diff --git a/src/crypto/ghash.h b/src/crypto/ghash.h deleted file mode 100644 index b123dfe0..00000000 --- a/src/crypto/ghash.h +++ /dev/null @@ -1,88 +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 -#include - -#include "crypto/crypto_export.h" - -namespace crypto { - -// GaloisHash implements the polynomial authenticator part of GCM as specified -// in http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf -// Specifically it implements the GHASH function, defined in section 2.3 of -// that document. -// -// In SP-800-38D, GHASH is defined differently and takes only a single data -// argument. But it is always called with an argument of a certain form: -// GHASH_H (A || 0^v || C || 0^u || [len(A)]_64 || [len(C)]_64) -// This mirrors how the gcm-revised-spec.pdf version of GHASH handles its two -// data arguments. The two GHASH functions therefore differ only in whether the -// data is formatted inside or outside of the function. -// -// WARNING: do not use this as a generic authenticator. Polynomial -// authenticators must be used in the correct manner and any use outside of GCM -// requires careful consideration. -// -// WARNING: this code is not constant time. However, in all likelihood, nor is -// the implementation of AES that is used. -class CRYPTO_EXPORT GaloisHash { - public: - explicit GaloisHash(const uint8_t key[16]); - - // Reset prepares to digest a fresh message with the same key. This is more - // efficient than creating a fresh object. - void Reset(); - - // UpdateAdditional hashes in `additional' data. This is data that is not - // encrypted, but is covered by the authenticator. All additional data must - // be written before any ciphertext is written. - void UpdateAdditional(const uint8_t* data, size_t length); - - // UpdateCiphertext hashes in ciphertext to be authenticated. - void UpdateCiphertext(const uint8_t* data, size_t length); - - // Finish completes the hash computation and writes at most |len| bytes of - // the result to |output|. - void Finish(void* output, size_t len); - - private: - enum State { - kHashingAdditionalData, - kHashingCiphertext, - kComplete, - }; - - struct FieldElement { - uint64_t low, hi; - }; - - // Add returns |x|+|y|. - static FieldElement Add(const FieldElement& x, const FieldElement& y); - // Double returns 2*|x|. - static FieldElement Double(const FieldElement& x); - // MulAfterPrecomputation sets |x| = |x|*h where h is |table[1]| and - // table[i] = i*h for i=0..15. - static void MulAfterPrecomputation(const FieldElement* table, - FieldElement* x); - // Mul16 sets |x| = 16*|x|. - static void Mul16(FieldElement* x); - - // UpdateBlocks processes |num_blocks| 16-bytes blocks from |bytes|. - void UpdateBlocks(const uint8_t* bytes, size_t num_blocks); - // Update processes |length| bytes from |bytes| and calls UpdateBlocks on as - // much data as possible. It uses |buf_| to buffer any remaining data and - // always consumes all of |bytes|. - void Update(const uint8_t* bytes, size_t length); - - FieldElement y_; - State state_; - size_t additional_bytes_; - size_t ciphertext_bytes_; - uint8_t buf_[16]; - size_t buf_used_; - FieldElement product_table_[16]; -}; - -} // namespace crypto diff --git a/src/crypto/openssl_util.cc b/src/crypto/openssl_util.cc index 48ec3e2e..e2b859e4 100644 --- a/src/crypto/openssl_util.cc +++ b/src/crypto/openssl_util.cc @@ -5,8 +5,8 @@ #include "crypto/openssl_util.h" #include -#include #include +#include #include #include @@ -48,16 +48,15 @@ class OpenSSLInitSingleton { #if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL) const bool has_neon = (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; - // CRYPTO_set_NEON_capable is called before |SSL_library_init| because this - // stops BoringSSL from probing for NEON support via SIGILL in the case - // that getauxval isn't present. - CRYPTO_set_NEON_capable(has_neon); - // See https://code.google.com/p/chromium/issues/detail?id=341598 base::CPU cpu; - CRYPTO_set_NEON_functional(!cpu.has_broken_neon()); + // CRYPTO_set_NEON_capable is called before |CRYPTO_library_init| because + // this stops BoringSSL from probing for NEON support via SIGILL in the case + // that getauxval isn't present. Also workaround a CPU with broken NEON + // support. See https://code.google.com/p/chromium/issues/detail?id=341598 + CRYPTO_set_NEON_capable(has_neon && !cpu.has_broken_neon()); #endif - SSL_library_init(); + CRYPTO_library_init(); } ~OpenSSLInitSingleton() {} diff --git a/src/crypto/openssl_util.h b/src/crypto/openssl_util.h index 78fa66e2..d608cdeb 100644 --- a/src/crypto/openssl_util.h +++ b/src/crypto/openssl_util.h @@ -58,12 +58,12 @@ class ScopedOpenSSLSafeSizeBuffer { // multiple times. // This function is thread-safe, and OpenSSL will only ever be initialized once. // OpenSSL will be properly shut down on program exit. -void CRYPTO_EXPORT EnsureOpenSSLInit(); +CRYPTO_EXPORT void EnsureOpenSSLInit(); // Drains the OpenSSL ERR_get_error stack. On a debug build the error codes // are send to VLOG(1), on a release build they are disregarded. In most // cases you should pass FROM_HERE as the |location|. -void CRYPTO_EXPORT ClearOpenSSLERRStack( +CRYPTO_EXPORT void ClearOpenSSLERRStack( const tracked_objects::Location& location); // Place an instance of this class on the call stack to automatically clear diff --git a/src/net/base/address_family.cc b/src/net/base/address_family.cc index 1af5e001..8fc2bf02 100644 --- a/src/net/base/address_family.cc +++ b/src/net/base/address_family.cc @@ -5,6 +5,7 @@ #include "net/base/address_family.h" #include "base/logging.h" +#include "net/base/ip_address.h" #include "net/base/sys_addrinfo.h" namespace net { @@ -20,6 +21,10 @@ AddressFamily GetAddressFamily(const IPAddressNumber& address) { } } +AddressFamily GetAddressFamily(const IPAddress& address) { + return GetAddressFamily(address.bytes()); +} + int ConvertAddressFamily(AddressFamily address_family) { switch (address_family) { case ADDRESS_FAMILY_UNSPECIFIED: diff --git a/src/net/base/address_family.h b/src/net/base/address_family.h index 57ec48db..748090e2 100644 --- a/src/net/base/address_family.h +++ b/src/net/base/address_family.h @@ -10,6 +10,8 @@ namespace net { +class IPAddress; + // Enum wrapper around the address family types supported by host resolver // procedures. enum AddressFamily { @@ -36,6 +38,9 @@ typedef int HostResolverFlags; // Returns AddressFamily for |address|. NET_EXPORT AddressFamily GetAddressFamily(const IPAddressNumber& address); +// GetAddressFamily for net::IPAddress. +NET_EXPORT AddressFamily GetAddressFamily(const IPAddress& address); + // Maps the given AddressFamily to either AF_INET, AF_INET6 or AF_UNSPEC. NET_EXPORT int ConvertAddressFamily(AddressFamily address_family); diff --git a/src/net/base/escape.cc b/src/net/base/escape.cc new file mode 100644 index 00000000..fc7d5cf0 --- /dev/null +++ b/src/net/base/escape.cc @@ -0,0 +1,513 @@ +// 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/escape.h" + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_offset_string_conversions.h" +#include "base/strings/utf_string_conversions.h" + +namespace net { + +namespace { + +const char kHexString[] = "0123456789ABCDEF"; +inline char IntToHex(int i) { + DCHECK_GE(i, 0) << i << " not a hex value"; + DCHECK_LE(i, 15) << i << " not a hex value"; + return kHexString[i]; +} + +// A fast bit-vector map for ascii characters. +// +// Internally stores 256 bits in an array of 8 ints. +// Does quick bit-flicking to lookup needed characters. +struct Charmap { + bool Contains(unsigned char c) const { + return ((map[c >> 5] & (1 << (c & 31))) != 0); + } + + uint32_t map[8]; +}; + +// Given text to escape and a Charmap defining which values to escape, +// return an escaped string. If use_plus is true, spaces are converted +// to +, otherwise, if spaces are in the charmap, they are converted to +// %20. And if keep_escaped is true, %XX will be kept as it is, otherwise, if +// '%' is in the charmap, it is converted to %25. +std::string Escape(const std::string& text, + const Charmap& charmap, + bool use_plus, + bool keep_escaped = false) { + std::string escaped; + escaped.reserve(text.length() * 3); + for (unsigned int i = 0; i < text.length(); ++i) { + unsigned char c = static_cast(text[i]); + if (use_plus && ' ' == c) { + escaped.push_back('+'); + } else if (keep_escaped && '%' == c && i + 2 < text.length() && + base::IsHexDigit(text[i + 1]) && base::IsHexDigit(text[i + 2])) { + escaped.push_back('%'); + } else if (charmap.Contains(c)) { + escaped.push_back('%'); + escaped.push_back(IntToHex(c >> 4)); + escaped.push_back(IntToHex(c & 0xf)); + } else { + escaped.push_back(c); + } + } + return escaped; +} + +// Contains nonzero when the corresponding character is unescapable for normal +// URLs. These characters are the ones that may change the parsing of a URL, so +// we don't want to unescape them sometimes. In many case we won't want to +// unescape spaces, but that is controlled by parameters to Unescape*. +// +// The basic rule is that we can't unescape anything that would changing parsing +// like # or ?. We also can't unescape &, =, or + since that could be part of a +// query and that could change the server's parsing of the query. Nor can we +// unescape \ since src/url/ will convert it to a /. +// +// Lastly, we can't unescape anything that doesn't have a canonical +// representation in a URL. This means that unescaping will change the URL, and +// you could get different behavior if you copy and paste the URL, or press +// enter in the URL bar. The list of characters that fall into this category +// are the ones labeled PASS (allow either escaped or unescaped) in the big +// lookup table at the top of url/url_canon_path.cc. Also, characters +// that have CHAR_QUERY set in url/url_canon_internal.cc but are not +// allowed in query strings according to http://www.ietf.org/rfc/rfc3261.txt are +// not unescaped, to avoid turning a valid url according to spec into an +// invalid one. +const char kUrlUnescape[128] = { +// NULL, control chars... + 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, +// 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, +// @ A B C D E F G H I J K L M N O + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// P Q R S T U V W X Y Z [ \ ] ^ _ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, +// ` a b c d e f g h i j k l m n o + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// p q r s t u v w x y z { | } ~ + 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 +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(escaped_text[index + 1])); + const typename STR::value_type least_sig_digit( + static_cast(escaped_text[index + 2])); + if (base::IsHexDigit(most_sig_digit) && base::IsHexDigit(least_sig_digit)) { + *value = base::HexDigitToInt(most_sig_digit) * 16 + + base::HexDigitToInt(least_sig_digit); + return true; + } + return false; +} + +// Returns true if there is an Arabic Language Mark at |index|. |first_byte| +// is the byte at |index|. +template +bool HasArabicLanguageMarkAtIndex(const STR& escaped_text, + unsigned char first_byte, + size_t index) { + if (first_byte != 0xD8) + return false; + unsigned char second_byte; + if (!UnescapeUnsignedCharAtIndex(escaped_text, index + 3, &second_byte)) + return false; + return second_byte == 0x9c; +} + +// Returns true if there is a BiDi control char at |index|. |first_byte| is the +// byte at |index|. +template +bool HasThreeByteBidiControlCharAtIndex(const STR& escaped_text, + unsigned char first_byte, + size_t index) { + if (first_byte != 0xE2) + return false; + unsigned char second_byte; + if (!UnescapeUnsignedCharAtIndex(escaped_text, index + 3, &second_byte)) + return false; + if (second_byte != 0x80 && second_byte != 0x81) + return false; + unsigned char third_byte; + if (!UnescapeUnsignedCharAtIndex(escaped_text, index + 6, &third_byte)) + return false; + if (second_byte == 0x80) { + return third_byte == 0x8E || + third_byte == 0x8F || + (third_byte >= 0xAA && third_byte <= 0xAE); + } + return third_byte >= 0xA6 && third_byte <= 0xA9; +} + +// Returns true if there is a four-byte banned char at |index|. |first_byte| is +// the byte at |index|. +template +bool HasFourByteBannedCharAtIndex(const STR& escaped_text, + unsigned char first_byte, + size_t index) { + // The following characters are blacklisted for spoofability concerns. + // U+1F50F LOCK WITH INK PEN (%F0%9F%94%8F) + // U+1F510 CLOSED LOCK WITH KEY (%F0%9F%94%90) + // U+1F512 LOCK (%F0%9F%94%92) + // U+1F513 OPEN LOCK (%F0%9F%94%93) + if (first_byte != 0xF0) + return false; + + unsigned char second_byte; + if (!UnescapeUnsignedCharAtIndex(escaped_text, index + 3, &second_byte) || + second_byte != 0x9F) { + return false; + } + + unsigned char third_byte; + if (!UnescapeUnsignedCharAtIndex(escaped_text, index + 6, &third_byte) || + third_byte != 0x94) { + return false; + } + + unsigned char fourth_byte; + return UnescapeUnsignedCharAtIndex(escaped_text, index + 9, &fourth_byte) && + (fourth_byte == 0x8F || fourth_byte == 0x90 || fourth_byte == 0x92 || + fourth_byte == 0x93); +} + +// 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 +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; + + // The output of the unescaping is always smaller than the input, so we can + // reserve the input size to make sure we have enough buffer and don't have + // to allocate in the loop below. + STR result; + result.reserve(escaped_text.length()); + + // Locations of adjusted text. + for (size_t i = 0, max = escaped_text.size(); i < max; ++i) { + if (static_cast(escaped_text[i]) >= 128) { + // Non ASCII character, append as is. + result.push_back(escaped_text[i]); + continue; + } + + 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) + // + // The following spoofable characters are also banned, because they could + // be used to imitate parts of a web browser's UI. + // + // U+1F50F LOCK WITH INK PEN (%F0%9F%94%8F) + // U+1F510 CLOSED LOCK WITH KEY (%F0%9F%94%90) + // U+1F512 LOCK (%F0%9F%94%92) + // U+1F513 OPEN LOCK (%F0%9F%94%93) + // + // However, some schemes such as data: and file: need to parse the exact + // binary data when loading the URL. For that reason, + // SPOOFING_AND_CONTROL_CHARS allows unescaping BiDi control characters. + // DO NOT use SPOOFING_AND_CONTROL_CHARS if the parsed URL is going to be + // displayed in the UI. + if (!(rules & UnescapeRule::SPOOFING_AND_CONTROL_CHARS)) { + if (HasArabicLanguageMarkAtIndex(escaped_text, first_byte, i)) { + // Keep Arabic Language Mark escaped. + result.append(escaped_text, i, 6); + i += 5; + continue; + } + if (HasThreeByteBidiControlCharAtIndex(escaped_text, first_byte, i)) { + // Keep BiDi control char escaped. + result.append(escaped_text, i, 9); + i += 8; + continue; + } + if (HasFourByteBannedCharAtIndex(escaped_text, first_byte, i)) { + // Keep banned char escaped. + result.append(escaped_text, i, 12); + i += 11; + 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 == '/' || first_byte == '\\') && + (rules & UnescapeRule::PATH_SEPARATORS)) || + (first_byte > ' ' && first_byte != '/' && first_byte != '\\' && + (rules & UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)) || + // Additionally allow non-display characters if requested. + (first_byte < ' ' && + (rules & UnescapeRule::SPOOFING_AND_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 { + // 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) && + escaped_text[i] == '+') { + result.push_back(' '); + } else { + // Normal case for unescaped characters. + result.push_back(escaped_text[i]); + } + } + + return result; +} + +template +void AppendEscapedCharForHTMLImpl(typename str::value_type c, str* output) { + static const struct { + char key; + const char* replacement; + } kCharsToEscape[] = { + { '<', "<" }, + { '>', ">" }, + { '&', "&" }, + { '"', """ }, + { '\'', "'" }, + }; + size_t k; + for (k = 0; k < arraysize(kCharsToEscape); ++k) { + if (c == kCharsToEscape[k].key) { + const char* p = kCharsToEscape[k].replacement; + while (*p) + output->push_back(*p++); + break; + } + } + if (k == arraysize(kCharsToEscape)) + output->push_back(c); +} + +template +str EscapeForHTMLImpl(const str& input) { + str result; + result.reserve(input.size()); // Optimize for no escaping. + + for (typename str::const_iterator i = input.begin(); i != input.end(); ++i) + AppendEscapedCharForHTMLImpl(*i, &result); + + return result; +} + +// Everything except alphanumerics and !'()*-._~ +// See RFC 2396 for the list of reserved characters. +static const Charmap kQueryCharmap = {{ + 0xffffffffL, 0xfc00987dL, 0x78000001L, 0xb8000001L, + 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL +}}; + +// non-printable, non-7bit, and (including space) "#%:<>?[\]^`{|} +static const Charmap kPathCharmap = {{ + 0xffffffffL, 0xd400002dL, 0x78000000L, 0xb8000001L, + 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL +}}; + +#if defined(OS_MACOSX) +// non-printable, non-7bit, and (including space) "#%<>[\]^`{|} +static const Charmap kNSURLCharmap = {{ + 0xffffffffL, 0x5000002dL, 0x78000000L, 0xb8000001L, + 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL +}}; +#endif // defined(OS_MACOSX) + +// non-printable, non-7bit, and (including space) ?>=<;+'&%$#"![\]^`{|} +static const Charmap kUrlEscape = {{ + 0xffffffffL, 0xf80008fdL, 0x78000001L, 0xb8000001L, + 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL +}}; + +// non-7bit +static const Charmap kNonASCIICharmap = {{ + 0x00000000L, 0x00000000L, 0x00000000L, 0x00000000L, + 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL +}}; + +// Everything except alphanumerics, the reserved characters(;/?:@&=+$,) and +// !'()*-._~#[] +static const Charmap kExternalHandlerCharmap = {{ + 0xffffffffL, 0x50000025L, 0x50000000L, 0xb8000001L, + 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL +}}; + +} // namespace + +std::string EscapeQueryParamValue(const std::string& text, bool use_plus) { + return Escape(text, kQueryCharmap, use_plus); +} + +std::string EscapePath(const std::string& path) { + return Escape(path, kPathCharmap, false); +} + +#if defined(OS_MACOSX) +std::string EscapeNSURLPrecursor(const std::string& precursor) { + return Escape(precursor, kNSURLCharmap, false, true); +} +#endif // defined(OS_MACOSX) + +std::string EscapeUrlEncodedData(const std::string& path, bool use_plus) { + return Escape(path, kUrlEscape, use_plus); +} + +std::string EscapeNonASCII(const std::string& input) { + return Escape(input, kNonASCIICharmap, false); +} + +std::string EscapeExternalHandlerValue(const std::string& text) { + return Escape(text, kExternalHandlerCharmap, false, true); +} + +void AppendEscapedCharForHTML(char c, std::string* output) { + AppendEscapedCharForHTMLImpl(c, output); +} + +std::string EscapeForHTML(const std::string& input) { + return EscapeForHTMLImpl(input); +} + +base::string16 EscapeForHTML(const base::string16& input) { + return EscapeForHTMLImpl(input); +} + +std::string UnescapeURLComponent(const std::string& escaped_text, + UnescapeRule::Type rules) { + return UnescapeURLWithAdjustmentsImpl(escaped_text, rules, NULL); +} + +base::string16 UnescapeURLComponent(const base::string16& escaped_text, + UnescapeRule::Type rules) { + return UnescapeURLWithAdjustmentsImpl(escaped_text, rules, NULL); +} + +base::string16 UnescapeAndDecodeUTF8URLComponent(const std::string& text, + UnescapeRule::Type rules) { + return UnescapeAndDecodeUTF8URLComponentWithAdjustments(text, rules, NULL); +} + +base::string16 UnescapeAndDecodeUTF8URLComponentWithAdjustments( + const std::string& text, + UnescapeRule::Type rules, + base::OffsetAdjuster::Adjustments* adjustments) { + base::string16 result; + 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) { + static const struct { + const char* ampersand_code; + const char replacement; + } kEscapeToChars[] = { + { "<", '<' }, + { ">", '>' }, + { "&", '&' }, + { """, '"' }, + { "'", '\''}, + }; + + if (input.find(base::ASCIIToUTF16("&")) == std::string::npos) + return input; + + base::string16 ampersand_chars[arraysize(kEscapeToChars)]; + base::string16 text(input); + for (base::string16::iterator iter = text.begin(); + iter != text.end(); ++iter) { + if (*iter == '&') { + // Potential ampersand encode char. + size_t index = iter - text.begin(); + for (size_t i = 0; i < arraysize(kEscapeToChars); i++) { + 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); + break; + } + } + } + } + return text; +} + +} // namespace net diff --git a/src/net/base/escape.h b/src/net/base/escape.h new file mode 100644 index 00000000..759631ae --- /dev/null +++ b/src/net/base/escape.h @@ -0,0 +1,162 @@ +// 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_ESCAPE_H_ +#define NET_BASE_ESCAPE_H_ + +#include + +#include +#include + +#include "base/strings/string16.h" +#include "base/strings/utf_offset_string_conversions.h" +#include "net/base/net_export.h" + +namespace net { + +// Escaping -------------------------------------------------------------------- + +// Escapes characters in text suitable for use as a query parameter value. +// We %XX everything except alphanumerics and -_.!~*'() +// Spaces change to "+" unless you pass usePlus=false. +// This is basically the same as encodeURIComponent in javascript. +NET_EXPORT std::string EscapeQueryParamValue(const std::string& text, + bool use_plus); + +// Escapes a partial or complete file/pathname. This includes: +// non-printable, non-7bit, and (including space) "#%:<>?[\]^`{|} +// For the base::string16 version, we attempt a conversion to |codepage| before +// encoding the string. If this conversion fails, we return false. +NET_EXPORT std::string EscapePath(const std::string& path); + +#if defined(OS_MACOSX) +// Escapes characters as per expectations of NSURL. This includes: +// non-printable, non-7bit, and (including space) "#%<>[\]^`{|} +NET_EXPORT std::string EscapeNSURLPrecursor(const std::string& precursor); +#endif // defined(OS_MACOSX) + +// Escapes application/x-www-form-urlencoded content. This includes: +// non-printable, non-7bit, and (including space) ?>=<;+'&%$#"![\]^`{|} +// Space is escaped as + (if use_plus is true) and other special characters +// as %XX (hex). +NET_EXPORT std::string EscapeUrlEncodedData(const std::string& path, + bool use_plus); + +// Escapes all non-ASCII input. +NET_EXPORT std::string EscapeNonASCII(const std::string& input); + +// Escapes characters in text suitable for use as an external protocol handler +// command. +// We %XX everything except alphanumerics and -_.!~*'() and the restricted +// chracters (;/?:@&=+$,#[]) and a valid percent escape sequence (%XX). +NET_EXPORT std::string EscapeExternalHandlerValue(const std::string& text); + +// Appends the given character to the output string, escaping the character if +// the character would be interpretted as an HTML delimiter. +NET_EXPORT void AppendEscapedCharForHTML(char c, std::string* output); + +// Escapes chars that might cause this text to be interpretted as HTML tags. +NET_EXPORT std::string EscapeForHTML(const std::string& text); +NET_EXPORT base::string16 EscapeForHTML(const base::string16& text); + +// Unescaping ------------------------------------------------------------------ + +class UnescapeRule { + public: + // A combination of the following flags that is passed to the unescaping + // functions. + typedef uint32_t Type; + + enum { + // Don't unescape anything at all. + NONE = 0, + + // Don't unescape anything special, but all normal unescaping will happen. + // This is a placeholder and can't be combined with other flags (since it's + // just the absence of them). All other unescape rules imply "normal" in + // addition to their special meaning. Things like escaped letters, digits, + // and most symbols will get unescaped with this mode. + NORMAL = 1 << 0, + + // Convert %20 to spaces. In some places where we're showing URLs, we may + // want this. In places where the URL may be copied and pasted out, then + // you wouldn't want this since it might not be interpreted in one piece + // by other applications. + SPACES = 1 << 1, + + // Unescapes '/' and '\\'. If these characters were unescaped, the resulting + // URL won't be the same as the source one. Moreover, they are dangerous to + // unescape in strings that will be used as file paths or names. This value + // should only be used when slashes don't have special meaning, like data + // URLs. + PATH_SEPARATORS = 1 << 2, + + // Unescapes various characters that will change the meaning of URLs, + // including '%', '+', '&', '#'. Does not unescape path separators. + // If these characters were unescaped, the resulting URL won't be the same + // as the source one. This flag is used when generating final output like + // filenames for URLs where we won't be interpreting as a URL and want to do + // as much unescaping as possible. + URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS = 1 << 3, + + // A combination of URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS and + // PATH_SEPARATORS. Warning about the use of PATH_SEPARATORS also apply + // here. + // TODO(mmenke): Audit all uses of this and replace with the above values, + // as needed. + URL_SPECIAL_CHARS = + PATH_SEPARATORS | URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS, + + // Unescapes characters that can be used in spoofing attempts (such as LOCK) + // and control characters (such as BiDi control characters and %01). This + // INCLUDES NULLs. This is used for rare cases such as data: URL decoding + // where the result is binary data. + // + // DO NOT use SPOOFING_AND_CONTROL_CHARS if the URL is going to be displayed + // in the UI for security reasons. + SPOOFING_AND_CONTROL_CHARS = 1 << 4, + + // URL queries use "+" for space. This flag controls that replacement. + REPLACE_PLUS_WITH_SPACE = 1 << 5, + }; +}; + +// Unescapes |escaped_text| and returns the result. +// Unescaping consists of looking for the exact pattern "%XX", where each X is +// a hex digit, and converting to the character with the numerical value of +// those digits. Thus "i%20=%203%3b" unescapes to "i = 3;". +// +// Watch out: this doesn't necessarily result in the correct final result, +// because the encoding may be unknown. For example, the input might be ASCII, +// which, after unescaping, is supposed to be interpreted as UTF-8, and then +// converted into full UTF-16 chars. This function won't tell you if any +// conversions need to take place, it only unescapes. +NET_EXPORT std::string UnescapeURLComponent(const std::string& escaped_text, + UnescapeRule::Type rules); +NET_EXPORT base::string16 UnescapeURLComponent( + const base::string16& escaped_text, + UnescapeRule::Type rules); + +// 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. |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); +NET_EXPORT base::string16 UnescapeAndDecodeUTF8URLComponentWithAdjustments( + const std::string& text, + UnescapeRule::Type rules, + base::OffsetAdjuster::Adjustments* adjustments); + +// Unescapes the following ampersand character codes from |text|: +// < > & " ' +NET_EXPORT base::string16 UnescapeForHTML(const base::string16& text); + +} // namespace net + +#endif // NET_BASE_ESCAPE_H_ diff --git a/src/net/base/host_port_pair.cc b/src/net/base/host_port_pair.cc index 03083f33..a0dd2d50 100644 --- a/src/net/base/host_port_pair.cc +++ b/src/net/base/host_port_pair.cc @@ -20,13 +20,11 @@ HostPortPair::HostPortPair(const std::string& in_host, uint16_t in_port) : host_(in_host), port_(in_port) { } -#if 0 // static HostPortPair HostPortPair::FromURL(const GURL& url) { return HostPortPair(url.HostNoBrackets(), static_cast(url.EffectiveIntPort())); } -#endif // static HostPortPair HostPortPair::FromIPEndPoint(const IPEndPoint& ipe) { diff --git a/src/net/base/ip_address.cc b/src/net/base/ip_address.cc index 060477ea..170be40c 100644 --- a/src/net/base/ip_address.cc +++ b/src/net/base/ip_address.cc @@ -17,9 +17,19 @@ IPAddress::IPAddress() {} IPAddress::IPAddress(const IPAddressNumber& address) : ip_address_(address) {} +IPAddress::IPAddress(const IPAddress& other) = default; + IPAddress::IPAddress(const uint8_t* address, size_t address_len) : ip_address_(address, address + address_len) {} +IPAddress::IPAddress(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) { + ip_address_.reserve(4); + ip_address_.push_back(b0); + ip_address_.push_back(b1); + ip_address_.push_back(b2); + ip_address_.push_back(b3); +} + IPAddress::~IPAddress() {} bool IPAddress::IsIPv4() const { @@ -38,7 +48,16 @@ bool IPAddress::IsReserved() const { return IsIPAddressReserved(ip_address_); } -bool IPAddress::IsIPv4Mapped() const { +bool IPAddress::IsZero() const { + for (auto x : ip_address_) { + if (x != 0) + return false; + } + + return !empty(); +} + +bool IPAddress::IsIPv4MappedIPv6() const { return net::IsIPv4Mapped(ip_address_); } @@ -46,14 +65,12 @@ std::string IPAddress::ToString() const { return IPAddressToString(ip_address_); } -// static -bool IPAddress::FromIPLiteral(const base::StringPiece& ip_literal, - IPAddress* ip_address) { +bool IPAddress::AssignFromIPLiteral(const base::StringPiece& ip_literal) { std::vector number; if (!ParseIPLiteralToNumber(ip_literal, &number)) return false; - std::swap(number, ip_address->ip_address_); + std::swap(number, ip_address_); return true; } @@ -61,6 +78,10 @@ bool IPAddress::operator==(const IPAddress& that) const { return ip_address_ == that.ip_address_; } +bool IPAddress::operator!=(const IPAddress& that) const { + return ip_address_ != that.ip_address_; +} + bool IPAddress::operator<(const IPAddress& that) const { // Sort IPv4 before IPv6. if (ip_address_.size() != that.ip_address_.size()) { @@ -70,4 +91,27 @@ bool IPAddress::operator<(const IPAddress& that) const { return ip_address_ < that.ip_address_; } +std::string IPAddressToStringWithPort(const IPAddress& address, uint16_t port) { + return IPAddressToStringWithPort(address.bytes(), port); +} + +std::string IPAddressToPackedString(const IPAddress& address) { + return IPAddressToPackedString(address.bytes()); +} + +IPAddress ConvertIPv4ToIPv4MappedIPv6(const IPAddress& address) { + return IPAddress(ConvertIPv4NumberToIPv6Number(address.bytes())); +} + +IPAddress ConvertIPv4MappedIPv6ToIPv4(const IPAddress& address) { + return IPAddress(ConvertIPv4MappedToIPv4(address.bytes())); +} + +bool IPAddressMatchesPrefix(const IPAddress& ip_address, + const IPAddress& ip_prefix, + size_t prefix_length_in_bits) { + return IPNumberMatchesPrefix(ip_address.bytes(), ip_prefix.bytes(), + prefix_length_in_bits); +} + } // namespace net diff --git a/src/net/base/ip_address.h b/src/net/base/ip_address.h index 69e6696f..f1f7480d 100644 --- a/src/net/base/ip_address.h +++ b/src/net/base/ip_address.h @@ -29,6 +29,8 @@ class NET_EXPORT IPAddress { // Creates an IP address from a deprecated IPAddressNumber. explicit IPAddress(const IPAddressNumber& address); + IPAddress(const IPAddress& other); + // Copies the input address to |ip_address_|. The input is expected to be in // network byte order. template @@ -39,6 +41,10 @@ class NET_EXPORT IPAddress { // parameter. The input is expected to be in network byte order. IPAddress(const uint8_t* address, size_t address_len); + // Initializes |ip_address_| from the 4 bX bytes. The bytes are expected to be + // in network byte order. + IPAddress(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3); + ~IPAddress(); // Returns true if the IP has |kIPv4AddressSize| elements. @@ -56,8 +62,11 @@ class NET_EXPORT IPAddress { // protocols's reserved ranges. bool IsReserved() const; + // Returns true if the IP is "zero" (e.g. the 0.0.0.0 IPv4 address). + bool IsZero() const; + // Returns true if |ip_address_| is an IPv4-mapped IPv6 address. - bool IsIPv4Mapped() const; + bool IsIPv4MappedIPv6() const; // The size in bytes of |ip_address_|. size_t size() const { return ip_address_.size(); } @@ -66,19 +75,20 @@ class NET_EXPORT IPAddress { bool empty() const { return ip_address_.empty(); } // Returns the canonical string representation of an IP address. - // For example: "192.168.0.1" or "::1". The IP address must be - // valid, calling this on an invalid address will result in a crash. + // For example: "192.168.0.1" or "::1". Returns the empty string when + // |ip_address_| is invalid. std::string ToString() const; // Parses an IP address literal (either IPv4 or IPv6) to its numeric value. - // Returns true on success and fills |ip_address| with the numeric value. - static bool FromIPLiteral(const base::StringPiece& ip_literal, - IPAddress* ip_address) WARN_UNUSED_RESULT; + // Returns true on success and fills |ip_address_| with the numeric value. + bool AssignFromIPLiteral(const base::StringPiece& ip_literal) + WARN_UNUSED_RESULT; // Returns the underlying byte vector. const std::vector& bytes() const { return ip_address_; }; bool operator==(const IPAddress& that) const; + bool operator!=(const IPAddress& that) const; bool operator<(const IPAddress& that) const; private: @@ -89,6 +99,40 @@ class NET_EXPORT IPAddress { // This class is copyable and assignable. }; +// TODO(Martijnc): These utility functions currently forward the calls to +// the IPAddressNumber implementations. Move the implementations over when +// the IPAddressNumber migration is complete. https://crbug.com/496258. + +// Returns the canonical string representation of an IP address along with its +// port. For example: "192.168.0.1:99" or "[::1]:80". +NET_EXPORT std::string IPAddressToStringWithPort(const IPAddress& address, + uint16_t port); + +// Returns the address as a sequence of bytes in network-byte-order. +NET_EXPORT std::string IPAddressToPackedString(const IPAddress& address); + +// Converts an IPv4 address to an IPv4-mapped IPv6 address. +// For example 192.168.0.1 would be converted to ::ffff:192.168.0.1. +NET_EXPORT IPAddress ConvertIPv4ToIPv4MappedIPv6(const IPAddress& address); + +// Converts an IPv4-mapped IPv6 address to IPv4 address. Should only be called +// on IPv4-mapped IPv6 addresses. +NET_EXPORT IPAddress ConvertIPv4MappedIPv6ToIPv4(const IPAddress& address); + +// Compares an IP address to see if it falls within the specified IP block. +// Returns true if it does, false otherwise. +// +// The IP block is given by (|ip_prefix|, |prefix_length_in_bits|) -- any +// IP address whose |prefix_length_in_bits| most significant bits match +// |ip_prefix| will be matched. +// +// In cases when an IPv4 address is being compared to an IPv6 address prefix +// and vice versa, the IPv4 addresses will be converted to IPv4-mapped +// (IPv6) addresses. +NET_EXPORT bool IPAddressMatchesPrefix(const IPAddress& ip_address, + const IPAddress& ip_prefix, + size_t prefix_length_in_bits); + } // namespace net #endif // NET_BASE_IP_ADDRESS_NET_H_ diff --git a/src/net/base/ip_address_number.cc b/src/net/base/ip_address_number.cc index 1e44b7d3..80cd7e92 100644 --- a/src/net/base/ip_address_number.cc +++ b/src/net/base/ip_address_number.cc @@ -102,8 +102,6 @@ std::string IPAddressToString(const uint8_t* address, size_t address_len) { url::AppendIPv4Address(address, &output); } else if (address_len == kIPv6AddressSize) { url::AppendIPv6Address(address, &output); - } else { - CHECK(false) << "Invalid IP address with length: " << address_len; } output.Complete(); @@ -114,6 +112,8 @@ std::string IPAddressToStringWithPort(const uint8_t* address, size_t address_len, uint16_t port) { std::string address_str = IPAddressToString(address, address_len); + if (address_str.empty()) + return address_str; if (address_len == kIPv6AddressSize) { // Need to bracket IPv6 addresses since they contain colons. @@ -123,17 +123,16 @@ std::string IPAddressToStringWithPort(const uint8_t* address, } std::string IPAddressToString(const IPAddressNumber& addr) { - return IPAddressToString(&addr.front(), addr.size()); + return IPAddressToString(addr.data(), addr.size()); } std::string IPAddressToStringWithPort(const IPAddressNumber& addr, uint16_t port) { - return IPAddressToStringWithPort(&addr.front(), addr.size(), port); + return IPAddressToStringWithPort(addr.data(), addr.size(), port); } std::string IPAddressToPackedString(const IPAddressNumber& addr) { - return std::string(reinterpret_cast(&addr.front()), - addr.size()); + return std::string(reinterpret_cast(addr.data()), addr.size()); } bool ParseURLHostnameToNumber(const std::string& hostname, diff --git a/src/net/base/ip_address_number.h b/src/net/base/ip_address_number.h index 2671a24d..4434fa05 100644 --- a/src/net/base/ip_address_number.h +++ b/src/net/base/ip_address_number.h @@ -37,12 +37,14 @@ static const size_t kIPv6AddressSize = 16; NET_EXPORT bool IsIPAddressReserved(const IPAddressNumber& address); // Returns the string representation of an IP address. -// For example: "192.168.0.1" or "::1". +// For example: "192.168.0.1" or "::1". Returns the empty string when |address| +// is invalid. NET_EXPORT std::string IPAddressToString(const uint8_t* address, size_t address_len); // Returns the string representation of an IP address along with its port. -// For example: "192.168.0.1:99" or "[::1]:80". +// For example: "192.168.0.1:99" or "[::1]:80". Returns the empty string when +// |address| is invalid (the port will be ignored). NET_EXPORT std::string IPAddressToStringWithPort(const uint8_t* address, size_t address_len, uint16_t port); diff --git a/src/net/base/ip_endpoint.cc b/src/net/base/ip_endpoint.cc index 5b355fe2..df0cb569 100644 --- a/src/net/base/ip_endpoint.cc +++ b/src/net/base/ip_endpoint.cc @@ -19,7 +19,10 @@ #include "base/strings/string_number_conversions.h" #include "base/sys_byteorder.h" #include "net/base/ip_address.h" -#include "net/base/net_util.h" + +#if defined(OS_WIN) +#include "net/base/winsock_util.h" +#endif namespace net { @@ -29,6 +32,52 @@ namespace { const socklen_t kSockaddrInSize = sizeof(struct sockaddr_in); const socklen_t kSockaddrIn6Size = sizeof(struct sockaddr_in6); +// Extracts the address and port portions of a sockaddr. +bool GetIPAddressFromSockAddr(const struct sockaddr* sock_addr, + socklen_t sock_addr_len, + const uint8_t** address, + size_t* address_len, + uint16_t* port) { + if (sock_addr->sa_family == AF_INET) { + if (sock_addr_len < static_cast(sizeof(struct sockaddr_in))) + return false; + const struct sockaddr_in* addr = + reinterpret_cast(sock_addr); + *address = reinterpret_cast(&addr->sin_addr); + *address_len = kIPv4AddressSize; + if (port) + *port = base::NetToHost16(addr->sin_port); + return true; + } + + if (sock_addr->sa_family == AF_INET6) { + if (sock_addr_len < static_cast(sizeof(struct sockaddr_in6))) + return false; + const struct sockaddr_in6* addr = + reinterpret_cast(sock_addr); + *address = reinterpret_cast(&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(sizeof(SOCKADDR_BTH))) + return false; + const SOCKADDR_BTH* addr = reinterpret_cast(sock_addr); + *address = reinterpret_cast(&addr->btAddr); + *address_len = kBluetoothAddressSize; + if (port) + *port = static_cast(addr->port); + return true; + } +#endif + + return false; // Unrecognized |sa_family|. +} + } // namespace IPEndPoint::IPEndPoint() : port_(0) {} @@ -40,8 +89,7 @@ IPEndPoint::IPEndPoint(const IPAddressNumber& address, uint16_t port) } IPEndPoint::IPEndPoint(const IPAddress& address, uint16_t port) - : address_(address.bytes()), port_(port) { -} + : address_(address), port_(port) {} IPEndPoint::IPEndPoint(const IPEndPoint& endpoint) { address_ = endpoint.address_; @@ -49,7 +97,7 @@ IPEndPoint::IPEndPoint(const IPEndPoint& endpoint) { } AddressFamily IPEndPoint::GetFamily() const { - return GetAddressFamily(address_); + return GetAddressFamily(address_.bytes()); } int IPEndPoint::GetSockAddrFamily() const { @@ -77,7 +125,7 @@ bool IPEndPoint::ToSockAddr(struct sockaddr* address, memset(addr, 0, sizeof(struct sockaddr_in)); addr->sin_family = AF_INET; addr->sin_port = base::HostToNet16(port_); - memcpy(&addr->sin_addr, &address_[0], kIPv4AddressSize); + memcpy(&addr->sin_addr, &address_.bytes()[0], kIPv4AddressSize); break; } case kIPv6AddressSize: { @@ -89,7 +137,7 @@ bool IPEndPoint::ToSockAddr(struct sockaddr* address, memset(addr6, 0, sizeof(struct sockaddr_in6)); addr6->sin6_family = AF_INET6; addr6->sin6_port = base::HostToNet16(port_); - memcpy(&addr6->sin6_addr, &address_[0], kIPv6AddressSize); + memcpy(&addr6->sin6_addr, &address_.bytes()[0], kIPv6AddressSize); break; } default: @@ -110,17 +158,17 @@ bool IPEndPoint::FromSockAddr(const struct sockaddr* sock_addr, return false; } - address_.assign(address, address + address_len); + address_ = net::IPAddress(address, address_len); port_ = port; return true; } std::string IPEndPoint::ToString() const { - return IPAddressToStringWithPort(address_, port_); + return IPAddressToStringWithPort(address_.bytes(), port_); } std::string IPEndPoint::ToStringWithoutPort() const { - return IPAddressToString(address_); + return address_.ToString(); } bool IPEndPoint::operator<(const IPEndPoint& other) const { diff --git a/src/net/base/ip_endpoint.h b/src/net/base/ip_endpoint.h index 07ef3841..a1c12296 100644 --- a/src/net/base/ip_endpoint.h +++ b/src/net/base/ip_endpoint.h @@ -11,7 +11,7 @@ #include "base/compiler_specific.h" #include "net/base/address_family.h" -#include "net/base/ip_address_number.h" +#include "net/base/ip_address.h" #include "net/base/net_export.h" #include "net/base/sys_addrinfo.h" @@ -19,8 +19,6 @@ struct sockaddr; namespace net { -class IPAddress; - // An IPEndPoint represents the address of a transport endpoint: // * IP address (either v4 or v6) // * Port @@ -33,7 +31,7 @@ class NET_EXPORT IPEndPoint { IPEndPoint(const IPAddress& address, uint16_t port); IPEndPoint(const IPEndPoint& endpoint); - const IPAddressNumber& address() const { return address_; } + const IPAddress& address() const { return address_; } uint16_t port() const { return port_; } // Returns AddressFamily of the address. @@ -59,18 +57,19 @@ class NET_EXPORT IPEndPoint { bool FromSockAddr(const struct sockaddr* address, socklen_t address_length) WARN_UNUSED_RESULT; - // Returns value as a string (e.g. "127.0.0.1:80"). The IP address must be - // valid. + // Returns value as a string (e.g. "127.0.0.1:80"). Returns the empty string + // when |address_| is invalid (the port will be ignored). std::string ToString() const; - // As above, but without port. + // As above, but without port. Returns the empty string when address_ is + // invalid. std::string ToStringWithoutPort() const; bool operator<(const IPEndPoint& that) const; bool operator==(const IPEndPoint& that) const; private: - IPAddressNumber address_; + IPAddress address_; uint16_t port_; }; diff --git a/src/net/base/linked_hash_map.h b/src/net/base/linked_hash_map.h index b86654bd..e3d12d63 100644 --- a/src/net/base/linked_hash_map.h +++ b/src/net/base/linked_hash_map.h @@ -18,9 +18,9 @@ #include #include +#include #include -#include "base/containers/hash_tables.h" #include "base/logging.h" #include "base/macros.h" @@ -30,11 +30,11 @@ // // We also keep a map for find. Since std::list is a // doubly-linked list, the iterators should remain stable. -template +template > class linked_hash_map { private: typedef std::list > ListType; - typedef base::hash_map MapType; + typedef std::unordered_map MapType; public: typedef typename ListType::iterator iterator; diff --git a/src/net/base/net_error_list.h b/src/net/base/net_error_list.h index 296024b2..be1b712c 100644 --- a/src/net/base/net_error_list.h +++ b/src/net/base/net_error_list.h @@ -363,6 +363,9 @@ NET_ERROR(CT_STH_INCOMPLETE, -169) // internally by the network stack. NET_ERROR(UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH, -170) +// Certificate Transparency: Failed to parse the received consistency proof. +NET_ERROR(CT_CONSISTENCY_PROOF_PARSING_FAILED, -171) + // Certificate error codes // // The values of certificate error codes must be consecutive. @@ -791,8 +794,7 @@ NET_ERROR(SELF_SIGNED_CERT_GENERATION_FAILED, -713) // The certificate database changed in some way. NET_ERROR(CERT_DATABASE_CHANGED, -714) -// Failure to import Channel ID. -NET_ERROR(CHANNEL_ID_IMPORT_FAILED, -715) +// Error -715 was removed (CHANNEL_ID_IMPORT_FAILED) // DNS error codes. diff --git a/src/net/base/net_util.cc b/src/net/base/net_util.cc deleted file mode 100644 index 146d32a9..00000000 --- a/src/net/base/net_util.cc +++ /dev/null @@ -1,617 +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/net_util.h" - -#include - -#include -#include -#include - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#include -#include -#include -#pragma comment(lib, "iphlpapi.lib") -#elif defined(OS_POSIX) -#include -#include -#include -#include -#if !defined(OS_NACL) -#include -#if !defined(OS_ANDROID) -#include -#endif // !defined(OS_NACL) -#endif // !defined(OS_ANDROID) -#endif // defined(OS_POSIX) - -#include "base/json/string_escape.h" -#include "base/logging.h" -#include "base/strings/string_piece.h" -#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/sys_byteorder.h" -#include "base/values.h" -#include "net/base/ip_address_number.h" -#include "url/gurl.h" -#include "url/third_party/mozilla/url_parse.h" -#include "url/url_canon.h" -#include "url/url_canon_ip.h" - -#if defined(OS_ANDROID) -#include "net/android/network_library.h" -#endif -#if defined(OS_WIN) -#include "net/base/winsock_init.h" -#endif - -namespace net { - -namespace { - -#if 0 -std::string NormalizeHostname(base::StringPiece host) { - std::string result = base::ToLowerASCII(host); - if (!result.empty() && *result.rbegin() == '.') - result.resize(result.size() - 1); - return result; -} -#endif - -#if 0 -bool IsNormalizedLocalhostTLD(const std::string& host) { - return base::EndsWith(host, ".localhost", base::CompareCase::SENSITIVE); -} -#endif - -#if 0 -// |host| should be normalized. -bool IsLocalHostname(const std::string& host) { - return host == "localhost" || host == "localhost.localdomain" || - IsNormalizedLocalhostTLD(host); -} -#endif - -#if 0 -// |host| should be normalized. -bool IsLocal6Hostname(const std::string& host) { - return host == "localhost6" || host == "localhost6.localdomain6"; -} -#endif - -} // namespace - -#if 0 -std::string CanonicalizeHost(const std::string& host, - url::CanonHostInfo* host_info) { - // Try to canonicalize the host. - const url::Component raw_host_component(0, static_cast(host.length())); - std::string canon_host; - 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::CanonHostInfo::BROKEN) { - // Success! Assert that there's no extra garbage. - canon_host_output.Complete(); - DCHECK_EQ(host_info->out_host.len, static_cast(canon_host.length())); - } else { - // Empty host, or canonicalization failed. We'll return empty. - canon_host.clear(); - } - - return canon_host; -} -#endif - -#if 0 -std::string GetDirectoryListingHeader(const base::string16& title) { - static const base::StringPiece header( - NetModule::GetResource(IDR_DIR_HEADER_HTML)); - // This can be null in unit tests. - DLOG_IF(WARNING, header.empty()) << - "Missing resource: directory listing header"; - - std::string result; - if (!header.empty()) - result.assign(header.data(), header.size()); - - result.append("\n"); - - return result; -} -#endif - -inline bool IsHostCharAlphanumeric(char c) { - // We can just check lowercase because uppercase characters have already been - // normalized. - return ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')); -} - -bool IsCanonicalizedHostCompliant(const std::string& host) { - if (host.empty()) - return false; - - bool in_component = false; - bool most_recent_component_started_alphanumeric = false; - - for (std::string::const_iterator i(host.begin()); i != host.end(); ++i) { - const char c = *i; - if (!in_component) { - most_recent_component_started_alphanumeric = IsHostCharAlphanumeric(c); - if (!most_recent_component_started_alphanumeric && (c != '-') && - (c != '_')) { - return false; - } - in_component = true; - } else if (c == '.') { - in_component = false; - } else if (!IsHostCharAlphanumeric(c) && (c != '-') && (c != '_')) { - return false; - } - } - - return most_recent_component_started_alphanumeric; -} - -int SetNonBlocking(int fd) { -#if defined(OS_WIN) - unsigned long no_block = 1; - return ioctlsocket(fd, FIONBIO, &no_block); -#elif defined(OS_POSIX) - int flags = fcntl(fd, F_GETFL, 0); - if (-1 == flags) - return flags; - return fcntl(fd, F_SETFL, flags | O_NONBLOCK); -#endif -} - -#if 0 -bool ParseHostAndPort(std::string::const_iterator host_and_port_begin, - std::string::const_iterator host_and_port_end, - std::string* host, - int* port) { - if (host_and_port_begin >= host_and_port_end) - return false; - - // 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::Component auth_component(0, auth_len); - url::Component username_component; - url::Component password_component; - url::Component hostname_component; - url::Component port_component; - - url::ParseAuthority(auth_begin, auth_component, &username_component, - &password_component, &hostname_component, &port_component); - - // There shouldn't be a username/password. - if (username_component.is_valid() || password_component.is_valid()) - return false; - - if (!hostname_component.is_nonempty()) - return false; // Failed parsing. - - int parsed_port_number = -1; - if (port_component.is_nonempty()) { - 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. - if (parsed_port_number < 0) - return false; // Failed parsing the port number. - } - - if (port_component.len == 0) - return false; // Reject inputs like "foo:" - - unsigned char tmp_ipv6_addr[16]; - - // If the hostname starts with a bracket, it is either an IPv6 literal or - // invalid. If it is an IPv6 literal then strip the brackets. - if (hostname_component.len > 0 && - auth_begin[hostname_component.begin] == '[') { - if (auth_begin[hostname_component.end() - 1] == ']' && - url::IPv6AddressToNumber( - auth_begin, hostname_component, tmp_ipv6_addr)) { - // Strip the brackets. - hostname_component.begin++; - hostname_component.len -= 2; - } else { - return false; - } - } - - // Pass results back to caller. - host->assign(auth_begin + hostname_component.begin, hostname_component.len); - *port = parsed_port_number; - - return true; // Success. -} - -bool ParseHostAndPort(const std::string& host_and_port, - std::string* host, - int* port) { - return ParseHostAndPort( - host_and_port.begin(), host_and_port.end(), host, port); -} -#endif - -#if 0 -std::string GetHostAndPort(const GURL& url) { - // For IPv6 literals, GURL::host() already includes the brackets so it is - // safe to just append a colon. - return base::StringPrintf("%s:%d", url.host().c_str(), - url.EffectiveIntPort()); -} - -std::string GetHostAndOptionalPort(const GURL& url) { - // For IPv6 literals, GURL::host() already includes the brackets - // so it is safe to just append a colon. - if (url.has_port()) - return base::StringPrintf("%s:%s", url.host().c_str(), url.port().c_str()); - return url.host(); -} -#endif - -#if 0 -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::CanonHostInfo host_info; - std::string canonical_name = CanonicalizeHost(host_or_ip, &host_info); - - // If canonicalization fails, then the input is truly malformed. However, - // to avoid mis-reporting bad inputs as "non-unique", treat them as unique. - if (canonical_name.empty()) - return false; - - // If |hostname| is an IP address, check to see if it's in an IANA-reserved - // range. - if (host_info.IsIPAddress()) { - IPAddressNumber host_addr; - if (!ParseIPLiteralToNumber(hostname.substr(host_info.out_host.begin, - host_info.out_host.len), - &host_addr)) { - return false; - } - switch (host_info.family) { - case url::CanonHostInfo::IPV4: - case url::CanonHostInfo::IPV6: - return IsIPAddressReserved(host_addr); - case url::CanonHostInfo::NEUTRAL: - case url::CanonHostInfo::BROKEN: - return false; - } - } - - // Check for a registry controlled portion of |hostname|, ignoring private - // registries, as they already chain to ICANN-administered registries, - // and explicitly ignoring unknown registries. - // - // Note: This means that as new gTLDs are introduced on the Internet, they - // will be treated as non-unique until the registry controlled domain list - // is updated. However, because gTLDs are expected to provide significant - // advance notice to deprecate older versions of this code, this an - // acceptable tradeoff. - return 0 == registry_controlled_domains::GetRegistryLength( - canonical_name, - registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, - registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); -} -#endif - -SockaddrStorage::SockaddrStorage(const SockaddrStorage& other) - : addr_len(other.addr_len), - addr(reinterpret_cast(&addr_storage)) { - memcpy(addr, other.addr, addr_len); -} - -void SockaddrStorage::operator=(const SockaddrStorage& other) { - addr_len = other.addr_len; - // addr is already set to &this->addr_storage by default ctor. - memcpy(addr, other.addr, addr_len); -} - -// Extracts the address and port portions of a sockaddr. -bool GetIPAddressFromSockAddr(const struct sockaddr* sock_addr, - socklen_t sock_addr_len, - const uint8_t** address, - size_t* address_len, - uint16_t* port) { - if (sock_addr->sa_family == AF_INET) { - if (sock_addr_len < static_cast(sizeof(struct sockaddr_in))) - return false; - const struct sockaddr_in* addr = - reinterpret_cast(sock_addr); - *address = reinterpret_cast(&addr->sin_addr); - *address_len = kIPv4AddressSize; - if (port) - *port = base::NetToHost16(addr->sin_port); - return true; - } - - if (sock_addr->sa_family == AF_INET6) { - if (sock_addr_len < static_cast(sizeof(struct sockaddr_in6))) - return false; - const struct sockaddr_in6* addr = - reinterpret_cast(sock_addr); - *address = reinterpret_cast(&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(sizeof(SOCKADDR_BTH))) - return false; - const SOCKADDR_BTH* addr = - reinterpret_cast(sock_addr); - *address = reinterpret_cast(&addr->btAddr); - *address_len = kBluetoothAddressSize; - if (port) - *port = static_cast(addr->port); - return true; - } -#endif - - return false; // Unrecognized |sa_family|. -} - -std::string NetAddressToString(const struct sockaddr* sa, - socklen_t sock_addr_len) { - const uint8_t* address; - size_t address_len; - if (!GetIPAddressFromSockAddr(sa, sock_addr_len, &address, - &address_len, NULL)) { - NOTREACHED(); - return std::string(); - } - return IPAddressToString(address, address_len); -} - -std::string NetAddressToStringWithPort(const struct sockaddr* sa, - socklen_t sock_addr_len) { - const uint8_t* address; - size_t address_len; - uint16_t port; - if (!GetIPAddressFromSockAddr(sa, sock_addr_len, &address, - &address_len, &port)) { - NOTREACHED(); - return std::string(); - } - return IPAddressToStringWithPort(address, address_len, port); -} - -std::string GetHostName() { -#if defined(OS_NACL) - NOTIMPLEMENTED(); - return std::string(); -#else // defined(OS_NACL) -#if defined(OS_WIN) - EnsureWinsockInit(); -#endif - - // Host names are limited to 255 bytes. - char buffer[256]; - int result = gethostname(buffer, sizeof(buffer)); - if (result != 0) { - DVLOG(1) << "gethostname() failed with " << result; - buffer[0] = '\0'; - } - return std::string(buffer); -#endif // !defined(OS_NACL) -} - -#if 0 -void GetIdentityFromURL(const GURL& url, - base::string16* username, - base::string16* password) { - UnescapeRule::Type flags = - UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS; - *username = UnescapeAndDecodeUTF8URLComponent(url.username(), flags); - *password = UnescapeAndDecodeUTF8URLComponent(url.password(), flags); -} -#endif - -#if 0 -std::string GetHostOrSpecFromURL(const GURL& url) { - return url.has_host() ? TrimEndingDot(url.host_piece()) : url.spec(); -} -#endif - -#if 0 -GURL SimplifyUrlForRequest(const GURL& url) { - DCHECK(url.is_valid()); - GURL::Replacements replacements; - replacements.ClearUsername(); - replacements.ClearPassword(); - replacements.ClearRef(); - return url.ReplaceComponents(replacements); -} -#endif - -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); - if (rv != 0) { - DVLOG(1) << "getifaddrs() failed with errno = " << errno; - return false; - } - - bool result = true; - for (struct ifaddrs* interface = interface_addr; - interface != NULL; - interface = interface->ifa_next) { - if (!(IFF_UP & interface->ifa_flags)) - continue; - if (IFF_LOOPBACK & interface->ifa_flags) - continue; - const struct sockaddr* addr = interface->ifa_addr; - if (!addr) - continue; - if (addr->sa_family == AF_INET6) { - // Safe cast since this is AF_INET6. - const struct sockaddr_in6* addr_in6 = - reinterpret_cast(addr); - const struct in6_addr* sin6_addr = &addr_in6->sin6_addr; - if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_LINKLOCAL(sin6_addr)) - continue; - } - if (addr->sa_family != AF_INET6 && addr->sa_family != AF_INET) - continue; - - result = false; - break; - } - freeifaddrs(interface_addr); - return result; -#elif defined(OS_WIN) - // TODO(wtc): implement with the GetAdaptersAddresses function. - NOTIMPLEMENTED(); - return false; -#else - NOTIMPLEMENTED(); - return false; -#endif // defined(various platforms) -} - -const uint16_t* GetPortFieldFromSockaddr(const struct sockaddr* address, - socklen_t address_len) { - if (address->sa_family == AF_INET) { - DCHECK_LE(sizeof(sockaddr_in), static_cast(address_len)); - const struct sockaddr_in* sockaddr = - reinterpret_cast(address); - return &sockaddr->sin_port; - } else if (address->sa_family == AF_INET6) { - DCHECK_LE(sizeof(sockaddr_in6), static_cast(address_len)); - const struct sockaddr_in6* sockaddr = - reinterpret_cast(address); - return &sockaddr->sin6_port; - } else { - NOTREACHED(); - return NULL; - } -} - -int GetPortFromSockaddr(const struct sockaddr* address, socklen_t address_len) { - const uint16_t* port_field = GetPortFieldFromSockaddr(address, address_len); - if (!port_field) - return -1; - return base::NetToHost16(*port_field); -} - -#if 0 -bool ResolveLocalHostname(base::StringPiece host, - uint16_t port, - AddressList* address_list) { - static const unsigned char kLocalhostIPv4[] = {127, 0, 0, 1}; - static const unsigned char kLocalhostIPv6[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; - - std::string normalized_host = NormalizeHostname(host); - - address_list->clear(); - - bool is_local6 = IsLocal6Hostname(normalized_host); - if (!is_local6 && !IsLocalHostname(normalized_host)) - return false; - - address_list->push_back( - IPEndPoint(IPAddressNumber(kLocalhostIPv6, - kLocalhostIPv6 + arraysize(kLocalhostIPv6)), - port)); - if (!is_local6) { - address_list->push_back( - IPEndPoint(IPAddressNumber(kLocalhostIPv4, - kLocalhostIPv4 + arraysize(kLocalhostIPv4)), - port)); - } - - return true; -} -#endif - -#if 0 -bool IsLocalhost(base::StringPiece host) { - std::string normalized_host = NormalizeHostname(host); - if (IsLocalHostname(normalized_host) || IsLocal6Hostname(normalized_host)) - return true; - - IPAddressNumber ip_number; - if (ParseIPLiteralToNumber(host, &ip_number)) { - size_t size = ip_number.size(); - switch (size) { - case kIPv4AddressSize: { - IPAddressNumber localhost_prefix; - localhost_prefix.push_back(127); - for (int i = 0; i < 3; ++i) { - localhost_prefix.push_back(0); - } - return IPNumberMatchesPrefix(ip_number, localhost_prefix, 8); - } - - case kIPv6AddressSize: { - struct in6_addr sin6_addr; - memcpy(&sin6_addr, &ip_number[0], kIPv6AddressSize); - return !!IN6_IS_ADDR_LOOPBACK(&sin6_addr); - } - - default: - NOTREACHED(); - } - } - - return false; -} -#endif - -bool HasGoogleHost(const GURL& url) { - static const char* kGoogleHostSuffixes[] = { - ".google.com", - ".youtube.com", - ".gmail.com", - ".doubleclick.net", - ".gstatic.com", - ".googlevideo.com", - ".googleusercontent.com", - ".googlesyndication.com", - ".google-analytics.com", - ".googleadservices.com", - ".googleapis.com", - ".ytimg.com", - }; - base::StringPiece host = url.host_piece(); - for (const char* suffix : kGoogleHostSuffixes) { - // Here it's possible to get away with faster case-sensitive comparisons - // because the list above is all lowercase, and a GURL's host name will - // always be canonicalized to lowercase as well. - if (base::EndsWith(host, suffix, base::CompareCase::SENSITIVE)) - return true; - } - return false; -} - -} // namespace net diff --git a/src/net/base/net_util.h b/src/net/base/net_util.h deleted file mode 100644 index ab7970c0..00000000 --- a/src/net/base/net_util.h +++ /dev/null @@ -1,218 +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_NET_UTIL_H_ -#define NET_BASE_NET_UTIL_H_ - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#include -#elif defined(OS_POSIX) -#include -#include -#endif - -#include -#include - -#include "base/strings/string16.h" -#include "net/base/net_export.h" - -class GURL; - -namespace base { -class Time; -} - -namespace url { -struct CanonHostInfo; -struct Parsed; -} - -namespace net { - -class AddressList; - -// This is a "forward declaration" to avoid including ip_address_number.h -// Keep this in sync. -typedef std::vector IPAddressNumber; - -#if defined(OS_WIN) -// Bluetooth address size. Windows Bluetooth is supported via winsock. -static const size_t kBluetoothAddressSize = 6; -#endif - -#if 0 -// Splits an input of the form [":"] into its consitituent parts. -// Saves the result into |*host| and |*port|. If the input did not have -// the optional port, sets |*port| to -1. -// Returns true if the parsing was successful, false otherwise. -// The returned host is NOT canonicalized, and may be invalid. -// -// IPv6 literals must be specified in a bracketed form, for instance: -// [::1]:90 and [::1] -// -// The resultant |*host| in both cases will be "::1" (not bracketed). -NET_EXPORT bool ParseHostAndPort( - std::string::const_iterator host_and_port_begin, - std::string::const_iterator host_and_port_end, - std::string* host, - int* port); -NET_EXPORT bool ParseHostAndPort( - const std::string& host_and_port, - std::string* host, - int* port); -#endif - -#if 0 -// Returns a host:port string for the given URL. -NET_EXPORT std::string GetHostAndPort(const GURL& url); - -// Returns a host[:port] string for the given URL, where the port is omitted -// if it is the default for the URL's scheme. -NET_EXPORT_PRIVATE std::string GetHostAndOptionalPort(const GURL& url); -#endif - -// Returns true if |hostname| contains a non-registerable or non-assignable -// domain name (eg: a gTLD that has not been assigned by IANA) or an IP address -// that falls in an IANA-reserved range. -NET_EXPORT bool IsHostnameNonUnique(const std::string& hostname); - -// Convenience struct for when you need a |struct sockaddr|. -struct SockaddrStorage { - SockaddrStorage() : addr_len(sizeof(addr_storage)), - addr(reinterpret_cast(&addr_storage)) {} - SockaddrStorage(const SockaddrStorage& other); - void operator=(const SockaddrStorage& other); - - struct sockaddr_storage addr_storage; - socklen_t addr_len; - struct sockaddr* const addr; -}; - -// Extracts the IP address and port portions of a sockaddr. |port| is optional, -// and will not be filled in if NULL. -bool GetIPAddressFromSockAddr(const struct sockaddr* sock_addr, - socklen_t sock_addr_len, - const unsigned char** address, - size_t* address_len, - uint16_t* port); - -// Same as IPAddressToString() but for a sockaddr. This output will not include -// the IPv6 scope ID. -NET_EXPORT std::string NetAddressToString(const struct sockaddr* sa, - socklen_t sock_addr_len); - -// Same as IPAddressToStringWithPort() but for a sockaddr. This output will not -// include the IPv6 scope ID. -NET_EXPORT std::string NetAddressToStringWithPort(const struct sockaddr* sa, - socklen_t sock_addr_len); - -// Returns the hostname of the current system. Returns empty string on failure. -NET_EXPORT std::string GetHostName(); - -// Extracts the unescaped username/password from |url|, saving the results -// into |*username| and |*password|. -NET_EXPORT_PRIVATE void GetIdentityFromURL(const GURL& url, - base::string16* username, - base::string16* password); - -// Returns either the host from |url|, or, if the host is empty, the full spec. -NET_EXPORT std::string GetHostOrSpecFromURL(const GURL& url); - -#if 0 -// 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::CanonHostInfo* host_info); -#endif - -// 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. -// The rules are: -// * One or more components separated by '.' -// * Each component contains only alphanumeric characters and '-' or '_' -// * The last component begins with an alphanumeric character -// * Optional trailing dot after last component (means "treat as FQDN") -// -// NOTE: You should only pass in hosts that have been returned from -// CanonicalizeHost(), or you may not get accurate results. -NET_EXPORT bool IsCanonicalizedHostCompliant(const std::string& host); - -#if 0 -// Call these functions to get the html snippet for a directory listing. -// The return values of both functions are in UTF-8. -NET_EXPORT std::string GetDirectoryListingHeader(const base::string16& title); -#endif - -// Given the name of a file in a directory (ftp or local) and -// other information (is_dir, size, modification time), it returns -// the html snippet to add the entry for the file to the directory listing. -// Currently, it's a script tag containing a call to a Javascript function -// |addRow|. -// -// |name| is the file name to be displayed. |raw_bytes| will be used -// as the actual target of the link (so for example, ftp links should use -// server's encoding). If |raw_bytes| is an empty string, UTF-8 encoded |name| -// will be used. -// -// Both |name| and |raw_bytes| are escaped internally. -NET_EXPORT std::string GetDirectoryListingEntry(const base::string16& name, - const std::string& raw_bytes, - bool is_dir, - int64_t size, - base::Time modified); - -// Set socket to non-blocking mode -NET_EXPORT int SetNonBlocking(int fd); - -#if 0 -// Strip the portions of |url| that aren't core to the network request. -// - user name / password -// - reference section -NET_EXPORT_PRIVATE GURL SimplifyUrlForRequest(const GURL& url); -#endif - -// Returns true if it can determine that only loopback addresses are configured. -// i.e. if only 127.0.0.1 and ::1 are routable. -// Also returns false if it cannot determine this. -bool HaveOnlyLoopbackAddresses(); - -// Retuns the port field of the |sockaddr|. -const uint16_t* GetPortFieldFromSockaddr(const struct sockaddr* address, - socklen_t address_len); -// Returns the value of port in |sockaddr| (in host byte ordering). -NET_EXPORT_PRIVATE int GetPortFromSockaddr(const struct sockaddr* address, - socklen_t address_len); - -#if 0 -// Resolves a local hostname (such as "localhost" or "localhost6") into -// IP endpoints with the given port. Returns true if |host| is a local -// hostname and false otherwise. Special IPv6 names (e.g. "localhost6") -// will resolve to an IPv6 address only, whereas other names will -// resolve to both IPv4 and IPv6. -NET_EXPORT_PRIVATE bool ResolveLocalHostname(base::StringPiece host, - uint16_t port, - AddressList* address_list); -#endif - -#if 0 -// Returns true if |host| is one of the local hostnames -// (e.g. "localhost") or IP addresses (IPv4 127.0.0.0/8 or IPv6 ::1). -// -// Note that this function does not check for IP addresses other than -// the above, although other IP addresses may point to the local -// machine. -NET_EXPORT_PRIVATE bool IsLocalhost(base::StringPiece host); -#endif - -// Returns true if the url's host is a Google server. This should only be used -// for histograms and shouldn't be used to affect behavior. -NET_EXPORT_PRIVATE bool HasGoogleHost(const GURL& url); - -} // namespace net - -#endif // NET_BASE_NET_UTIL_H_ diff --git a/src/net/base/port_util.cc b/src/net/base/port_util.cc index 1867dc36..7328b5e8 100644 --- a/src/net/base/port_util.cc +++ b/src/net/base/port_util.cc @@ -4,6 +4,7 @@ #include "net/base/port_util.h" +#include #include #include "base/lazy_instance.h" diff --git a/custom/net_util.cc b/src/net/base/url_util.cc similarity index 50% rename from custom/net_util.cc rename to src/net/base/url_util.cc index 146d32a9..4decf0cf 100644 --- a/custom/net_util.cc +++ b/src/net/base/url_util.cc @@ -1,183 +1,164 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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/base/net_util.h" - -#include - -#include -#include -#include +#include "net/base/url_util.h" #include "build/build_config.h" -#if defined(OS_WIN) -#include -#include -#include -#include -#pragma comment(lib, "iphlpapi.lib") -#elif defined(OS_POSIX) -#include -#include +#if defined(OS_POSIX) #include -#include -#if !defined(OS_NACL) -#include -#if !defined(OS_ANDROID) -#include -#endif // !defined(OS_NACL) -#endif // !defined(OS_ANDROID) -#endif // defined(OS_POSIX) - -#include "base/json/string_escape.h" +#elif defined(OS_WIN) +#include +#endif + #include "base/logging.h" -#include "base/strings/string_piece.h" -#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/sys_byteorder.h" -#include "base/values.h" +#include "net/base/escape.h" #include "net/base/ip_address_number.h" +#if 0 +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#endif #include "url/gurl.h" -#include "url/third_party/mozilla/url_parse.h" #include "url/url_canon.h" #include "url/url_canon_ip.h" -#if defined(OS_ANDROID) -#include "net/android/network_library.h" -#endif -#if defined(OS_WIN) -#include "net/base/winsock_init.h" -#endif - namespace net { namespace { -#if 0 -std::string NormalizeHostname(base::StringPiece host) { - std::string result = base::ToLowerASCII(host); - if (!result.empty() && *result.rbegin() == '.') - result.resize(result.size() - 1); - return result; +bool IsHostCharAlphanumeric(char c) { + // We can just check lowercase because uppercase characters have already been + // normalized. + return ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')); } -#endif -#if 0 bool IsNormalizedLocalhostTLD(const std::string& host) { return base::EndsWith(host, ".localhost", base::CompareCase::SENSITIVE); } -#endif -#if 0 -// |host| should be normalized. -bool IsLocalHostname(const std::string& host) { - return host == "localhost" || host == "localhost.localdomain" || - IsNormalizedLocalhostTLD(host); -} -#endif +} // namespace -#if 0 -// |host| should be normalized. -bool IsLocal6Hostname(const std::string& host) { - return host == "localhost6" || host == "localhost6.localdomain6"; -} -#endif +GURL AppendQueryParameter(const GURL& url, + const std::string& name, + const std::string& value) { + std::string query(url.query()); -} // namespace + if (!query.empty()) + query += "&"; -#if 0 -std::string CanonicalizeHost(const std::string& host, - url::CanonHostInfo* host_info) { - // Try to canonicalize the host. - const url::Component raw_host_component(0, static_cast(host.length())); - std::string canon_host; - url::StdStringCanonOutput canon_host_output(&canon_host); - url::CanonicalizeHostVerbose(host.c_str(), raw_host_component, - &canon_host_output, host_info); + query += (EscapeQueryParamValue(name, true) + "=" + + EscapeQueryParamValue(value, true)); + GURL::Replacements replacements; + replacements.SetQueryStr(query); + return url.ReplaceComponents(replacements); +} - if (host_info->out_host.is_nonempty() && - 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(canon_host.length())); - } else { - // Empty host, or canonicalization failed. We'll return empty. - canon_host.clear(); +GURL AppendOrReplaceQueryParameter(const GURL& url, + const std::string& name, + const std::string& value) { + bool replaced = false; + std::string param_name = EscapeQueryParamValue(name, true); + std::string param_value = EscapeQueryParamValue(value, true); + + const std::string input = url.query(); + url::Component cursor(0, input.size()); + std::string output; + 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); + std::string key_value_pair; + // Check |replaced| as only the first pair should be replaced. + if (!replaced && key == param_name) { + replaced = true; + key_value_pair = (param_name + "=" + param_value); + } else { + key_value_pair.assign(input.data(), + key_range.begin, + value_range.end() - key_range.begin); + } + if (!output.empty()) + output += "&"; + + output += key_value_pair; } + if (!replaced) { + if (!output.empty()) + output += "&"; - return canon_host; + output += (param_name + "=" + param_value); + } + GURL::Replacements replacements; + replacements.SetQueryStr(output); + return url.ReplaceComponents(replacements); } -#endif -#if 0 -std::string GetDirectoryListingHeader(const base::string16& title) { - static const base::StringPiece header( - NetModule::GetResource(IDR_DIR_HEADER_HTML)); - // This can be null in unit tests. - DLOG_IF(WARNING, header.empty()) << - "Missing resource: directory listing header"; - - std::string result; - if (!header.empty()) - result.assign(header.data(), header.size()); - - result.append("\n"); - - return result; +QueryIterator::QueryIterator(const GURL& url) + : url_(url), + at_end_(!url.is_valid()) { + if (!at_end_) { + query_ = url.parsed_for_possibly_invalid_spec().query; + Advance(); + } } -#endif -inline bool IsHostCharAlphanumeric(char c) { - // We can just check lowercase because uppercase characters have already been - // normalized. - return ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')); +QueryIterator::~QueryIterator() { } -bool IsCanonicalizedHostCompliant(const std::string& host) { - if (host.empty()) - return false; +std::string QueryIterator::GetKey() const { + DCHECK(!at_end_); + if (key_.is_nonempty()) + return url_.spec().substr(key_.begin, key_.len); + return std::string(); +} - bool in_component = false; - bool most_recent_component_started_alphanumeric = false; +std::string QueryIterator::GetValue() const { + DCHECK(!at_end_); + if (value_.is_nonempty()) + return url_.spec().substr(value_.begin, value_.len); + return std::string(); +} - for (std::string::const_iterator i(host.begin()); i != host.end(); ++i) { - const char c = *i; - if (!in_component) { - most_recent_component_started_alphanumeric = IsHostCharAlphanumeric(c); - if (!most_recent_component_started_alphanumeric && (c != '-') && - (c != '_')) { - return false; - } - in_component = true; - } else if (c == '.') { - in_component = false; - } else if (!IsHostCharAlphanumeric(c) && (c != '-') && (c != '_')) { - return false; - } +const std::string& QueryIterator::GetUnescapedValue() { + DCHECK(!at_end_); + if (value_.is_nonempty() && unescaped_value_.empty()) { + unescaped_value_ = UnescapeURLComponent( + GetValue(), + UnescapeRule::SPACES | + UnescapeRule::URL_SPECIAL_CHARS | + UnescapeRule::REPLACE_PLUS_WITH_SPACE); } + return unescaped_value_; +} - return most_recent_component_started_alphanumeric; +bool QueryIterator::IsAtEnd() const { + return at_end_; } -int SetNonBlocking(int fd) { -#if defined(OS_WIN) - unsigned long no_block = 1; - return ioctlsocket(fd, FIONBIO, &no_block); -#elif defined(OS_POSIX) - int flags = fcntl(fd, F_GETFL, 0); - if (-1 == flags) - return flags; - return fcntl(fd, F_SETFL, flags | O_NONBLOCK); -#endif +void QueryIterator::Advance() { + DCHECK (!at_end_); + key_.reset(); + value_.reset(); + unescaped_value_.clear(); + at_end_ = + !url::ExtractQueryKeyValue(url_.spec().c_str(), &query_, &key_, &value_); +} + +bool GetValueForKeyInQuery(const GURL& url, + const std::string& search_key, + std::string* out_value) { + for (QueryIterator it(url); !it.IsAtEnd(); it.Advance()) { + if (it.GetKey() == search_key) { + *out_value = it.GetUnescapedValue(); + return true; + } + } + return false; } -#if 0 bool ParseHostAndPort(std::string::const_iterator host_and_port_begin, std::string::const_iterator host_and_port_end, std::string* host, @@ -248,9 +229,8 @@ bool ParseHostAndPort(const std::string& host_and_port, return ParseHostAndPort( host_and_port.begin(), host_and_port.end(), host, port); } -#endif -#if 0 + std::string GetHostAndPort(const GURL& url) { // For IPv6 literals, GURL::host() already includes the brackets so it is // safe to just append a colon. @@ -265,7 +245,67 @@ std::string GetHostAndOptionalPort(const GURL& url) { return base::StringPrintf("%s:%s", url.host().c_str(), url.port().c_str()); return url.host(); } -#endif + +std::string TrimEndingDot(base::StringPiece host) { + base::StringPiece host_trimmed = host; + size_t len = host_trimmed.length(); + if (len > 1 && host_trimmed[len - 1] == '.') { + host_trimmed.remove_suffix(1); + } + return host_trimmed.as_string(); +} + +std::string GetHostOrSpecFromURL(const GURL& url) { + return url.has_host() ? TrimEndingDot(url.host_piece()) : url.spec(); +} + +std::string CanonicalizeHost(base::StringPiece host, + url::CanonHostInfo* host_info) { + // Try to canonicalize the host. + const url::Component raw_host_component(0, static_cast(host.length())); + std::string canon_host; + url::StdStringCanonOutput canon_host_output(&canon_host); + url::CanonicalizeHostVerbose(host.data(), raw_host_component, + &canon_host_output, host_info); + + if (host_info->out_host.is_nonempty() && + 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(canon_host.length())); + } else { + // Empty host, or canonicalization failed. We'll return empty. + canon_host.clear(); + } + + return canon_host; +} + +bool IsCanonicalizedHostCompliant(const std::string& host) { + if (host.empty()) + return false; + + bool in_component = false; + bool most_recent_component_started_alphanumeric = false; + + for (std::string::const_iterator i(host.begin()); i != host.end(); ++i) { + const char c = *i; + if (!in_component) { + most_recent_component_started_alphanumeric = IsHostCharAlphanumeric(c); + if (!most_recent_component_started_alphanumeric && (c != '-') && + (c != '_')) { + return false; + } + in_component = true; + } else if (c == '.') { + in_component = false; + } else if (!IsHostCharAlphanumeric(c) && (c != '-') && (c != '_')) { + return false; + } + } + + return most_recent_component_started_alphanumeric; +} #if 0 bool IsHostnameNonUnique(const std::string& hostname) { @@ -315,249 +355,8 @@ bool IsHostnameNonUnique(const std::string& hostname) { } #endif -SockaddrStorage::SockaddrStorage(const SockaddrStorage& other) - : addr_len(other.addr_len), - addr(reinterpret_cast(&addr_storage)) { - memcpy(addr, other.addr, addr_len); -} - -void SockaddrStorage::operator=(const SockaddrStorage& other) { - addr_len = other.addr_len; - // addr is already set to &this->addr_storage by default ctor. - memcpy(addr, other.addr, addr_len); -} - -// Extracts the address and port portions of a sockaddr. -bool GetIPAddressFromSockAddr(const struct sockaddr* sock_addr, - socklen_t sock_addr_len, - const uint8_t** address, - size_t* address_len, - uint16_t* port) { - if (sock_addr->sa_family == AF_INET) { - if (sock_addr_len < static_cast(sizeof(struct sockaddr_in))) - return false; - const struct sockaddr_in* addr = - reinterpret_cast(sock_addr); - *address = reinterpret_cast(&addr->sin_addr); - *address_len = kIPv4AddressSize; - if (port) - *port = base::NetToHost16(addr->sin_port); - return true; - } - - if (sock_addr->sa_family == AF_INET6) { - if (sock_addr_len < static_cast(sizeof(struct sockaddr_in6))) - return false; - const struct sockaddr_in6* addr = - reinterpret_cast(sock_addr); - *address = reinterpret_cast(&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(sizeof(SOCKADDR_BTH))) - return false; - const SOCKADDR_BTH* addr = - reinterpret_cast(sock_addr); - *address = reinterpret_cast(&addr->btAddr); - *address_len = kBluetoothAddressSize; - if (port) - *port = static_cast(addr->port); - return true; - } -#endif - - return false; // Unrecognized |sa_family|. -} - -std::string NetAddressToString(const struct sockaddr* sa, - socklen_t sock_addr_len) { - const uint8_t* address; - size_t address_len; - if (!GetIPAddressFromSockAddr(sa, sock_addr_len, &address, - &address_len, NULL)) { - NOTREACHED(); - return std::string(); - } - return IPAddressToString(address, address_len); -} - -std::string NetAddressToStringWithPort(const struct sockaddr* sa, - socklen_t sock_addr_len) { - const uint8_t* address; - size_t address_len; - uint16_t port; - if (!GetIPAddressFromSockAddr(sa, sock_addr_len, &address, - &address_len, &port)) { - NOTREACHED(); - return std::string(); - } - return IPAddressToStringWithPort(address, address_len, port); -} - -std::string GetHostName() { -#if defined(OS_NACL) - NOTIMPLEMENTED(); - return std::string(); -#else // defined(OS_NACL) -#if defined(OS_WIN) - EnsureWinsockInit(); -#endif - - // Host names are limited to 255 bytes. - char buffer[256]; - int result = gethostname(buffer, sizeof(buffer)); - if (result != 0) { - DVLOG(1) << "gethostname() failed with " << result; - buffer[0] = '\0'; - } - return std::string(buffer); -#endif // !defined(OS_NACL) -} - -#if 0 -void GetIdentityFromURL(const GURL& url, - base::string16* username, - base::string16* password) { - UnescapeRule::Type flags = - UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS; - *username = UnescapeAndDecodeUTF8URLComponent(url.username(), flags); - *password = UnescapeAndDecodeUTF8URLComponent(url.password(), flags); -} -#endif - -#if 0 -std::string GetHostOrSpecFromURL(const GURL& url) { - return url.has_host() ? TrimEndingDot(url.host_piece()) : url.spec(); -} -#endif - -#if 0 -GURL SimplifyUrlForRequest(const GURL& url) { - DCHECK(url.is_valid()); - GURL::Replacements replacements; - replacements.ClearUsername(); - replacements.ClearPassword(); - replacements.ClearRef(); - return url.ReplaceComponents(replacements); -} -#endif - -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); - if (rv != 0) { - DVLOG(1) << "getifaddrs() failed with errno = " << errno; - return false; - } - - bool result = true; - for (struct ifaddrs* interface = interface_addr; - interface != NULL; - interface = interface->ifa_next) { - if (!(IFF_UP & interface->ifa_flags)) - continue; - if (IFF_LOOPBACK & interface->ifa_flags) - continue; - const struct sockaddr* addr = interface->ifa_addr; - if (!addr) - continue; - if (addr->sa_family == AF_INET6) { - // Safe cast since this is AF_INET6. - const struct sockaddr_in6* addr_in6 = - reinterpret_cast(addr); - const struct in6_addr* sin6_addr = &addr_in6->sin6_addr; - if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_LINKLOCAL(sin6_addr)) - continue; - } - if (addr->sa_family != AF_INET6 && addr->sa_family != AF_INET) - continue; - - result = false; - break; - } - freeifaddrs(interface_addr); - return result; -#elif defined(OS_WIN) - // TODO(wtc): implement with the GetAdaptersAddresses function. - NOTIMPLEMENTED(); - return false; -#else - NOTIMPLEMENTED(); - return false; -#endif // defined(various platforms) -} - -const uint16_t* GetPortFieldFromSockaddr(const struct sockaddr* address, - socklen_t address_len) { - if (address->sa_family == AF_INET) { - DCHECK_LE(sizeof(sockaddr_in), static_cast(address_len)); - const struct sockaddr_in* sockaddr = - reinterpret_cast(address); - return &sockaddr->sin_port; - } else if (address->sa_family == AF_INET6) { - DCHECK_LE(sizeof(sockaddr_in6), static_cast(address_len)); - const struct sockaddr_in6* sockaddr = - reinterpret_cast(address); - return &sockaddr->sin6_port; - } else { - NOTREACHED(); - return NULL; - } -} - -int GetPortFromSockaddr(const struct sockaddr* address, socklen_t address_len) { - const uint16_t* port_field = GetPortFieldFromSockaddr(address, address_len); - if (!port_field) - return -1; - return base::NetToHost16(*port_field); -} - -#if 0 -bool ResolveLocalHostname(base::StringPiece host, - uint16_t port, - AddressList* address_list) { - static const unsigned char kLocalhostIPv4[] = {127, 0, 0, 1}; - static const unsigned char kLocalhostIPv6[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; - - std::string normalized_host = NormalizeHostname(host); - - address_list->clear(); - - bool is_local6 = IsLocal6Hostname(normalized_host); - if (!is_local6 && !IsLocalHostname(normalized_host)) - return false; - - address_list->push_back( - IPEndPoint(IPAddressNumber(kLocalhostIPv6, - kLocalhostIPv6 + arraysize(kLocalhostIPv6)), - port)); - if (!is_local6) { - address_list->push_back( - IPEndPoint(IPAddressNumber(kLocalhostIPv4, - kLocalhostIPv4 + arraysize(kLocalhostIPv4)), - port)); - } - - return true; -} -#endif - -#if 0 bool IsLocalhost(base::StringPiece host) { - std::string normalized_host = NormalizeHostname(host); - if (IsLocalHostname(normalized_host) || IsLocal6Hostname(normalized_host)) + if (IsLocalHostname(host, nullptr)) return true; IPAddressNumber ip_number; @@ -586,7 +385,24 @@ bool IsLocalhost(base::StringPiece host) { return false; } -#endif + +GURL SimplifyUrlForRequest(const GURL& url) { + DCHECK(url.is_valid()); + GURL::Replacements replacements; + replacements.ClearUsername(); + replacements.ClearPassword(); + replacements.ClearRef(); + return url.ReplaceComponents(replacements); +} + +void GetIdentityFromURL(const GURL& url, + base::string16* username, + base::string16* password) { + UnescapeRule::Type flags = + UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS; + *username = UnescapeAndDecodeUTF8URLComponent(url.username(), flags); + *password = UnescapeAndDecodeUTF8URLComponent(url.password(), flags); +} bool HasGoogleHost(const GURL& url) { static const char* kGoogleHostSuffixes[] = { @@ -614,4 +430,24 @@ bool HasGoogleHost(const GURL& url) { return false; } +bool IsLocalHostname(base::StringPiece host, bool* is_local6) { + std::string normalized_host = base::ToLowerASCII(host); + // Remove any trailing '.'. + if (!normalized_host.empty() && *normalized_host.rbegin() == '.') + normalized_host.resize(normalized_host.size() - 1); + + if (normalized_host == "localhost6" || + normalized_host == "localhost6.localdomain6") { + if (is_local6) + *is_local6 = true; + return true; + } + + if (is_local6) + *is_local6 = false; + return normalized_host == "localhost" || + normalized_host == "localhost.localdomain" || + IsNormalizedLocalhostTLD(normalized_host); +} + } // namespace net diff --git a/src/net/base/url_util.h b/src/net/base/url_util.h new file mode 100644 index 00000000..75f7b620 --- /dev/null +++ b/src/net/base/url_util.h @@ -0,0 +1,177 @@ +// 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. + +// This file contains a set of utility functions related to parsing, +// manipulating, and interacting with URLs and hostnames. These functions are +// intended to be of a text-processing nature, and should not attempt to use any +// networking or blocking services. + +#ifndef NET_BASE_URL_UTIL_H_ +#define NET_BASE_URL_UTIL_H_ + +#include + +#include "base/macros.h" +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "net/base/net_export.h" +#include "url/third_party/mozilla/url_parse.h" + +class GURL; + +namespace url { +struct CanonHostInfo; +} + +namespace net { + +// Returns a new GURL by appending the given query parameter name and the +// value. Unsafe characters in the name and the value are escaped like +// %XX%XX. The original query component is preserved if it's present. +// +// Examples: +// +// AppendQueryParameter(GURL("http://example.com"), "name", "value").spec() +// => "http://example.com?name=value" +// AppendQueryParameter(GURL("http://example.com?x=y"), "name", "value").spec() +// => "http://example.com?x=y&name=value" +NET_EXPORT GURL AppendQueryParameter(const GURL& url, + const std::string& name, + const std::string& value); + +// Returns a new GURL by appending or replacing the given query parameter name +// and the value. If |name| appears more than once, only the first name-value +// pair is replaced. Unsafe characters in the name and the value are escaped +// like %XX%XX. The original query component is preserved if it's present. +// +// Examples: +// +// AppendOrReplaceQueryParameter( +// GURL("http://example.com"), "name", "new").spec() +// => "http://example.com?name=value" +// AppendOrReplaceQueryParameter( +// GURL("http://example.com?x=y&name=old"), "name", "new").spec() +// => "http://example.com?x=y&name=new" +NET_EXPORT GURL AppendOrReplaceQueryParameter(const GURL& url, + const std::string& name, + const std::string& value); + +// Iterates over the key-value pairs in the query portion of |url|. +class NET_EXPORT QueryIterator { + public: + explicit QueryIterator(const GURL& url); + ~QueryIterator(); + + std::string GetKey() const; + std::string GetValue() const; + const std::string& GetUnescapedValue(); + + bool IsAtEnd() const; + void Advance(); + + private: + const GURL& url_; + url::Component query_; + bool at_end_; + url::Component key_; + url::Component value_; + std::string unescaped_value_; + + DISALLOW_COPY_AND_ASSIGN(QueryIterator); +}; + +// Looks for |search_key| in the query portion of |url|. Returns true if the +// key is found and sets |out_value| to the unescaped value for the key. +// Returns false if the key is not found. +NET_EXPORT bool GetValueForKeyInQuery(const GURL& url, + const std::string& search_key, + std::string* out_value); + +// Splits an input of the form [":"] into its consitituent parts. +// Saves the result into |*host| and |*port|. If the input did not have +// the optional port, sets |*port| to -1. +// Returns true if the parsing was successful, false otherwise. +// The returned host is NOT canonicalized, and may be invalid. +// +// IPv6 literals must be specified in a bracketed form, for instance: +// [::1]:90 and [::1] +// +// The resultant |*host| in both cases will be "::1" (not bracketed). +NET_EXPORT bool ParseHostAndPort( + std::string::const_iterator host_and_port_begin, + std::string::const_iterator host_and_port_end, + std::string* host, + int* port); +NET_EXPORT bool ParseHostAndPort(const std::string& host_and_port, + std::string* host, + int* port); + +// Returns a host:port string for the given URL. +NET_EXPORT std::string GetHostAndPort(const GURL& url); + +// Returns a host[:port] string for the given URL, where the port is omitted +// if it is the default for the URL's scheme. +NET_EXPORT std::string GetHostAndOptionalPort(const GURL& url); + +// Returns the hostname by trimming the ending dot, if one exists. +NET_EXPORT std::string TrimEndingDot(base::StringPiece host); + +// Returns either the host from |url|, or, if the host is empty, the full spec. +NET_EXPORT std::string GetHostOrSpecFromURL(const GURL& url); + +// Canonicalizes |host| and returns it. Also fills |host_info| with +// IP address information. |host_info| must not be NULL. +NET_EXPORT std::string CanonicalizeHost(base::StringPiece host, + 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. +// The rules are: +// * One or more components separated by '.' +// * Each component contains only alphanumeric characters and '-' or '_' +// * The last component begins with an alphanumeric character +// * Optional trailing dot after last component (means "treat as FQDN") +// +// NOTE: You should only pass in hosts that have been returned from +// CanonicalizeHost(), or you may not get accurate results. +NET_EXPORT bool IsCanonicalizedHostCompliant(const std::string& host); + +// Returns true if |hostname| contains a non-registerable or non-assignable +// domain name (eg: a gTLD that has not been assigned by IANA) or an IP address +// that falls in an IANA-reserved range. +NET_EXPORT bool IsHostnameNonUnique(const std::string& hostname); + +// Returns true if |host| is one of the local hostnames +// (e.g. "localhost") or IP addresses (IPv4 127.0.0.0/8 or IPv6 ::1). +// +// Note that this function does not check for IP addresses other than +// the above, although other IP addresses may point to the local +// machine. +NET_EXPORT bool IsLocalhost(base::StringPiece host); + +// Strip the portions of |url| that aren't core to the network request. +// - user name / password +// - reference section +NET_EXPORT GURL SimplifyUrlForRequest(const GURL& url); + +// Extracts the unescaped username/password from |url|, saving the results +// into |*username| and |*password|. +NET_EXPORT_PRIVATE void GetIdentityFromURL(const GURL& url, + base::string16* username, + base::string16* password); + +// Returns true if the url's host is a Google server. This should only be used +// for histograms and shouldn't be used to affect behavior. +NET_EXPORT_PRIVATE bool HasGoogleHost(const GURL& url); + +// This function tests |host| to see if it is of any local hostname form. +// |host| is normalized before being tested and if |is_local6| is not NULL then +// it it will be set to true if the localhost name implies an IPv6 interface ( +// for instance localhost6.localdomain6). +NET_EXPORT_PRIVATE bool IsLocalHostname(base::StringPiece host, + bool* is_local6); + +} // namespace net + +#endif // NET_BASE_URL_UTIL_H_ diff --git a/src/net/base/winsock_util.cc b/src/net/base/winsock_util.cc new file mode 100644 index 00000000..5522e271 --- /dev/null +++ b/src/net/base/winsock_util.cc @@ -0,0 +1,52 @@ +// 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/winsock_util.h" + +#include "base/logging.h" +#include "net/base/net_errors.h" + +namespace net { + +namespace { + +// Prevent the compiler from optimizing away the arguments so they appear +// nicely on the stack in crash dumps. +#pragma warning(push) +#pragma warning (disable: 4748) +#pragma optimize( "", off ) + +// Pass the important values as function arguments so that they are available +// in crash dumps. +void CheckEventWait(WSAEVENT hEvent, DWORD wait_rv, DWORD expected) { + if (wait_rv != expected) { + DWORD err = ERROR_SUCCESS; + if (wait_rv == WAIT_FAILED) + err = GetLastError(); + CHECK(false); // Crash. + } +} + +#pragma optimize( "", on ) +#pragma warning(pop) + +} // namespace + +void AssertEventNotSignaled(WSAEVENT hEvent) { + DWORD wait_rv = WaitForSingleObject(hEvent, 0); + CheckEventWait(hEvent, wait_rv, WAIT_TIMEOUT); +} + +bool ResetEventIfSignaled(WSAEVENT hEvent) { + // TODO(wtc): Remove the CHECKs after enough testing. + DWORD wait_rv = WaitForSingleObject(hEvent, 0); + if (wait_rv == WAIT_TIMEOUT) + return false; // The event object is not signaled. + CheckEventWait(hEvent, wait_rv, WAIT_OBJECT_0); + BOOL ok = WSAResetEvent(hEvent); + CHECK(ok); + return true; +} + +} // namespace net diff --git a/src/net/base/winsock_util.h b/src/net/base/winsock_util.h new file mode 100644 index 00000000..80edc3fa --- /dev/null +++ b/src/net/base/winsock_util.h @@ -0,0 +1,33 @@ +// 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_WINSOCK_UTIL_H_ +#define NET_BASE_WINSOCK_UTIL_H_ + +#include +#include + +#include "net/base/net_export.h" + +namespace net { + +// Bluetooth address size. Windows Bluetooth is supported via winsock. +static const size_t kBluetoothAddressSize = 6; + +// Assert that the (manual-reset) event object is not signaled. +void AssertEventNotSignaled(WSAEVENT hEvent); + +// If the (manual-reset) event object is signaled, resets it and returns true. +// Otherwise, does nothing and returns false. Called after a Winsock function +// succeeds synchronously +// +// Our testing shows that except in rare cases (when running inside QEMU), +// the event object is already signaled at this point, so we call this method +// to avoid a context switch in common cases. This is just a performance +// optimization. The code still works if this function simply returns false. +bool ResetEventIfSignaled(WSAEVENT hEvent); + +} // namespace net + +#endif // NET_BASE_WINSOCK_UTIL_H_ diff --git a/src/net/http/http_util.cc b/src/net/http/http_util.cc index 08382d9a..932f8386 100644 --- a/src/net/http/http_util.cc +++ b/src/net/http/http_util.cc @@ -16,6 +16,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" +#include "net/base/url_util.h" namespace net { @@ -40,6 +41,14 @@ static size_t FindStringEnd(const std::string& line, size_t start, char delim) { // HttpUtil ------------------------------------------------------------------- +// static +std::string HttpUtil::SpecForRequest(const GURL& url) { + // We may get ftp scheme when fetching ftp resources through proxy. + DCHECK(url.is_valid() && (url.SchemeIsHTTPOrHTTPS() || url.SchemeIs("ftp") || + url.SchemeIsWSOrWSS())); + return SimplifyUrlForRequest(url).spec(); +} + // static void HttpUtil::ParseContentType(const std::string& content_type_str, std::string* mime_type, @@ -729,6 +738,9 @@ bool HttpUtil::HasStrongValidators(HttpVersion version, const std::string& etag_header, const std::string& last_modified_header, const std::string& date_header) { + if (!HasValidators(version, etag_header, last_modified_header)) + return false; + if (version < HttpVersion(1, 1)) return false; @@ -752,9 +764,27 @@ bool HttpUtil::HasStrongValidators(HttpVersion version, if (!base::Time::FromString(date_header.c_str(), &date)) return false; + // Last-Modified is implicitly weak unless it is at least 60 seconds before + // the Date value. return ((date - last_modified).InSeconds() >= 60); } +bool HttpUtil::HasValidators(HttpVersion version, + const std::string& etag_header, + const std::string& last_modified_header) { + if (version < HttpVersion(1, 0)) + return false; + + base::Time last_modified; + if (base::Time::FromString(last_modified_header.c_str(), &last_modified)) + return true; + + // It is OK to consider an empty string in etag_header to be a missing header + // since valid ETags are always quoted-strings (see RFC 2616 3.11) and thus + // empty ETags aren't empty strings (i.e., an empty ETag might be "\"\""). + return version >= HttpVersion(1, 1) && !etag_header.empty(); +} + // Functions for histogram initialization. The code 0 is put in the map to // track status codes that are invalid. // TODO(gavinp): Greatly prune the collected codes once we learn which @@ -858,6 +888,8 @@ HttpUtil::ValuesIterator::ValuesIterator( values_.set_quote_chars("\'\""); } +HttpUtil::ValuesIterator::ValuesIterator(const ValuesIterator& other) = default; + HttpUtil::ValuesIterator::~ValuesIterator() { } @@ -894,6 +926,9 @@ HttpUtil::NameValuePairsIterator::NameValuePairsIterator( char delimiter) : NameValuePairsIterator(begin, end, delimiter, VALUES_NOT_OPTIONAL) {} +HttpUtil::NameValuePairsIterator::NameValuePairsIterator( + const NameValuePairsIterator& other) = default; + HttpUtil::NameValuePairsIterator::~NameValuePairsIterator() {} // We expect properties to be formatted as one of: diff --git a/src/net/http/http_util.h b/src/net/http/http_util.h index 13209b8f..458a7187 100644 --- a/src/net/http/http_util.h +++ b/src/net/http/http_util.h @@ -205,11 +205,20 @@ class NET_EXPORT HttpUtil { // Returns true if the parameters describe a response with a strong etag or // last-modified header. See section 13.3.3 of RFC 2616. + // An empty string should be passed for missing headers. static bool HasStrongValidators(HttpVersion version, const std::string& etag_header, const std::string& last_modified_header, const std::string& date_header); + // Returns true if this response has any validator (either a Last-Modified or + // an ETag) regardless of whether it is strong or weak. See section 13.3.3 of + // RFC 2616. + // An empty string should be passed for missing headers. + static bool HasValidators(HttpVersion version, + const std::string& etag_header, + const std::string& last_modified_header); + // Gets a vector of common HTTP status codes for histograms of status // codes. Currently returns everything in the range [100, 600), plus 0 // (for invalid responses/status codes). @@ -291,6 +300,7 @@ class NET_EXPORT HttpUtil { ValuesIterator(std::string::const_iterator values_begin, std::string::const_iterator values_end, char delimiter); + ValuesIterator(const ValuesIterator& other); ~ValuesIterator(); // Advances the iterator to the next value, if any. Returns true if there @@ -338,6 +348,8 @@ class NET_EXPORT HttpUtil { std::string::const_iterator end, char delimiter); + NameValuePairsIterator(const NameValuePairsIterator& other); + ~NameValuePairsIterator(); // Advances the iterator to the next pair, if any. Returns true if there diff --git a/src/net/log/net_log.h b/src/net/log/net_log.h index 87141079..01b49b2a 100644 --- a/src/net/log/net_log.h +++ b/src/net/log/net_log.h @@ -219,9 +219,6 @@ class NET_EXPORT NetLog { // Adds an observer and sets its log capture mode. The observer must not be // watching any NetLog, including this one, when this is called. // - // NetLog implementations must call NetLog::OnAddObserver to update the - // observer's internal state. - // // DEPRECATED: The ability to watch the netlog stream is being phased out // (crbug.com/472693) as it can be misused in production code. Please consult // with a net/log OWNER before introducing a new dependency on this. @@ -234,8 +231,7 @@ class NET_EXPORT NetLog { void SetObserverCaptureMode(ThreadSafeObserver* observer, NetLogCaptureMode capture_mode); - // Removes an observer. NetLog implementations must call - // NetLog::OnAddObserver to update the observer's internal state. + // Removes an observer. // // For thread safety reasons, it is recommended that this not be called in // an object's destructor. diff --git a/src/net/log/net_log_event_type_list.h b/src/net/log/net_log_event_type_list.h index 612d08b5..5817b5b6 100644 --- a/src/net/log/net_log_event_type_list.h +++ b/src/net/log/net_log_event_type_list.h @@ -476,12 +476,31 @@ EVENT_TYPE(SSL_PRIVATE_KEY_OPERATION) // { // "net_error": , // } +// TODO(nharper): remove this event. EVENT_TYPE(SSL_GET_DOMAIN_BOUND_CERT) +// The start/end of getting a Channel ID key. +// +// The START event contains these parameters: +// { +// "ephemeral": , +// "service": , +// "store": , +// } +// +// The END event may contain these parameters: +// { +// "net_error": , +// "key": , +// } +EVENT_TYPE(SSL_GET_CHANNEL_ID) + // The SSL server requested a channel id. +// TODO(nharper): Remove this event. EVENT_TYPE(SSL_CHANNEL_ID_REQUESTED) // A channel ID was provided to the SSL library to be sent to the SSL server. +// TODO(nharper): Remove this event. EVENT_TYPE(SSL_CHANNEL_ID_PROVIDED) // A client certificate (or none) was provided to the SSL library to be sent @@ -614,6 +633,18 @@ EVENT_TYPE(SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED) // } EVENT_TYPE(SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED) +// The certificate was checked for compliance with Certificate Transparency +// requirements. +// +// The following parameters are attached to the event: +// { +// "certificate": +// "build_timely": +// "ct_compliance_status": +// } +EVENT_TYPE(CERT_CT_COMPLIANCE_CHECKED) + // The EV certificate was checked for compliance with Certificate Transparency // requirements. // @@ -1029,6 +1060,12 @@ EVENT_TYPE(HTTP_STREAM_REQUEST_PROTO) // Job. The orphaned Job will continue to run to completion. EVENT_TYPE(HTTP_STREAM_JOB_ORPHANED) +// Emitted when a job is asked to resume after non-zero microseconds. +// { +// "resume_after_ms": +// } +EVENT_TYPE(HTTP_STREAM_JOB_DELAYED) + // ------------------------------------------------------------------------ // HttpNetworkTransaction // ------------------------------------------------------------------------ @@ -1109,6 +1146,9 @@ EVENT_TYPE(HTTP_TRANSACTION_READ_BODY) // restarting for authentication, on keep alive connections. EVENT_TYPE(HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART) +// Measures the time taken to look up the key used for Token Binding. +EVENT_TYPE(HTTP_TRANSACTION_GET_TOKEN_BINDING_KEY) + // This event is sent when we try to restart a transaction after an error. // The following parameters are attached: // { @@ -1163,15 +1203,16 @@ EVENT_TYPE(HTTP2_SESSION_SYN_STREAM) // } EVENT_TYPE(HTTP2_SESSION_PUSHED_SYN_STREAM) -// This event is sent for sending an HTTP/2 (or SPDY) HEADERS frame. +// This event is sent for sending an HTTP/2 HEADERS frame. // The following parameters are attached: // { -// "flags": , // "headers": , // "fin": , -// "unidirectional": , -// "priority": , // "stream_id": , +// "has_priority": , +// "parent_stream_id": , +// "priority": , +// "exclusive": . // } EVENT_TYPE(HTTP2_SESSION_SEND_HEADERS) @@ -1326,10 +1367,6 @@ EVENT_TYPE(HTTP2_SESSION_CLOSE) // the maximum number of concurrent streams. EVENT_TYPE(HTTP2_SESSION_STALLED_MAX_STREAMS) -// Received a value for initial window size in SETTINGS frame with -// flow control turned off. -EVENT_TYPE(HTTP2_SESSION_INITIAL_WINDOW_SIZE_NO_FLOW_CONTROL) - // Received an out-of-range value for initial window size in SETTINGS // frame. // { @@ -2426,14 +2463,12 @@ EVENT_TYPE(SIMPLE_CACHE_ENTRY_CREATE_BEGIN) // } EVENT_TYPE(SIMPLE_CACHE_ENTRY_CREATE_END) -// This event is created when ReadEntry is called. +// This event is created when ReadData is called. // It contains the following parameters: // { // "index": , // "offset": , // "buf_len": , -// "truncate": , // } EVENT_TYPE(SIMPLE_CACHE_ENTRY_READ_CALL) @@ -2444,12 +2479,10 @@ EVENT_TYPE(SIMPLE_CACHE_ENTRY_READ_CALL) // "index": , // "offset": , // "buf_len": , -// "truncate": , // } EVENT_TYPE(SIMPLE_CACHE_ENTRY_READ_BEGIN) -// This event is created when the Simple Cache finishes a ReadEntry call. +// This event is created when the Simple Cache finishes a ReadData call. // It contains the following parameters: // { // "bytes_copied": , @@ -2470,7 +2503,7 @@ EVENT_TYPE(SIMPLE_CACHE_ENTRY_CHECKSUM_BEGIN) // } EVENT_TYPE(SIMPLE_CACHE_ENTRY_CHECKSUM_END) -// This event is created when WriteEntry is called. +// This event is created when WriteData is called. // It contains the following parameters: // { // "index": , @@ -2502,7 +2535,7 @@ EVENT_TYPE(SIMPLE_CACHE_ENTRY_WRITE_OPTIMISTIC) // } EVENT_TYPE(SIMPLE_CACHE_ENTRY_WRITE_BEGIN) -// This event is created when the Simple Cache finishes a WriteEntry call. +// This event is created when the Simple Cache finishes a WriteData call. // It contains the following parameters: // { // "bytes_copied": , @@ -2510,6 +2543,60 @@ EVENT_TYPE(SIMPLE_CACHE_ENTRY_WRITE_BEGIN) // } EVENT_TYPE(SIMPLE_CACHE_ENTRY_WRITE_END) +// This event is created when ReadSparseData is called. +// It contains the following parameters: +// { +// "offset": , +// "buf_len": , +// "truncate": , +// } +EVENT_TYPE(SIMPLE_CACHE_ENTRY_READ_SPARSE_CALL) + +// This event is created when the Simple Cache actually begins reading sparse +// data from the cache entry. +// It contains the following parameters: +// { +// "offset": , +// "buf_len": , +// "truncate": , +// } +EVENT_TYPE(SIMPLE_CACHE_ENTRY_READ_SPARSE_BEGIN) + +// This event is created when the Simple Cache finishes a ReadSparseData call. +// It contains the following parameters: +// { +// "bytes_copied": , +// "net_error": , +// } +EVENT_TYPE(SIMPLE_CACHE_ENTRY_READ_SPARSE_END) + +// This event is created when WriteSparseData is called. +// It contains the following parameters: +// { +// "offset": , +// "buf_len": , +// } +EVENT_TYPE(SIMPLE_CACHE_ENTRY_WRITE_SPARSE_CALL) + +// This event is created when the Simple Cache actually begins writing sparse +// data to the cache entry. +// It contains the following parameters: +// { +// "offset": , +// "buf_len": , +// } +EVENT_TYPE(SIMPLE_CACHE_ENTRY_WRITE_SPARSE_BEGIN) + +// This event is created when the Simple Cache finishes a WriteSparseData call. +// It contains the following parameters: +// { +// "bytes_copied": , +// "net_error": , +// } +EVENT_TYPE(SIMPLE_CACHE_ENTRY_WRITE_SPARSE_END) + // This event is created when DoomEntry is called. It contains no parameters. EVENT_TYPE(SIMPLE_CACHE_ENTRY_DOOM_CALL) diff --git a/src/net/quic/congestion_control/general_loss_algorithm.cc b/src/net/quic/congestion_control/general_loss_algorithm.cc index 887341f5..f1239b7a 100644 --- a/src/net/quic/congestion_control/general_loss_algorithm.cc +++ b/src/net/quic/congestion_control/general_loss_algorithm.cc @@ -5,6 +5,7 @@ #include "net/quic/congestion_control/general_loss_algorithm.h" #include "net/quic/congestion_control/rtt_stats.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_protocol.h" namespace net { @@ -32,21 +33,6 @@ LossDetectionType GeneralLossAlgorithm::GetLossDetectionType() const { return loss_type_; } -PacketNumberSet GeneralLossAlgorithm::DetectLostPackets( - const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - QuicPacketNumber largest_observed, - const RttStats& rtt_stats) { - SendAlgorithmInterface::CongestionVector packets_lost; - DetectLosses(unacked_packets, time, rtt_stats, &packets_lost); - PacketNumberSet lost_packets; - for (const std::pair& pair : - packets_lost) { - lost_packets.insert(pair.first); - } - return lost_packets; -} - // Uses nack counts to decide when packets are lost. void GeneralLossAlgorithm::DetectLosses( const QuicUnackedPacketMap& unacked_packets, @@ -80,9 +66,9 @@ void GeneralLossAlgorithm::DetectLosses( } // FACK based loss detection. - LOG_IF(DFATAL, it->nack_count == 0 && it->sent_time.IsInitialized()) + QUIC_BUG_IF(it->nack_count == 0 && it->sent_time.IsInitialized()) << "All packets less than largest observed should have been nacked." - << "packet_number:" << packet_number + << " packet_number:" << packet_number << " largest_observed:" << largest_observed; if (it->nack_count >= kNumberOfNacksBeforeRetransmission) { packets_lost->push_back(std::make_pair(packet_number, it->bytes_sent)); @@ -99,7 +85,7 @@ void GeneralLossAlgorithm::DetectLosses( // 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->retransmittable_frames && + if (!it->retransmittable_frames.empty() && 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. diff --git a/src/net/quic/congestion_control/general_loss_algorithm.h b/src/net/quic/congestion_control/general_loss_algorithm.h index 54c170fc..162f758c 100644 --- a/src/net/quic/congestion_control/general_loss_algorithm.h +++ b/src/net/quic/congestion_control/general_loss_algorithm.h @@ -33,12 +33,6 @@ class NET_EXPORT_PRIVATE GeneralLossAlgorithm : public LossDetectionInterface { loss_type_ = loss_type; } - // Only supported for tests. - PacketNumberSet DetectLostPackets(const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - QuicPacketNumber largest_observed, - const RttStats& rtt_stats) override; - // Uses |largest_observed| and time to decide when packets are lost. void DetectLosses( const QuicUnackedPacketMap& unacked_packets, diff --git a/src/net/quic/congestion_control/hybrid_slow_start.cc b/src/net/quic/congestion_control/hybrid_slow_start.cc index a5f53f96..5f334c74 100644 --- a/src/net/quic/congestion_control/hybrid_slow_start.cc +++ b/src/net/quic/congestion_control/hybrid_slow_start.cc @@ -30,12 +30,11 @@ HybridSlowStart::HybridSlowStart() rtt_sample_count_(0), current_min_rtt_(QuicTime::Delta::Zero()) {} -void HybridSlowStart::OnPacketAcked(QuicPacketNumber acked_packet_number, - bool in_slow_start) { +void HybridSlowStart::OnPacketAcked(QuicPacketNumber acked_packet_number) { // 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_packet_number)) { + if (IsEndOfRound(acked_packet_number)) { started_ = false; } } diff --git a/src/net/quic/congestion_control/hybrid_slow_start.h b/src/net/quic/congestion_control/hybrid_slow_start.h index cf752f65..dbd440df 100644 --- a/src/net/quic/congestion_control/hybrid_slow_start.h +++ b/src/net/quic/congestion_control/hybrid_slow_start.h @@ -29,7 +29,7 @@ class NET_EXPORT_PRIVATE HybridSlowStart { public: HybridSlowStart(); - void OnPacketAcked(QuicPacketNumber acked_packet_number, bool in_slow_start); + void OnPacketAcked(QuicPacketNumber acked_packet_number); void OnPacketSent(QuicPacketNumber packet_number); diff --git a/src/net/quic/congestion_control/loss_detection_interface.cc b/src/net/quic/congestion_control/loss_detection_interface.cc index 4db56c99..bf894286 100644 --- a/src/net/quic/congestion_control/loss_detection_interface.cc +++ b/src/net/quic/congestion_control/loss_detection_interface.cc @@ -5,8 +5,7 @@ #include "net/quic/congestion_control/loss_detection_interface.h" #include "net/quic/congestion_control/general_loss_algorithm.h" -#include "net/quic/congestion_control/tcp_loss_algorithm.h" -#include "net/quic/congestion_control/time_loss_algorithm.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_flags.h" namespace net { @@ -14,17 +13,7 @@ namespace net { // Factory for loss detection algorithm. LossDetectionInterface* LossDetectionInterface::Create( LossDetectionType loss_type) { - if (FLAGS_quic_general_loss_algorithm) { - return new GeneralLossAlgorithm(loss_type); - } - switch (loss_type) { - case kNack: - return new TCPLossAlgorithm(); - case kTime: - return new TimeLossAlgorithm(); - } - LOG(DFATAL) << "Unknown loss detection algorithm:" << loss_type; - return nullptr; + return new GeneralLossAlgorithm(loss_type); } } // namespace net diff --git a/src/net/quic/congestion_control/loss_detection_interface.h b/src/net/quic/congestion_control/loss_detection_interface.h index d337dbe2..ea931387 100644 --- a/src/net/quic/congestion_control/loss_detection_interface.h +++ b/src/net/quic/congestion_control/loss_detection_interface.h @@ -26,12 +26,6 @@ class NET_EXPORT_PRIVATE LossDetectionInterface { virtual LossDetectionType GetLossDetectionType() const = 0; // Called when a new ack arrives or the loss alarm fires. - virtual PacketNumberSet DetectLostPackets( - const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - QuicPacketNumber largest_observed, - const RttStats& rtt_stats) = 0; - virtual void DetectLosses( const QuicUnackedPacketMap& unacked_packets, const QuicTime& time, diff --git a/src/net/quic/congestion_control/pacing_sender.cc b/src/net/quic/congestion_control/pacing_sender.cc index 040310f5..9789da1b 100644 --- a/src/net/quic/congestion_control/pacing_sender.cc +++ b/src/net/quic/congestion_control/pacing_sender.cc @@ -79,7 +79,7 @@ bool PacingSender::OnPacketSent( } // The next packet should be sent as soon as the current packets has been // transferred. - QuicTime::Delta delay = PacingRate().TransferTime(bytes); + QuicTime::Delta delay = sender_->PacingRate().TransferTime(bytes); // 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_) { @@ -111,6 +111,10 @@ void PacingSender::OnRetransmissionTimeout(bool packets_retransmitted) { sender_->OnRetransmissionTimeout(packets_retransmitted); } +void PacingSender::OnConnectionMigration() { + sender_->OnConnectionMigration(); +} + QuicTime::Delta PacingSender::TimeUntilSend( QuicTime now, QuicByteCount bytes_in_flight, diff --git a/src/net/quic/congestion_control/pacing_sender.h b/src/net/quic/congestion_control/pacing_sender.h index 185fec43..8073d586 100644 --- a/src/net/quic/congestion_control/pacing_sender.h +++ b/src/net/quic/congestion_control/pacing_sender.h @@ -54,7 +54,7 @@ class NET_EXPORT_PRIVATE PacingSender : public SendAlgorithmInterface { QuicByteCount bytes, HasRetransmittableData is_retransmittable) override; void OnRetransmissionTimeout(bool packets_retransmitted) override; - void OnConnectionMigration() override {} + void OnConnectionMigration() override; QuicTime::Delta TimeUntilSend( QuicTime now, QuicByteCount bytes_in_flight, diff --git a/src/net/quic/congestion_control/rtt_stats.h b/src/net/quic/congestion_control/rtt_stats.h index 0827fa3c..736178e9 100644 --- a/src/net/quic/congestion_control/rtt_stats.h +++ b/src/net/quic/congestion_control/rtt_stats.h @@ -12,6 +12,7 @@ #include #include "base/macros.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" @@ -52,7 +53,7 @@ class NET_EXPORT_PRIVATE RttStats { // Sets an initial RTT to be used for SmoothedRtt before any RTT updates. void set_initial_rtt_us(int64_t initial_rtt_us) { if (initial_rtt_us <= 0) { - LOG(DFATAL) << "Attempt to set initial rtt to <= 0."; + QUIC_BUG << "Attempt to set initial rtt to <= 0."; return; } initial_rtt_us_ = initial_rtt_us; diff --git a/src/net/quic/congestion_control/send_algorithm_interface.cc b/src/net/quic/congestion_control/send_algorithm_interface.cc index d8a9c93c..20821a61 100644 --- a/src/net/quic/congestion_control/send_algorithm_interface.cc +++ b/src/net/quic/congestion_control/send_algorithm_interface.cc @@ -6,6 +6,9 @@ #include "net/quic/congestion_control/tcp_cubic_bytes_sender.h" #include "net/quic/congestion_control/tcp_cubic_sender.h" +#include "net/quic/congestion_control/tcp_cubic_sender_bytes.h" +#include "net/quic/congestion_control/tcp_cubic_sender_packets.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" namespace net { @@ -24,18 +27,38 @@ SendAlgorithmInterface* SendAlgorithmInterface::Create( kDefaultTCPMSS; switch (congestion_control_type) { case kCubic: + if (FLAGS_quic_use_new_tcp_sender) { + return new TcpCubicSenderPackets( + clock, rtt_stats, false /* don't use Reno */, + initial_congestion_window, max_congestion_window, stats); + } return new TcpCubicSender(clock, rtt_stats, false /* don't use Reno */, initial_congestion_window, max_congestion_window, stats); case kCubicBytes: + if (FLAGS_quic_use_new_tcp_sender) { + return new TcpCubicSenderBytes( + clock, rtt_stats, false /* don't use Reno */, + initial_congestion_window, max_congestion_window, stats); + } return new TcpCubicBytesSender( clock, rtt_stats, false /* don't use Reno */, initial_congestion_window, max_congestion_window, stats); case kReno: + if (FLAGS_quic_use_new_tcp_sender) { + return new TcpCubicSenderPackets(clock, rtt_stats, true /* use Reno */, + initial_congestion_window, + max_congestion_window, stats); + } return new TcpCubicSender(clock, rtt_stats, true /* use Reno */, initial_congestion_window, max_congestion_window, stats); case kRenoBytes: + if (FLAGS_quic_use_new_tcp_sender) { + return new TcpCubicSenderBytes(clock, rtt_stats, true /* use Reno */, + initial_congestion_window, + max_congestion_window, stats); + } return new TcpCubicBytesSender(clock, rtt_stats, true /* use Reno */, initial_congestion_window, max_congestion_window, stats); diff --git a/src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc b/src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc index 7f817ebf..2ceb719c 100644 --- a/src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc +++ b/src/net/quic/congestion_control/tcp_cubic_bytes_sender.cc @@ -10,6 +10,7 @@ #include "net/quic/congestion_control/rtt_stats.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/proto/cached_network_parameters.pb.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_flags.h" using std::max; @@ -52,7 +53,8 @@ TcpCubicBytesSender::TcpCubicBytesSender( initial_tcp_congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS), initial_max_tcp_congestion_window_(max_congestion_window * - kDefaultTCPMSS) {} + kDefaultTCPMSS), + slow_start_large_reduction_(false) {} TcpCubicBytesSender::~TcpCubicBytesSender() {} @@ -75,6 +77,11 @@ void TcpCubicBytesSender::SetFromConfig(const QuicConfig& config, min4_mode_ = true; min_congestion_window_ = kDefaultTCPMSS; } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kSSLR)) { + // Slow Start Fast Exit experiment. + slow_start_large_reduction_ = true; + } } } @@ -145,8 +152,9 @@ void TcpCubicBytesSender::OnPacketAcked(QuicPacketNumber acked_packet_number, return; } MaybeIncreaseCwnd(acked_packet_number, acked_bytes, bytes_in_flight); - // TODO(ianswett): Should this even be called when not in slow start? - hybrid_slow_start_.OnPacketAcked(acked_packet_number, InSlowStart()); + if (InSlowStart()) { + hybrid_slow_start_.OnPacketAcked(acked_packet_number); + } } void TcpCubicBytesSender::OnPacketLost(QuicPacketNumber packet_number, @@ -156,6 +164,12 @@ void TcpCubicBytesSender::OnPacketLost(QuicPacketNumber packet_number, if (packet_number <= largest_sent_at_last_cutback_) { if (last_cutback_exited_slowstart_) { ++stats_->slowstart_packets_lost; + if (slow_start_large_reduction_) { + // Reduce congestion window by 1 MSS for every loss. + congestion_window_ = + max(congestion_window_ - kDefaultTCPMSS, min_congestion_window_); + slowstart_threshold_ = congestion_window_; + } } DVLOG(1) << "Ignoring loss for largest_missing:" << packet_number << " because it was sent prior to the last CWND cutback."; @@ -169,17 +183,21 @@ void TcpCubicBytesSender::OnPacketLost(QuicPacketNumber packet_number, prr_.OnPacketLost(bytes_in_flight); - if (reno_) { + // TODO(jri): Separate out all of slow start into a separate class. + if (slow_start_large_reduction_ && InSlowStart()) { + DCHECK_LT(kDefaultTCPMSS, congestion_window_); + congestion_window_ = congestion_window_ - kDefaultTCPMSS; + } else if (reno_) { congestion_window_ = congestion_window_ * RenoBeta(); } else { congestion_window_ = cubic_.CongestionWindowAfterPacketLoss(congestion_window_); } - slowstart_threshold_ = congestion_window_; // Enforce TCP's minimum congestion window of 2*MSS. if (congestion_window_ < min_congestion_window_) { congestion_window_ = min_congestion_window_; } + slowstart_threshold_ = congestion_window_; largest_sent_at_last_cutback_ = largest_sent_packet_number_; // Reset packet count from congestion avoidance mode. We start counting again // when we're out of recovery. @@ -298,7 +316,7 @@ void TcpCubicBytesSender::MaybeIncreaseCwnd( QuicPacketNumber acked_packet_number, QuicByteCount acked_bytes, QuicByteCount bytes_in_flight) { - LOG_IF(DFATAL, InRecovery()) << "Never increase the CWND during recovery."; + QUIC_BUG_IF(InRecovery()) << "Never increase the CWND during recovery."; // Do not increase the congestion window unless the sender is close to using // the current window. if (!IsCwndLimited(bytes_in_flight)) { diff --git a/src/net/quic/congestion_control/tcp_cubic_bytes_sender.h b/src/net/quic/congestion_control/tcp_cubic_bytes_sender.h index c96b178e..e1b0185f 100644 --- a/src/net/quic/congestion_control/tcp_cubic_bytes_sender.h +++ b/src/net/quic/congestion_control/tcp_cubic_bytes_sender.h @@ -140,6 +140,9 @@ class NET_EXPORT_PRIVATE TcpCubicBytesSender : public SendAlgorithmInterface { // set when this algorithm is created. const QuicByteCount initial_max_tcp_congestion_window_; + // When true, exit slow start with large cutback of congestion window. + bool slow_start_large_reduction_; + DISALLOW_COPY_AND_ASSIGN(TcpCubicBytesSender); }; diff --git a/src/net/quic/congestion_control/tcp_cubic_sender.cc b/src/net/quic/congestion_control/tcp_cubic_sender.cc index 256126f6..f78f5873 100644 --- a/src/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/src/net/quic/congestion_control/tcp_cubic_sender.cc @@ -11,6 +11,7 @@ #include "net/quic/congestion_control/rtt_stats.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/proto/cached_network_parameters.pb.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_flags.h" using std::max; @@ -50,7 +51,8 @@ TcpCubicSender::TcpCubicSender(const QuicClock* clock, last_cutback_exited_slowstart_(false), max_tcp_congestion_window_(max_tcp_congestion_window), initial_tcp_congestion_window_(initial_tcp_congestion_window), - initial_max_tcp_congestion_window_(max_tcp_congestion_window) {} + initial_max_tcp_congestion_window_(max_tcp_congestion_window), + slow_start_large_reduction_(false) {} TcpCubicSender::~TcpCubicSender() { UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_); @@ -90,6 +92,11 @@ void TcpCubicSender::SetFromConfig(const QuicConfig& config, min4_mode_ = true; min_congestion_window_ = 1; } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kSSLR)) { + // Slow Start Fast Exit experiment. + slow_start_large_reduction_ = true; + } } } @@ -159,8 +166,9 @@ void TcpCubicSender::OnPacketAcked(QuicPacketNumber acked_packet_number, return; } MaybeIncreaseCwnd(acked_packet_number, bytes_in_flight); - // TODO(ianswett): Should this even be called when not in slow start? - hybrid_slow_start_.OnPacketAcked(acked_packet_number, InSlowStart()); + if (InSlowStart()) { + hybrid_slow_start_.OnPacketAcked(acked_packet_number); + } } void TcpCubicSender::OnPacketLost(QuicPacketNumber packet_number, @@ -170,6 +178,12 @@ void TcpCubicSender::OnPacketLost(QuicPacketNumber packet_number, if (packet_number <= largest_sent_at_last_cutback_) { if (last_cutback_exited_slowstart_) { ++stats_->slowstart_packets_lost; + if (slow_start_large_reduction_) { + // Reduce congestion window by 1 for every loss. + congestion_window_ = + max(congestion_window_ - 1, min_congestion_window_); + slowstart_threshold_ = congestion_window_; + } } DVLOG(1) << "Ignoring loss for largest_missing:" << packet_number << " because it was sent prior to the last CWND cutback."; @@ -183,17 +197,21 @@ void TcpCubicSender::OnPacketLost(QuicPacketNumber packet_number, prr_.OnPacketLost(bytes_in_flight); - if (reno_) { + // TODO(jri): Separate out all of slow start into a separate class. + if (slow_start_large_reduction_ && InSlowStart()) { + DCHECK_LT(1u, congestion_window_); + congestion_window_ = congestion_window_ - 1; + } else if (reno_) { congestion_window_ = congestion_window_ * RenoBeta(); } else { congestion_window_ = cubic_.CongestionWindowAfterPacketLoss(congestion_window_); } - slowstart_threshold_ = congestion_window_; // Enforce a minimum congestion window. if (congestion_window_ < min_congestion_window_) { congestion_window_ = min_congestion_window_; } + slowstart_threshold_ = congestion_window_; largest_sent_at_last_cutback_ = largest_sent_packet_number_; // reset packet count from congestion avoidance mode. We start // counting again when we're out of recovery. @@ -311,7 +329,7 @@ bool TcpCubicSender::InRecovery() const { // represents, but quic has a separate ack for each packet. void TcpCubicSender::MaybeIncreaseCwnd(QuicPacketNumber acked_packet_number, QuicByteCount bytes_in_flight) { - LOG_IF(DFATAL, InRecovery()) << "Never increase the CWND during recovery."; + QUIC_BUG_IF(InRecovery()) << "Never increase the CWND during recovery."; // Do not increase the congestion window unless the sender is close to using // the current window. if (!IsCwndLimited(bytes_in_flight)) { diff --git a/src/net/quic/congestion_control/tcp_cubic_sender.h b/src/net/quic/congestion_control/tcp_cubic_sender.h index 30b3f2dc..9b253a38 100644 --- a/src/net/quic/congestion_control/tcp_cubic_sender.h +++ b/src/net/quic/congestion_control/tcp_cubic_sender.h @@ -141,6 +141,9 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { // this algorithm is created. const QuicPacketCount initial_max_tcp_congestion_window_; + // When true, exit slow start with large cutback of congestion window. + bool slow_start_large_reduction_; + DISALLOW_COPY_AND_ASSIGN(TcpCubicSender); }; diff --git a/src/net/quic/congestion_control/tcp_cubic_sender_base.cc b/src/net/quic/congestion_control/tcp_cubic_sender_base.cc new file mode 100644 index 00000000..deb5a4f3 --- /dev/null +++ b/src/net/quic/congestion_control/tcp_cubic_sender_base.cc @@ -0,0 +1,267 @@ +// 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/tcp_cubic_sender_packets.h" + +#include + +#include "base/metrics/histogram_macros.h" +#include "net/quic/congestion_control/prr_sender.h" +#include "net/quic/congestion_control/rtt_stats.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/proto/cached_network_parameters.pb.h" +#include "net/quic/quic_bug_tracker.h" +#include "net/quic/quic_flags.h" + +using std::max; +using std::min; + +namespace net { + +namespace { +// Constants based on TCP defaults. +// 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 QuicByteCount kMaxBurstBytes = 3 * kDefaultTCPMSS; +const float kRenoBeta = 0.7f; // Reno backoff factor. +const uint32_t kDefaultNumConnections = 2; // N-connection emulation. +} // namespace + +TcpCubicSenderBase::TcpCubicSenderBase(const QuicClock* clock, + const RttStats* rtt_stats, + bool reno, + QuicConnectionStats* stats) + : rtt_stats_(rtt_stats), + stats_(stats), + reno_(reno), + num_connections_(kDefaultNumConnections), + largest_sent_packet_number_(0), + largest_acked_packet_number_(0), + largest_sent_at_last_cutback_(0), + min4_mode_(false), + last_cutback_exited_slowstart_(false), + slow_start_large_reduction_(false) {} + +TcpCubicSenderBase::~TcpCubicSenderBase() {} + +void TcpCubicSenderBase::SetFromConfig(const QuicConfig& config, + Perspective perspective) { + if (perspective == Perspective::IS_SERVER) { + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW03)) { + // Initial window experiment. + SetCongestionWindowInPackets(3); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) { + // Initial window experiment. + SetCongestionWindowInPackets(10); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW20)) { + // Initial window experiment. + SetCongestionWindowInPackets(20); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW50)) { + // Initial window experiment. + SetCongestionWindowInPackets(50); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN1)) { + // Min CWND experiment. + SetMinCongestionWindowInPackets(1); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN4)) { + // Min CWND of 4 experiment. + min4_mode_ = true; + SetMinCongestionWindowInPackets(1); + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kSSLR)) { + // Slow Start Fast Exit experiment. + slow_start_large_reduction_ = true; + } + } +} + +void TcpCubicSenderBase::ResumeConnectionState( + const CachedNetworkParameters& cached_network_params, + bool max_bandwidth_resumption) { + QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond( + max_bandwidth_resumption + ? cached_network_params.max_bandwidth_estimate_bytes_per_second() + : cached_network_params.bandwidth_estimate_bytes_per_second()); + QuicTime::Delta rtt = + QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms()); + + SetCongestionWindowFromBandwidthAndRtt(bandwidth, rtt); +} + +void TcpCubicSenderBase::SetNumEmulatedConnections(int num_connections) { + num_connections_ = max(1, num_connections); +} + +float TcpCubicSenderBase::RenoBeta() const { + // 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: + return (num_connections_ - 1 + kRenoBeta) / num_connections_; +} + +void TcpCubicSenderBase::OnCongestionEvent( + bool rtt_updated, + QuicByteCount bytes_in_flight, + const CongestionVector& acked_packets, + const CongestionVector& lost_packets) { + if (rtt_updated && InSlowStart() && + hybrid_slow_start_.ShouldExitSlowStart( + rtt_stats_->latest_rtt(), rtt_stats_->min_rtt(), + GetCongestionWindow() / kDefaultTCPMSS)) { + ExitSlowstart(); + } + for (CongestionVector::const_iterator it = lost_packets.begin(); + it != lost_packets.end(); ++it) { + OnPacketLost(it->first, bytes_in_flight); + } + for (CongestionVector::const_iterator it = acked_packets.begin(); + it != acked_packets.end(); ++it) { + OnPacketAcked(it->first, it->second, bytes_in_flight); + } +} + +void TcpCubicSenderBase::OnPacketAcked(QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount bytes_in_flight) { + largest_acked_packet_number_ = + max(acked_packet_number, largest_acked_packet_number_); + if (InRecovery()) { + // PRR is used when in recovery. + prr_.OnPacketAcked(acked_bytes); + return; + } + MaybeIncreaseCwnd(acked_packet_number, acked_bytes, bytes_in_flight); + if (InSlowStart()) { + hybrid_slow_start_.OnPacketAcked(acked_packet_number); + } +} + +bool TcpCubicSenderBase::OnPacketSent( + QuicTime /*sent_time*/, + QuicByteCount /*bytes_in_flight*/, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData is_retransmittable) { + if (InSlowStart()) { + ++(stats_->slowstart_packets_sent); + } + + // Only update bytes_in_flight_ for data packets. + if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) { + return false; + } + if (InRecovery()) { + // PRR is used when in recovery. + prr_.OnPacketSent(bytes); + } + DCHECK_LT(largest_sent_packet_number_, packet_number); + largest_sent_packet_number_ = packet_number; + hybrid_slow_start_.OnPacketSent(packet_number); + return true; +} + +QuicTime::Delta TcpCubicSenderBase::TimeUntilSend( + QuicTime /* now */, + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) const { + if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) { + DCHECK(!FLAGS_quic_respect_send_alarm2); + // For TCP we can always send an ACK immediately. + return QuicTime::Delta::Zero(); + } + if (InRecovery()) { + // PRR is used when in recovery. + return prr_.TimeUntilSend(GetCongestionWindow(), bytes_in_flight, + GetSlowStartThreshold()); + } + if (GetCongestionWindow() > bytes_in_flight) { + return QuicTime::Delta::Zero(); + } + if (min4_mode_ && bytes_in_flight < 4 * kDefaultTCPMSS) { + return QuicTime::Delta::Zero(); + } + return QuicTime::Delta::Infinite(); +} + +QuicBandwidth TcpCubicSenderBase::PacingRate() const { + // We pace at twice the rate of the underlying sender's bandwidth estimate + // during slow start and 1.25x during congestion avoidance to ensure pacing + // doesn't prevent us from filling the window. + QuicTime::Delta srtt = rtt_stats_->smoothed_rtt(); + if (srtt.IsZero()) { + srtt = QuicTime::Delta::FromMicroseconds(rtt_stats_->initial_rtt_us()); + } + const QuicBandwidth bandwidth = + QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt); + return bandwidth.Scale(InSlowStart() ? 2 : 1.25); +} + +QuicBandwidth TcpCubicSenderBase::BandwidthEstimate() const { + QuicTime::Delta srtt = rtt_stats_->smoothed_rtt(); + if (srtt.IsZero()) { + // If we haven't measured an rtt, the bandwidth estimate is unknown. + return QuicBandwidth::Zero(); + } + return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt); +} + +QuicTime::Delta TcpCubicSenderBase::RetransmissionDelay() const { + if (rtt_stats_->smoothed_rtt().IsZero()) { + return QuicTime::Delta::Zero(); + } + return rtt_stats_->smoothed_rtt().Add( + rtt_stats_->mean_deviation().Multiply(4)); +} + +bool TcpCubicSenderBase::InSlowStart() const { + return GetCongestionWindow() < GetSlowStartThreshold(); +} + +bool TcpCubicSenderBase::IsCwndLimited(QuicByteCount bytes_in_flight) const { + const QuicByteCount congestion_window = GetCongestionWindow(); + if (bytes_in_flight >= congestion_window) { + return true; + } + const QuicByteCount available_bytes = congestion_window - bytes_in_flight; + const bool slow_start_limited = + InSlowStart() && bytes_in_flight > congestion_window / 2; + return slow_start_limited || available_bytes <= kMaxBurstBytes; +} + +bool TcpCubicSenderBase::InRecovery() const { + return largest_acked_packet_number_ <= largest_sent_at_last_cutback_ && + largest_acked_packet_number_ != 0; +} + +void TcpCubicSenderBase::OnRetransmissionTimeout(bool packets_retransmitted) { + largest_sent_at_last_cutback_ = 0; + if (!packets_retransmitted) { + return; + } + hybrid_slow_start_.Restart(); + HandleRetransmissionTimeout(); +} + +void TcpCubicSenderBase::OnConnectionMigration() { + hybrid_slow_start_.Restart(); + prr_ = PrrSender(); + largest_sent_packet_number_ = 0; + largest_acked_packet_number_ = 0; + largest_sent_at_last_cutback_ = 0; + last_cutback_exited_slowstart_ = false; +} + +} // namespace net diff --git a/src/net/quic/congestion_control/tcp_cubic_sender_base.h b/src/net/quic/congestion_control/tcp_cubic_sender_base.h new file mode 100644 index 00000000..4e80a0e1 --- /dev/null +++ b/src/net/quic/congestion_control/tcp_cubic_sender_base.h @@ -0,0 +1,150 @@ +// 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. +// +// TCP cubic send side congestion algorithm, emulates the behavior of TCP cubic. + +#ifndef NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BASE_H_ +#define NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BASE_H_ + +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "net/base/net_export.h" +#include "net/quic/congestion_control/cubic.h" +#include "net/quic/congestion_control/hybrid_slow_start.h" +#include "net/quic/congestion_control/prr_sender.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 { + +class RttStats; + +namespace test { +class TcpCubicSenderBasePeer; +} // namespace test + +class NET_EXPORT_PRIVATE TcpCubicSenderBase : public SendAlgorithmInterface { + public: + // Reno option and max_tcp_congestion_window are provided for testing. + TcpCubicSenderBase(const QuicClock* clock, + const RttStats* rtt_stats, + bool reno, + QuicConnectionStats* stats); + ~TcpCubicSenderBase() override; + + // Start implementation of SendAlgorithmInterface. + void SetFromConfig(const QuicConfig& config, + Perspective perspective) override; + void ResumeConnectionState( + const CachedNetworkParameters& cached_network_params, + bool max_bandwidth_resumption) override; + void SetNumEmulatedConnections(int num_connections) override; + void OnCongestionEvent(bool rtt_updated, + QuicByteCount bytes_in_flight, + const CongestionVector& acked_packets, + const CongestionVector& lost_packets) override; + bool OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData is_retransmittable) override; + void OnRetransmissionTimeout(bool packets_retransmitted) override; + void OnConnectionMigration() override; + QuicTime::Delta TimeUntilSend( + QuicTime now, + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) const override; + QuicBandwidth PacingRate() const override; + QuicBandwidth BandwidthEstimate() const override; + QuicTime::Delta RetransmissionDelay() const override; + bool InSlowStart() const override; + bool InRecovery() const override; + + protected: + // Called when resuming a previous bandwidth. + virtual void SetCongestionWindowFromBandwidthAndRtt(QuicBandwidth bandwidth, + QuicTime::Delta rtt) = 0; + + // Called when initializing the congestion window. + virtual void SetCongestionWindowInPackets( + QuicPacketCount congestion_window) = 0; + + // Called when initializing the minimum congestion window. + virtual void SetMinCongestionWindowInPackets( + QuicPacketCount congestion_window) = 0; + + // Called when slow start is exited to set SSTHRESH. + virtual void ExitSlowstart() = 0; + + // Called when a packet is lost. + virtual void OnPacketLost(QuicPacketNumber largest_loss, + QuicByteCount bytes_in_flight) = 0; + + // Called when a packet has been acked to possibly increase the congestion + // window. + virtual void MaybeIncreaseCwnd(QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount bytes_in_flight) = 0; + + // Called when a retransmission has occured which resulted in packets + // being retransmitted. + virtual void HandleRetransmissionTimeout() = 0; + + // Compute the TCP Reno beta based on the current number of connections. + float RenoBeta() const; + + bool IsCwndLimited(QuicByteCount bytes_in_flight) const; + + private: + friend class test::TcpCubicSenderBasePeer; + + // TODO(ianswett): Remove these and migrate to OnCongestionEvent. + void OnPacketAcked(QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount bytes_in_flight); + + protected: + // TODO(rch): Make these private and clean up subclass access to them. + HybridSlowStart hybrid_slow_start_; + PrrSender prr_; + const RttStats* rtt_stats_; + QuicConnectionStats* stats_; + + // If true, Reno congestion control is used instead of Cubic. + const bool reno_; + + // Number of connections to simulate. + uint32_t num_connections_; + + // Track the largest packet that has been sent. + QuicPacketNumber largest_sent_packet_number_; + + // Track the largest packet that has been acked. + QuicPacketNumber largest_acked_packet_number_; + + // Track the largest packet number outstanding when a CWND cutback occurs. + QuicPacketNumber largest_sent_at_last_cutback_; + + // Whether to use 4 packets as the actual min, but pace lower. + bool min4_mode_; + + // Whether the last loss event caused us to exit slowstart. + // Used for stats collection of slowstart_packets_lost + bool last_cutback_exited_slowstart_; + + // When true, exit slow start with large cutback of congestion window. + bool slow_start_large_reduction_; + + private: + DISALLOW_COPY_AND_ASSIGN(TcpCubicSenderBase); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BASE_H_ diff --git a/src/net/quic/congestion_control/tcp_cubic_sender_bytes.cc b/src/net/quic/congestion_control/tcp_cubic_sender_bytes.cc new file mode 100644 index 00000000..39804878 --- /dev/null +++ b/src/net/quic/congestion_control/tcp_cubic_sender_bytes.cc @@ -0,0 +1,207 @@ +// Copyright (c) 2015 The Chromium 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_cubic_sender_bytes.h" + +#include + +#include "net/quic/congestion_control/prr_sender.h" +#include "net/quic/congestion_control/rtt_stats.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/proto/cached_network_parameters.pb.h" +#include "net/quic/quic_bug_tracker.h" +#include "net/quic/quic_flags.h" + +using std::max; +using std::min; + +namespace net { + +namespace { +// Constants based on TCP defaults. +// The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a +// fast retransmission. +const QuicByteCount kDefaultMinimumCongestionWindow = 2 * kDefaultTCPMSS; +} // namespace + +TcpCubicSenderBytes::TcpCubicSenderBytes( + const QuicClock* clock, + const RttStats* rtt_stats, + bool reno, + QuicPacketCount initial_tcp_congestion_window, + QuicPacketCount max_congestion_window, + QuicConnectionStats* stats) + : TcpCubicSenderBase(clock, rtt_stats, reno, stats), + cubic_(clock), + num_acked_packets_(0), + congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS), + min_congestion_window_(kDefaultMinimumCongestionWindow), + max_congestion_window_(max_congestion_window * kDefaultTCPMSS), + slowstart_threshold_(max_congestion_window * kDefaultTCPMSS), + initial_tcp_congestion_window_(initial_tcp_congestion_window * + kDefaultTCPMSS), + initial_max_tcp_congestion_window_(max_congestion_window * + kDefaultTCPMSS) {} + +TcpCubicSenderBytes::~TcpCubicSenderBytes() {} + +void TcpCubicSenderBytes::SetCongestionWindowFromBandwidthAndRtt( + QuicBandwidth bandwidth, + QuicTime::Delta rtt) { + // Make sure CWND is in appropriate range (in case of bad data). + QuicByteCount new_congestion_window = bandwidth.ToBytesPerPeriod(rtt); + congestion_window_ = + max(min(new_congestion_window, kMaxCongestionWindow * kDefaultTCPMSS), + kMinCongestionWindowForBandwidthResumption * kDefaultTCPMSS); +} + +void TcpCubicSenderBytes::SetCongestionWindowInPackets( + QuicPacketCount congestion_window) { + congestion_window_ = congestion_window * kDefaultTCPMSS; +} + +void TcpCubicSenderBytes::SetMinCongestionWindowInPackets( + QuicPacketCount congestion_window) { + min_congestion_window_ = congestion_window * kDefaultTCPMSS; +} + +void TcpCubicSenderBytes::SetNumEmulatedConnections(int num_connections) { + TcpCubicSenderBase::SetNumEmulatedConnections(num_connections); + cubic_.SetNumConnections(num_connections_); +} + +void TcpCubicSenderBytes::SetMaxCongestionWindow( + QuicByteCount max_congestion_window) { + max_congestion_window_ = max_congestion_window; +} + +void TcpCubicSenderBytes::ExitSlowstart() { + slowstart_threshold_ = congestion_window_; +} + +void TcpCubicSenderBytes::OnPacketLost(QuicPacketNumber packet_number, + 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 (packet_number <= largest_sent_at_last_cutback_) { + if (last_cutback_exited_slowstart_) { + ++stats_->slowstart_packets_lost; + if (slow_start_large_reduction_) { + // Reduce congestion window by 1 MSS for every loss. + congestion_window_ = + max(congestion_window_ - kDefaultTCPMSS, min_congestion_window_); + slowstart_threshold_ = congestion_window_; + } + } + DVLOG(1) << "Ignoring loss for largest_missing:" << packet_number + << " 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; + } + + prr_.OnPacketLost(bytes_in_flight); + + // TODO(jri): Separate out all of slow start into a separate class. + if (slow_start_large_reduction_ && InSlowStart()) { + DCHECK_LT(kDefaultTCPMSS, congestion_window_); + congestion_window_ = congestion_window_ - kDefaultTCPMSS; + } else if (reno_) { + congestion_window_ = congestion_window_ * RenoBeta(); + } else { + congestion_window_ = + cubic_.CongestionWindowAfterPacketLoss(congestion_window_); + } + // Enforce TCP's minimum congestion window of 2*MSS. + if (congestion_window_ < min_congestion_window_) { + congestion_window_ = min_congestion_window_; + } + slowstart_threshold_ = congestion_window_; + largest_sent_at_last_cutback_ = largest_sent_packet_number_; + // Reset packet count from congestion avoidance mode. We start counting again + // when we're out of recovery. + num_acked_packets_ = 0; + DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_; +} + +QuicByteCount TcpCubicSenderBytes::GetCongestionWindow() const { + return congestion_window_; +} + +QuicByteCount TcpCubicSenderBytes::GetSlowStartThreshold() const { + return slowstart_threshold_; +} + +// 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 TcpCubicSenderBytes::MaybeIncreaseCwnd( + QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount bytes_in_flight) { + QUIC_BUG_IF(InRecovery()) << "Never increase the CWND during recovery."; + // Do not increase the congestion window unless the sender is close to using + // the current window. + if (!IsCwndLimited(bytes_in_flight)) { + cubic_.OnApplicationLimited(); + return; + } + if (congestion_window_ >= max_congestion_window_) { + return; + } + if (InSlowStart()) { + // TCP slow start, exponential growth, increase by one for each ACK. + congestion_window_ += kDefaultTCPMSS; + DVLOG(1) << "Slow start; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_; + return; + } + // Congestion avoidance. + if (reno_) { + // Classic Reno congestion avoidance. + ++num_acked_packets_; + // Divide by num_connections to smoothly increase the CWND at a faster rate + // than conventional Reno. + if (num_acked_packets_ * num_connections_ >= + congestion_window_ / kDefaultTCPMSS) { + congestion_window_ += kDefaultTCPMSS; + num_acked_packets_ = 0; + } + + DVLOG(1) << "Reno; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_ + << " congestion window count: " << num_acked_packets_; + } else { + congestion_window_ = + min(max_congestion_window_, + cubic_.CongestionWindowAfterAck(acked_bytes, congestion_window_, + rtt_stats_->min_rtt())); + DVLOG(1) << "Cubic; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_; + } +} + +void TcpCubicSenderBytes::HandleRetransmissionTimeout() { + cubic_.Reset(); + slowstart_threshold_ = congestion_window_ / 2; + congestion_window_ = min_congestion_window_; +} + +void TcpCubicSenderBytes::OnConnectionMigration() { + TcpCubicSenderBase::OnConnectionMigration(); + cubic_.Reset(); + num_acked_packets_ = 0; + congestion_window_ = initial_tcp_congestion_window_; + max_congestion_window_ = initial_max_tcp_congestion_window_; + slowstart_threshold_ = initial_max_tcp_congestion_window_; +} + +CongestionControlType TcpCubicSenderBytes::GetCongestionControlType() const { + return reno_ ? kRenoBytes : kCubicBytes; +} + +} // namespace net diff --git a/src/net/quic/congestion_control/tcp_cubic_sender_bytes.h b/src/net/quic/congestion_control/tcp_cubic_sender_bytes.h new file mode 100644 index 00000000..c1cffe42 --- /dev/null +++ b/src/net/quic/congestion_control/tcp_cubic_sender_bytes.h @@ -0,0 +1,98 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TCP cubic send side congestion algorithm, emulates the behavior of TCP cubic. + +#ifndef NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BYTES_H_ +#define NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BYTES_H_ + +#include + +#include "base/macros.h" +#include "net/base/net_export.h" +#include "net/quic/congestion_control/cubic_bytes.h" +#include "net/quic/congestion_control/hybrid_slow_start.h" +#include "net/quic/congestion_control/prr_sender.h" +#include "net/quic/congestion_control/tcp_cubic_sender_base.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 { + +class RttStats; + +namespace test { +class TcpCubicSenderBytesPeer; +} // namespace test + +class NET_EXPORT_PRIVATE TcpCubicSenderBytes : public TcpCubicSenderBase { + public: + TcpCubicSenderBytes(const QuicClock* clock, + const RttStats* rtt_stats, + bool reno, + QuicPacketCount initial_tcp_congestion_window, + QuicPacketCount max_congestion_window, + QuicConnectionStats* stats); + ~TcpCubicSenderBytes() override; + + // Start implementation of SendAlgorithmInterface. + void SetNumEmulatedConnections(int num_connections) override; + void SetMaxCongestionWindow(QuicByteCount max_congestion_window) override; + void OnConnectionMigration() override; + QuicByteCount GetCongestionWindow() const override; + QuicByteCount GetSlowStartThreshold() const override; + CongestionControlType GetCongestionControlType() const override; + // End implementation of SendAlgorithmInterface. + + protected: + // TcpCubicSenderBase methods + void SetCongestionWindowFromBandwidthAndRtt(QuicBandwidth bandwidth, + QuicTime::Delta rtt) override; + void SetCongestionWindowInPackets(QuicPacketCount congestion_window) override; + void SetMinCongestionWindowInPackets( + QuicPacketCount congestion_window) override; + void ExitSlowstart() override; + void OnPacketLost(QuicPacketNumber largest_loss, + QuicByteCount bytes_in_flight) override; + void MaybeIncreaseCwnd(QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount bytes_in_flight) override; + void HandleRetransmissionTimeout() override; + + private: + friend class test::TcpCubicSenderBytesPeer; + + CubicBytes cubic_; + + // ACK counter for the Reno implementation. + uint64_t num_acked_packets_; + + // Congestion window in bytes. + QuicByteCount congestion_window_; + + // Minimum congestion window in bytes. + QuicByteCount min_congestion_window_; + + // Maximum congestion window in bytes. + QuicByteCount max_congestion_window_; + + // Slow start congestion window in bytes, aka ssthresh. + QuicByteCount slowstart_threshold_; + + // Initial TCP congestion window in bytes. This variable can only be set when + // this algorithm is created. + const QuicByteCount initial_tcp_congestion_window_; + + // Initial maximum TCP congestion window in bytes. This variable can only be + // set when this algorithm is created. + const QuicByteCount initial_max_tcp_congestion_window_; + + DISALLOW_COPY_AND_ASSIGN(TcpCubicSenderBytes); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_BYTES_SENDER_H_ diff --git a/src/net/quic/congestion_control/tcp_cubic_sender_packets.cc b/src/net/quic/congestion_control/tcp_cubic_sender_packets.cc new file mode 100644 index 00000000..ee2837ac --- /dev/null +++ b/src/net/quic/congestion_control/tcp_cubic_sender_packets.cc @@ -0,0 +1,204 @@ +// 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/tcp_cubic_sender_packets.h" + +#include + +#include "base/metrics/histogram_macros.h" +#include "net/quic/congestion_control/prr_sender.h" +#include "net/quic/congestion_control/rtt_stats.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/proto/cached_network_parameters.pb.h" +#include "net/quic/quic_bug_tracker.h" +#include "net/quic/quic_flags.h" + +using std::max; +using std::min; + +namespace net { + +namespace { +// Constants based on TCP defaults. +// 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 QuicPacketCount kDefaultMinimumCongestionWindow = 2; +} // namespace + +TcpCubicSenderPackets::TcpCubicSenderPackets( + const QuicClock* clock, + const RttStats* rtt_stats, + bool reno, + QuicPacketCount initial_tcp_congestion_window, + QuicPacketCount max_tcp_congestion_window, + QuicConnectionStats* stats) + : TcpCubicSenderBase(clock, rtt_stats, reno, stats), + cubic_(clock), + congestion_window_count_(0), + congestion_window_(initial_tcp_congestion_window), + min_congestion_window_(kDefaultMinimumCongestionWindow), + slowstart_threshold_(max_tcp_congestion_window), + max_tcp_congestion_window_(max_tcp_congestion_window), + initial_tcp_congestion_window_(initial_tcp_congestion_window), + initial_max_tcp_congestion_window_(max_tcp_congestion_window) {} + +TcpCubicSenderPackets::~TcpCubicSenderPackets() {} + +void TcpCubicSenderPackets::SetCongestionWindowFromBandwidthAndRtt( + QuicBandwidth bandwidth, + QuicTime::Delta rtt) { + // Make sure CWND is in appropriate range (in case of bad data). + QuicPacketCount new_congestion_window = + bandwidth.ToBytesPerPeriod(rtt) / kDefaultTCPMSS; + congestion_window_ = max(min(new_congestion_window, kMaxCongestionWindow), + kMinCongestionWindowForBandwidthResumption); +} + +void TcpCubicSenderPackets::SetCongestionWindowInPackets( + QuicPacketCount congestion_window) { + congestion_window_ = congestion_window; +} + +void TcpCubicSenderPackets::SetMinCongestionWindowInPackets( + QuicPacketCount congestion_window) { + min_congestion_window_ = congestion_window; +} + +void TcpCubicSenderPackets::SetNumEmulatedConnections(int num_connections) { + TcpCubicSenderBase::SetNumEmulatedConnections(num_connections); + cubic_.SetNumConnections(num_connections_); +} + +void TcpCubicSenderPackets::SetMaxCongestionWindow( + QuicByteCount max_congestion_window) { + max_tcp_congestion_window_ = max_congestion_window / kDefaultTCPMSS; +} + +void TcpCubicSenderPackets::ExitSlowstart() { + slowstart_threshold_ = congestion_window_; +} + +void TcpCubicSenderPackets::OnPacketLost(QuicPacketNumber packet_number, + 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 (packet_number <= largest_sent_at_last_cutback_) { + if (last_cutback_exited_slowstart_) { + ++stats_->slowstart_packets_lost; + if (slow_start_large_reduction_) { + // Reduce congestion window by 1 for every loss. + congestion_window_ = + max(congestion_window_ - 1, min_congestion_window_); + slowstart_threshold_ = congestion_window_; + } + } + DVLOG(1) << "Ignoring loss for largest_missing:" << packet_number + << " 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; + } + + prr_.OnPacketLost(bytes_in_flight); + + // TODO(jri): Separate out all of slow start into a separate class. + if (slow_start_large_reduction_ && InSlowStart()) { + DCHECK_LT(1u, congestion_window_); + congestion_window_ = congestion_window_ - 1; + } else if (reno_) { + congestion_window_ = congestion_window_ * RenoBeta(); + } else { + congestion_window_ = + cubic_.CongestionWindowAfterPacketLoss(congestion_window_); + } + // Enforce a minimum congestion window. + if (congestion_window_ < min_congestion_window_) { + congestion_window_ = min_congestion_window_; + } + slowstart_threshold_ = congestion_window_; + largest_sent_at_last_cutback_ = largest_sent_packet_number_; + // 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_; +} + +QuicByteCount TcpCubicSenderPackets::GetCongestionWindow() const { + return congestion_window_ * kDefaultTCPMSS; +} + +QuicByteCount TcpCubicSenderPackets::GetSlowStartThreshold() const { + return slowstart_threshold_ * kDefaultTCPMSS; +} + +// 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 TcpCubicSenderPackets::MaybeIncreaseCwnd( + QuicPacketNumber acked_packet_number, + QuicByteCount /*acked_bytes*/, + QuicByteCount bytes_in_flight) { + QUIC_BUG_IF(InRecovery()) << "Never increase the CWND during recovery."; + // Do not increase the congestion window unless the sender is close to using + // the current window. + if (!IsCwndLimited(bytes_in_flight)) { + cubic_.OnApplicationLimited(); + return; + } + if (congestion_window_ >= max_tcp_congestion_window_) { + return; + } + if (InSlowStart()) { + // TCP slow start, exponential growth, increase by one for each ACK. + ++congestion_window_; + DVLOG(1) << "Slow start; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_; + return; + } + // Congestion avoidance + if (reno_) { + // Classic Reno congestion avoidance. + ++congestion_window_count_; + // Divide by num_connections to smoothly increase the CWND at a faster + // rate than conventional Reno. + if (congestion_window_count_ * num_connections_ >= 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 TcpCubicSenderPackets::HandleRetransmissionTimeout() { + cubic_.Reset(); + slowstart_threshold_ = congestion_window_ / 2; + congestion_window_ = min_congestion_window_; +} + +void TcpCubicSenderPackets::OnConnectionMigration() { + TcpCubicSenderBase::OnConnectionMigration(); + cubic_.Reset(); + congestion_window_count_ = 0; + congestion_window_ = initial_tcp_congestion_window_; + slowstart_threshold_ = initial_max_tcp_congestion_window_; + max_tcp_congestion_window_ = initial_max_tcp_congestion_window_; +} + +CongestionControlType TcpCubicSenderPackets::GetCongestionControlType() const { + return reno_ ? kReno : kCubic; +} + +} // namespace net diff --git a/src/net/quic/congestion_control/tcp_cubic_sender_packets.h b/src/net/quic/congestion_control/tcp_cubic_sender_packets.h new file mode 100644 index 00000000..43a23a41 --- /dev/null +++ b/src/net/quic/congestion_control/tcp_cubic_sender_packets.h @@ -0,0 +1,100 @@ +// 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. +// +// TCP cubic send side congestion algorithm, emulates the behavior of TCP cubic. + +#ifndef NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_PACKETS_H_ +#define NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_PACKETS_H_ + +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "net/base/net_export.h" +#include "net/quic/congestion_control/cubic.h" +#include "net/quic/congestion_control/hybrid_slow_start.h" +#include "net/quic/congestion_control/prr_sender.h" +#include "net/quic/congestion_control/tcp_cubic_sender_base.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 { + +class RttStats; + +namespace test { +class TcpCubicSenderPacketsPeer; +} // namespace test + +class NET_EXPORT_PRIVATE TcpCubicSenderPackets : public TcpCubicSenderBase { + public: + // Reno option and max_tcp_congestion_window are provided for testing. + TcpCubicSenderPackets(const QuicClock* clock, + const RttStats* rtt_stats, + bool reno, + QuicPacketCount initial_tcp_congestion_window, + QuicPacketCount max_tcp_congestion_window, + QuicConnectionStats* stats); + ~TcpCubicSenderPackets() override; + + // Start implementation of SendAlgorithmInterface. + void SetNumEmulatedConnections(int num_connections) override; + void SetMaxCongestionWindow(QuicByteCount max_congestion_window) override; + void OnConnectionMigration() override; + QuicByteCount GetCongestionWindow() const override; + QuicByteCount GetSlowStartThreshold() const override; + CongestionControlType GetCongestionControlType() const override; + // End implementation of SendAlgorithmInterface. + + protected: + // TcpCubicSenderBase methods + void SetCongestionWindowFromBandwidthAndRtt(QuicBandwidth bandwidth, + QuicTime::Delta rtt) override; + void SetCongestionWindowInPackets(QuicPacketCount congestion_window) override; + void SetMinCongestionWindowInPackets( + QuicPacketCount congestion_window) override; + void ExitSlowstart() override; + void OnPacketLost(QuicPacketNumber largest_loss, + QuicByteCount bytes_in_flight) override; + void MaybeIncreaseCwnd(QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount bytes_in_flight) override; + void HandleRetransmissionTimeout() override; + + private: + friend class test::TcpCubicSenderPacketsPeer; + + Cubic cubic_; + + // ACK counter for the Reno implementation. + uint64_t congestion_window_count_; + + // Congestion window in packets. + QuicPacketCount congestion_window_; + + // Minimum congestion window in packets. + QuicPacketCount min_congestion_window_; + + // Slow start congestion window in packets, aka ssthresh. + QuicPacketCount slowstart_threshold_; + + // Maximum number of outstanding packets for tcp. + QuicPacketCount max_tcp_congestion_window_; + + // Initial TCP congestion window. This variable can only be set when this + // algorithm is created. + const QuicPacketCount initial_tcp_congestion_window_; + + // Initial maximum TCP congestion window. This variable can only be set when + // this algorithm is created. + const QuicPacketCount initial_max_tcp_congestion_window_; + + DISALLOW_COPY_AND_ASSIGN(TcpCubicSenderPackets); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_H_ diff --git a/src/net/quic/congestion_control/tcp_loss_algorithm.cc b/src/net/quic/congestion_control/tcp_loss_algorithm.cc deleted file mode 100644 index f493d961..00000000 --- a/src/net/quic/congestion_control/tcp_loss_algorithm.cc +++ /dev/null @@ -1,104 +0,0 @@ -// 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 { - -// 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 due -// to early retransmission. -static const double kEarlyRetransmitLossDelayMultiplier = 1.25; - -} // namespace - -TCPLossAlgorithm::TCPLossAlgorithm() - : loss_detection_timeout_(QuicTime::Zero()) {} - -LossDetectionType TCPLossAlgorithm::GetLossDetectionType() const { - return kNack; -} - -// Uses nack counts to decide when packets are lost. -PacketNumberSet TCPLossAlgorithm::DetectLostPackets( - const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - QuicPacketNumber largest_observed, - const RttStats& rtt_stats) { - PacketNumberSet lost_packets; - loss_detection_timeout_ = QuicTime::Zero(); - QuicTime::Delta early_retransmit_delay = QuicTime::Delta::Max( - QuicTime::Delta::FromMilliseconds(kMinLossDelayMs), - rtt_stats.smoothed_rtt().Multiply(kEarlyRetransmitLossDelayMultiplier)); - - QuicPacketNumber packet_number = unacked_packets.GetLeastUnacked(); - for (QuicUnackedPacketMap::const_iterator it = unacked_packets.begin(); - it != unacked_packets.end() && packet_number <= largest_observed; - ++it, ++packet_number) { - if (!it->in_flight) { - continue; - } - - LOG_IF(DFATAL, it->nack_count == 0 && it->sent_time.IsInitialized()) - << "All packets less than largest observed should have been nacked." - << "packet_number:" << packet_number - << " largest_observed:" << largest_observed; - if (it->nack_count >= kNumberOfNacksBeforeRetransmission) { - lost_packets.insert(packet_number); - continue; - } - - // Immediately lose the packet if it's been an srtt between the sent time - // of it and the largest observed. This speeds recovery from timer based - // retransmissions, such as TLP and RTO, when there may be fewer than - // kNumberOfNacksBeforeRetransmission nacks. - if (it->sent_time.Add(rtt_stats.smoothed_rtt()) < - unacked_packets.GetTransmissionInfo(largest_observed).sent_time) { - lost_packets.insert(packet_number); - 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->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->sent_time.Add(early_retransmit_delay)) { - lost_packets.insert(packet_number); - } else { - // Set the timeout for the earliest retransmittable packet where early - // retransmit applies. - loss_detection_timeout_ = it->sent_time.Add(early_retransmit_delay); - break; - } - } - } - - return lost_packets; -} - -void TCPLossAlgorithm::DetectLosses( - const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - const RttStats& rtt_stats, - SendAlgorithmInterface::CongestionVector* packets_lost) { - LOG(DFATAL) << "DetectLoss is unsupported by TCPLossAlgorithm."; -} - -QuicTime TCPLossAlgorithm::GetLossTimeout() const { - return loss_detection_timeout_; -} - -} // namespace net diff --git a/src/net/quic/congestion_control/tcp_loss_algorithm.h b/src/net/quic/congestion_control/tcp_loss_algorithm.h deleted file mode 100644 index 35f04ce7..00000000 --- a/src/net/quic/congestion_control/tcp_loss_algorithm.h +++ /dev/null @@ -1,54 +0,0 @@ -// 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 -#include - -#include "base/macros.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: - // TCP retransmits after 3 nacks. - static const QuicPacketCount kNumberOfNacksBeforeRetransmission = 3; - - TCPLossAlgorithm(); - ~TCPLossAlgorithm() override {} - - LossDetectionType GetLossDetectionType() const override; - - // Uses nack counts to decide when packets are lost. - PacketNumberSet DetectLostPackets(const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - QuicPacketNumber largest_observed, - const RttStats& rtt_stats) override; - // Unsupported. - void DetectLosses( - const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - const RttStats& rtt_stats, - SendAlgorithmInterface::CongestionVector* packets_lost) override; - - // Returns a non-zero value when the early retransmit timer is active. - 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/src/net/quic/congestion_control/time_loss_algorithm.cc b/src/net/quic/congestion_control/time_loss_algorithm.cc deleted file mode 100644 index fbca9da9..00000000 --- a/src/net/quic/congestion_control/time_loss_algorithm.cc +++ /dev/null @@ -1,81 +0,0 @@ -// 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; -} - -PacketNumberSet TimeLossAlgorithm::DetectLostPackets( - const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - QuicPacketNumber largest_observed, - const RttStats& rtt_stats) { - PacketNumberSet lost_packets; - loss_detection_timeout_ = QuicTime::Zero(); - QuicTime::Delta loss_delay = QuicTime::Delta::Max( - QuicTime::Delta::FromMilliseconds(kMinLossDelayMs), - QuicTime::Delta::Max(rtt_stats.smoothed_rtt(), rtt_stats.latest_rtt()) - .Multiply(kLossDelayMultiplier)); - - QuicPacketNumber packet_number = unacked_packets.GetLeastUnacked(); - for (QuicUnackedPacketMap::const_iterator it = unacked_packets.begin(); - it != unacked_packets.end() && packet_number <= largest_observed; - ++it, ++packet_number) { - if (!it->in_flight) { - continue; - } - LOG_IF(DFATAL, it->nack_count == 0 && it->sent_time.IsInitialized()) - << "All packets less than largest observed should have been nacked." - << "packet_number:" << packet_number - << " largest_observed:" << largest_observed; - - // 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->sent_time.Add(loss_delay); - if (time < when_lost) { - loss_detection_timeout_ = when_lost; - break; - } - lost_packets.insert(packet_number); - } - - return lost_packets; -} - -void TimeLossAlgorithm::DetectLosses( - const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - const RttStats& rtt_stats, - SendAlgorithmInterface::CongestionVector* packets_lost) { - LOG(DFATAL) << "DetectLoss is unsupported by TimeLossAlgorithm."; -} - -// 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/src/net/quic/congestion_control/time_loss_algorithm.h b/src/net/quic/congestion_control/time_loss_algorithm.h deleted file mode 100644 index 6c1359ed..00000000 --- a/src/net/quic/congestion_control/time_loss_algorithm.h +++ /dev/null @@ -1,58 +0,0 @@ -// 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 -#include - -#include "base/macros.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(); - ~TimeLossAlgorithm() override {} - - 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. - PacketNumberSet DetectLostPackets(const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - QuicPacketNumber largest_observed, - const RttStats& rtt_stats) override; - - // Unsupported. - void DetectLosses( - const QuicUnackedPacketMap& unacked_packets, - const QuicTime& time, - const RttStats& rtt_stats, - SendAlgorithmInterface::CongestionVector* packets_lost) 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). - 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/src/net/quic/crypto/aead_base_decrypter.h b/src/net/quic/crypto/aead_base_decrypter.h index 39cc555f..edd110b5 100644 --- a/src/net/quic/crypto/aead_base_decrypter.h +++ b/src/net/quic/crypto/aead_base_decrypter.h @@ -38,7 +38,8 @@ class NET_EXPORT_PRIVATE AeadBaseDecrypter : public QuicDecrypter { // QuicDecrypter implementation bool SetKey(base::StringPiece key) override; bool SetNoncePrefix(base::StringPiece nonce_prefix) override; - bool DecryptPacket(QuicPacketNumber packet_number, + bool DecryptPacket(QuicPathId path_id, + QuicPacketNumber packet_number, const base::StringPiece& associated_data, const base::StringPiece& ciphertext, char* output, diff --git a/src/net/quic/crypto/aead_base_decrypter_nss.cc b/src/net/quic/crypto/aead_base_decrypter_nss.cc index 9036681a..26e72ee6 100644 --- a/src/net/quic/crypto/aead_base_decrypter_nss.cc +++ b/src/net/quic/crypto/aead_base_decrypter_nss.cc @@ -8,6 +8,8 @@ #include "base/memory/scoped_ptr.h" #include "crypto/scoped_nss_types.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_utils.h" using base::StringPiece; @@ -45,7 +47,8 @@ bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -bool AeadBaseDecrypter::DecryptPacket(QuicPacketNumber packet_number, +bool AeadBaseDecrypter::DecryptPacket(QuicPathId path_id, + QuicPacketNumber packet_number, const StringPiece& associated_data, const StringPiece& ciphertext, char* output, @@ -59,7 +62,14 @@ bool AeadBaseDecrypter::DecryptPacket(QuicPacketNumber packet_number, const size_t nonce_size = nonce_prefix_size_ + sizeof(packet_number); DCHECK_LE(nonce_size, sizeof(nonce)); memcpy(nonce, nonce_prefix_, nonce_prefix_size_); - memcpy(nonce + nonce_prefix_size_, &packet_number, sizeof(packet_number)); + if (FLAGS_quic_include_path_id_in_iv) { + uint64_t path_id_packet_number = + QuicUtils::PackPathIdAndPacketNumber(path_id, packet_number); + memcpy(nonce + nonce_prefix_size_, &path_id_packet_number, + sizeof(path_id_packet_number)); + } else { + memcpy(nonce + nonce_prefix_size_, &packet_number, sizeof(packet_number)); + } // NSS 3.14.x incorrectly requires an output buffer at least as long as // the ciphertext (NSS bug diff --git a/src/net/quic/crypto/aead_base_decrypter_openssl.cc b/src/net/quic/crypto/aead_base_decrypter_openssl.cc index 3a4e4648..512b8047 100644 --- a/src/net/quic/crypto/aead_base_decrypter_openssl.cc +++ b/src/net/quic/crypto/aead_base_decrypter_openssl.cc @@ -8,6 +8,8 @@ #include #include "base/memory/scoped_ptr.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_utils.h" using base::StringPiece; @@ -77,7 +79,8 @@ bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -bool AeadBaseDecrypter::DecryptPacket(QuicPacketNumber packet_number, +bool AeadBaseDecrypter::DecryptPacket(QuicPathId path_id, + QuicPacketNumber packet_number, const StringPiece& associated_data, const StringPiece& ciphertext, char* output, @@ -90,7 +93,14 @@ bool AeadBaseDecrypter::DecryptPacket(QuicPacketNumber packet_number, uint8_t nonce[sizeof(nonce_prefix_) + sizeof(packet_number)]; const size_t nonce_size = nonce_prefix_size_ + sizeof(packet_number); memcpy(nonce, nonce_prefix_, nonce_prefix_size_); - memcpy(nonce + nonce_prefix_size_, &packet_number, sizeof(packet_number)); + if (FLAGS_quic_include_path_id_in_iv) { + uint64_t path_id_packet_number = + QuicUtils::PackPathIdAndPacketNumber(path_id, packet_number); + memcpy(nonce + nonce_prefix_size_, &path_id_packet_number, + sizeof(path_id_packet_number)); + } else { + memcpy(nonce + nonce_prefix_size_, &packet_number, sizeof(packet_number)); + } if (!EVP_AEAD_CTX_open( ctx_.get(), reinterpret_cast(output), output_length, max_output_length, reinterpret_cast(nonce), diff --git a/src/net/quic/crypto/aead_base_encrypter.h b/src/net/quic/crypto/aead_base_encrypter.h index 74219d67..33301899 100644 --- a/src/net/quic/crypto/aead_base_encrypter.h +++ b/src/net/quic/crypto/aead_base_encrypter.h @@ -38,7 +38,8 @@ class NET_EXPORT_PRIVATE AeadBaseEncrypter : public QuicEncrypter { // QuicEncrypter implementation bool SetKey(base::StringPiece key) override; bool SetNoncePrefix(base::StringPiece nonce_prefix) override; - bool EncryptPacket(QuicPacketNumber packet_number, + bool EncryptPacket(QuicPathId path_id, + QuicPacketNumber packet_number, base::StringPiece associated_data, base::StringPiece plaintext, char* output, diff --git a/src/net/quic/crypto/aead_base_encrypter_nss.cc b/src/net/quic/crypto/aead_base_encrypter_nss.cc index b586a1d3..c7841e1d 100644 --- a/src/net/quic/crypto/aead_base_encrypter_nss.cc +++ b/src/net/quic/crypto/aead_base_encrypter_nss.cc @@ -8,6 +8,8 @@ #include "base/memory/scoped_ptr.h" #include "crypto/scoped_nss_types.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_utils.h" using base::StringPiece; @@ -117,7 +119,8 @@ bool AeadBaseEncrypter::Encrypt(StringPiece nonce, return true; } -bool AeadBaseEncrypter::EncryptPacket(QuicPacketNumber packet_number, +bool AeadBaseEncrypter::EncryptPacket(QuicPathId path_id, + QuicPacketNumber packet_number, StringPiece associated_data, StringPiece plaintext, char* output, @@ -132,8 +135,15 @@ bool AeadBaseEncrypter::EncryptPacket(QuicPacketNumber packet_number, const size_t nonce_size = nonce_prefix_size_ + sizeof(packet_number); ALIGNAS(4) char nonce_buffer[kMaxNonceSize]; memcpy(nonce_buffer, nonce_prefix_, nonce_prefix_size_); - memcpy(nonce_buffer + nonce_prefix_size_, &packet_number, - sizeof(packet_number)); + if (FLAGS_quic_include_path_id_in_iv) { + uint64_t path_id_packet_number = + QuicUtils::PackPathIdAndPacketNumber(path_id, packet_number); + memcpy(nonce_buffer + nonce_prefix_size_, &path_id_packet_number, + sizeof(path_id_packet_number)); + } else { + memcpy(nonce_buffer + nonce_prefix_size_, &packet_number, + sizeof(packet_number)); + } if (!Encrypt(StringPiece(nonce_buffer, nonce_size), associated_data, plaintext, reinterpret_cast(output))) { return false; diff --git a/src/net/quic/crypto/aead_base_encrypter_openssl.cc b/src/net/quic/crypto/aead_base_encrypter_openssl.cc index 2408cecc..699c60ad 100644 --- a/src/net/quic/crypto/aead_base_encrypter_openssl.cc +++ b/src/net/quic/crypto/aead_base_encrypter_openssl.cc @@ -9,6 +9,8 @@ #include #include "base/memory/scoped_ptr.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_utils.h" using base::StringPiece; @@ -102,7 +104,8 @@ bool AeadBaseEncrypter::Encrypt(StringPiece nonce, return true; } -bool AeadBaseEncrypter::EncryptPacket(QuicPacketNumber packet_number, +bool AeadBaseEncrypter::EncryptPacket(QuicPathId path_id, + QuicPacketNumber packet_number, StringPiece associated_data, StringPiece plaintext, char* output, @@ -117,8 +120,16 @@ bool AeadBaseEncrypter::EncryptPacket(QuicPacketNumber packet_number, const size_t nonce_size = nonce_prefix_size_ + sizeof(packet_number); ALIGNAS(4) char nonce_buffer[kMaxNonceSize]; memcpy(nonce_buffer, nonce_prefix_, nonce_prefix_size_); - memcpy(nonce_buffer + nonce_prefix_size_, &packet_number, - sizeof(packet_number)); + if (FLAGS_quic_include_path_id_in_iv) { + uint64_t path_id_packet_number = + QuicUtils::PackPathIdAndPacketNumber(path_id, packet_number); + memcpy(nonce_buffer + nonce_prefix_size_, &path_id_packet_number, + sizeof(path_id_packet_number)); + } else { + memcpy(nonce_buffer + nonce_prefix_size_, &packet_number, + sizeof(packet_number)); + } + if (!Encrypt(StringPiece(nonce_buffer, nonce_size), associated_data, plaintext, reinterpret_cast(output))) { return false; diff --git a/src/net/quic/crypto/common_cert_set.cc b/src/net/quic/crypto/common_cert_set.cc index b771fa00..b469b68a 100644 --- a/src/net/quic/crypto/common_cert_set.cc +++ b/src/net/quic/crypto/common_cert_set.cc @@ -4,6 +4,8 @@ #include "net/quic/crypto/common_cert_set.h" +#include + #include "base/logging.h" #include "base/macros.h" #include "base/memory/singleton.h" diff --git a/src/net/quic/crypto/common_cert_set.h b/src/net/quic/crypto/common_cert_set.h index b4fbfac0..6d152054 100644 --- a/src/net/quic/crypto/common_cert_set.h +++ b/src/net/quic/crypto/common_cert_set.h @@ -5,7 +5,7 @@ #ifndef NET_QUIC_CRYPTO_COMMON_CERT_SET_H_ #define NET_QUIC_CRYPTO_COMMON_CERT_SET_H_ -#include +#include #include "base/compiler_specific.h" #include "base/strings/string_piece.h" diff --git a/src/net/quic/crypto/crypto_handshake.cc b/src/net/quic/crypto/crypto_handshake.cc index afe59916..23f64afd 100644 --- a/src/net/quic/crypto/crypto_handshake.cc +++ b/src/net/quic/crypto/crypto_handshake.cc @@ -21,9 +21,6 @@ QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters() QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {} -QuicCryptoProof::QuicCryptoProof() : certs(nullptr) {} -QuicCryptoProof::~QuicCryptoProof() {} - CrypterPair::CrypterPair() {} CrypterPair::~CrypterPair() {} diff --git a/src/net/quic/crypto/crypto_handshake.h b/src/net/quic/crypto/crypto_handshake.h index 71792bac..d9d51e31 100644 --- a/src/net/quic/crypto/crypto_handshake.h +++ b/src/net/quic/crypto/crypto_handshake.h @@ -87,8 +87,7 @@ enum HandshakeFailureReason { }; // These errors will be packed into an uint32_t and we don't want to set the -// most -// significant bit, which may be misinterpreted as the sign bit. +// most significant bit, which may be misinterpreted as the sign bit. static_assert(MAX_FAILURE_REASON <= 32, "failure reason out of sync"); // A CrypterPair contains the encrypter and decrypter for an encryption level. @@ -109,6 +108,10 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { QuicTag aead; std::string initial_premaster_secret; std::string forward_secure_premaster_secret; + // initial_subkey_secret is used as the PRK input to the HKDF used when + // performing key extraction that needs to happen before forward-secure keys + // are available. + std::string initial_subkey_secret; // subkey_secret is used as the PRK input to the HKDF used for key extraction. std::string subkey_secret; CrypterPair initial_crypters; @@ -148,18 +151,6 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { bool sct_supported_by_client; }; -struct NET_EXPORT_PRIVATE QuicCryptoProof { - QuicCryptoProof(); - ~QuicCryptoProof(); - - std::string signature; - // QuicCryptoProof does not take ownership of |certs|. - const std::vector* certs; - std::string cert_sct; - // The SCID of the server config whose signature is |signature|. - std::string primary_scid; -}; - // QuicCryptoConfig contains common configuration between clients and servers. class NET_EXPORT_PRIVATE QuicCryptoConfig { public: diff --git a/src/net/quic/crypto/crypto_handshake_message.cc b/src/net/quic/crypto/crypto_handshake_message.cc index b81b4629..5e4e48dd 100644 --- a/src/net/quic/crypto/crypto_handshake_message.cc +++ b/src/net/quic/crypto/crypto_handshake_message.cc @@ -248,6 +248,15 @@ string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { done = true; } break; + case kRCID: + // uint64_t value + if (it->second.size() == 8) { + uint64_t value; + memcpy(&value, it->second.data(), sizeof(value)); + ret += base::Uint64ToString(value); + done = true; + } + break; case kTBKP: case kKEXS: case kAEAD: diff --git a/src/net/quic/crypto/crypto_protocol.h b/src/net/quic/crypto/crypto_protocol.h index 1666050c..5d2f71f5 100644 --- a/src/net/quic/crypto/crypto_protocol.h +++ b/src/net/quic/crypto/crypto_protocol.h @@ -89,12 +89,16 @@ const QuicTag kMIN4 = TAG('M', 'I', 'N', '4'); // Min CWND of 4 packets, const QuicTag kTLPR = TAG('T', 'L', 'P', 'R'); // Tail loss probe delay of // 0.5RTT. const QuicTag kACKD = TAG('A', 'C', 'K', 'D'); // Ack decimation style acking. +const QuicTag kSSLR = TAG('S', 'S', 'L', 'R'); // Slow Start Large Reduction. // Optional support of truncated Connection IDs. If sent by a peer, the value // is the minimum number of bytes allowed for the connection ID sent to the // peer. const QuicTag kTCID = TAG('T', 'C', 'I', 'D'); // Connection ID truncation. +// Multipath option. +const QuicTag kMPTH = TAG('M', 'P', 'T', 'H'); // Enable multipath. + // FEC options const QuicTag kFHDR = TAG('F', 'H', 'D', 'R'); // FEC protect headers const QuicTag kFSTR = TAG('F', 'S', 'T', 'R'); // FEC protect all streams @@ -125,6 +129,7 @@ const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID. // Client hello tags const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce +const QuicTag kNONP = TAG('N', 'O', 'N', 'P'); // The client's proof nonce const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated // encryption algorithms diff --git a/src/net/quic/crypto/crypto_secret_boxer.cc b/src/net/quic/crypto/crypto_secret_boxer.cc index 6a7eecbb..08a43174 100644 --- a/src/net/quic/crypto/crypto_secret_boxer.cc +++ b/src/net/quic/crypto/crypto_secret_boxer.cc @@ -15,6 +15,7 @@ using base::StringPiece; using std::string; +using std::vector; namespace net { @@ -34,21 +35,34 @@ static const size_t kKeySize = 16; // It's not terrible, but it's not a "forget about it" margin. static const size_t kBoxNonceSize = 12; +CryptoSecretBoxer::CryptoSecretBoxer() {} + +CryptoSecretBoxer::~CryptoSecretBoxer() {} + // static size_t CryptoSecretBoxer::GetKeySize() { return kKeySize; } -void CryptoSecretBoxer::SetKey(StringPiece key) { - DCHECK_EQ(kKeySize, key.size()); - key_ = key.as_string(); +void CryptoSecretBoxer::SetKeys(const vector& keys) { + DCHECK(!keys.empty()); + vector copy = keys; + for (const string& key : keys) { + DCHECK_EQ(kKeySize, key.size()); + } + base::AutoLock l(lock_); + keys_.swap(copy); } string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const { scoped_ptr encrypter(new Aes128Gcm12Encrypter()); - if (!encrypter->SetKey(key_)) { - DLOG(DFATAL) << "CryptoSecretBoxer's encrypter->SetKey failed."; - return string(); + { + base::AutoLock l(lock_); + DCHECK_EQ(kKeySize, keys_[0].size()); + if (!encrypter->SetKey(keys_[0])) { + DLOG(DFATAL) << "CryptoSecretBoxer's encrypter->SetKey failed."; + return string(); + } } size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length()); @@ -86,17 +100,25 @@ bool CryptoSecretBoxer::Unbox(StringPiece ciphertext, sizeof(packet_number)); scoped_ptr decrypter(new Aes128Gcm12Decrypter()); - if (!decrypter->SetKey(key_)) { - DLOG(DFATAL) << "CryptoSecretBoxer's decrypter->SetKey failed."; - return false; - } - decrypter->SetNoncePrefix(nonce_prefix); char plaintext[kMaxPacketSize]; size_t plaintext_length = 0; - const bool success = decrypter->DecryptPacket( - packet_number, StringPiece() /* associated data */, ciphertext, plaintext, - &plaintext_length, kMaxPacketSize); - if (!success) { + bool ok = false; + { + base::AutoLock l(lock_); + for (const string& key : keys_) { + if (decrypter->SetKey(key)) { + decrypter->SetNoncePrefix(nonce_prefix); + if (decrypter->DecryptPacket( + /*path_id=*/0u, packet_number, + /*associated data=*/StringPiece(), ciphertext, plaintext, + &plaintext_length, kMaxPacketSize)) { + ok = true; + break; + } + } + } + } + if (!ok) { return false; } diff --git a/src/net/quic/crypto/crypto_secret_boxer.h b/src/net/quic/crypto/crypto_secret_boxer.h index 266b34a5..09ec23c3 100644 --- a/src/net/quic/crypto/crypto_secret_boxer.h +++ b/src/net/quic/crypto/crypto_secret_boxer.h @@ -8,9 +8,11 @@ #include #include +#include #include "base/macros.h" #include "base/strings/string_piece.h" +#include "base/synchronization/lock.h" #include "net/base/net_export.h" namespace net { @@ -22,31 +24,37 @@ class QuicRandom; // thread-safe. class NET_EXPORT_PRIVATE CryptoSecretBoxer { public: - CryptoSecretBoxer() {} + CryptoSecretBoxer(); + ~CryptoSecretBoxer(); // GetKeySize returns the number of bytes in a key. static size_t GetKeySize(); - // SetKey sets the key for this object. This must be done before |Box| or - // |Unbox| are called. |key| must be |GetKeySize()| bytes long. - void SetKey(base::StringPiece key); + // SetKeys sets a std::list of encryption keys. The first key in the std::list + // will be used by |Box|, but all supplied keys will be tried by |Unbox|, to + // handle key skew across the fleet. This must be called before |Box| or + // |Unbox|. Keys must be |GetKeySize()| bytes long. + void SetKeys(const std::vector& keys); // Box encrypts |plaintext| using a random nonce generated from |rand| and // returns the resulting ciphertext. Since an authenticator and nonce are - // included, the result will be slightly larger than |plaintext|. + // included, the result will be slightly larger than |plaintext|. The first + // key in the std::vector supplied to |SetKeys| will be used. std::string Box(QuicRandom* rand, base::StringPiece plaintext) const; // Unbox takes the result of a previous call to |Box| in |ciphertext| and - // authenticates+decrypts it. If |ciphertext| is not authentic then it - // returns false. Otherwise, |out_storage| is used to store the result and - // |out| is set to point into |out_storage| and contains the original - // plaintext. + // authenticates+decrypts it. If |ciphertext| cannot be decrypted with any of + // the supplied keys, the function returns false. Otherwise, |out_storage| is + // used to store the result and |out| is set to point into |out_storage| and + // contains the original plaintext. bool Unbox(base::StringPiece ciphertext, std::string* out_storage, base::StringPiece* out) const; private: - std::string key_; + mutable base::Lock lock_; + // GUARDED_BY(lock_). + std::vector keys_; DISALLOW_COPY_AND_ASSIGN(CryptoSecretBoxer); }; diff --git a/src/net/quic/crypto/crypto_utils.cc b/src/net/quic/crypto/crypto_utils.cc index ec1c46bb..8843e2c6 100644 --- a/src/net/quic/crypto/crypto_utils.cc +++ b/src/net/quic/crypto/crypto_utils.cc @@ -5,7 +5,7 @@ #include "net/quic/crypto/crypto_utils.h" #include "crypto/hkdf.h" -#include "net/base/net_util.h" +#include "net/base/url_util.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/quic_decrypter.h" @@ -48,7 +48,6 @@ void CryptoUtils::GenerateNonce(QuicWallTime now, kNonceSize - bytes_written); } -#if 0 // static bool CryptoUtils::IsValidSNI(StringPiece sni) { // TODO(rtenneti): Support RFC2396 hostname. @@ -80,29 +79,6 @@ string CryptoUtils::NormalizeHostname(const char* hostname) { } return host; } -#else -// TODO(hodduc): We should implement this ! -// static -bool CryptoUtils::IsValidSNI(StringPiece sni) { - return sni.find_last_of('.') != string::npos; -} - -string CryptoUtils::NormalizeHostname(const char* hostname) { - string host(hostname); - - // Walk backwards over the string, stopping at the first trailing dot. - size_t host_end = host.length(); - while (host_end != 0 && host[host_end - 1] == '.') { - host_end--; - } - - // Erase the trailing dots. - if (host_end != host.length()) { - host.erase(host_end, host.length() - host_end); - } - return host; -} -#endif // static bool CryptoUtils::DeriveKeys(StringPiece premaster_secret, diff --git a/src/net/quic/crypto/null_decrypter.cc b/src/net/quic/crypto/null_decrypter.cc index 67709c3d..52217b58 100644 --- a/src/net/quic/crypto/null_decrypter.cc +++ b/src/net/quic/crypto/null_decrypter.cc @@ -6,6 +6,7 @@ #include +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_data_reader.h" #include "net/quic/quic_utils.h" @@ -24,7 +25,8 @@ bool NullDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { return nonce_prefix.empty(); } -bool NullDecrypter::DecryptPacket(QuicPacketNumber /*packet_number*/, +bool NullDecrypter::DecryptPacket(QuicPathId /*path_id*/, + QuicPacketNumber /*packet_number*/, const StringPiece& associated_data, const StringPiece& ciphertext, char* output, @@ -39,7 +41,7 @@ bool NullDecrypter::DecryptPacket(QuicPacketNumber /*packet_number*/, StringPiece plaintext = reader.ReadRemainingPayload(); if (plaintext.length() > max_output_length) { - LOG(DFATAL) << "Output buffer must be larger than the plaintext."; + QUIC_BUG << "Output buffer must be larger than the plaintext."; return false; } if (hash != ComputeHash(associated_data, plaintext)) { diff --git a/src/net/quic/crypto/null_decrypter.h b/src/net/quic/crypto/null_decrypter.h index 64a6a19b..91b58565 100644 --- a/src/net/quic/crypto/null_decrypter.h +++ b/src/net/quic/crypto/null_decrypter.h @@ -28,7 +28,8 @@ class NET_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter { // QuicDecrypter implementation bool SetKey(base::StringPiece key) override; bool SetNoncePrefix(base::StringPiece nonce_prefix) override; - bool DecryptPacket(QuicPacketNumber packet_number, + bool DecryptPacket(QuicPathId path_id, + QuicPacketNumber packet_number, const base::StringPiece& associated_data, const base::StringPiece& ciphertext, char* output, diff --git a/src/net/quic/crypto/null_encrypter.cc b/src/net/quic/crypto/null_encrypter.cc index 470674b1..6d7ef809 100644 --- a/src/net/quic/crypto/null_encrypter.cc +++ b/src/net/quic/crypto/null_encrypter.cc @@ -24,7 +24,8 @@ bool NullEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { return nonce_prefix.empty(); } -bool NullEncrypter::EncryptPacket(QuicPacketNumber /*packet_number*/, +bool NullEncrypter::EncryptPacket(QuicPathId /*path_id*/, + QuicPacketNumber /*packet_number*/, StringPiece associated_data, StringPiece plaintext, char* output, diff --git a/src/net/quic/crypto/null_encrypter.h b/src/net/quic/crypto/null_encrypter.h index c07495e7..77d6e91e 100644 --- a/src/net/quic/crypto/null_encrypter.h +++ b/src/net/quic/crypto/null_encrypter.h @@ -25,7 +25,8 @@ class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter { // QuicEncrypter implementation bool SetKey(base::StringPiece key) override; bool SetNoncePrefix(base::StringPiece nonce_prefix) override; - bool EncryptPacket(QuicPacketNumber packet_number, + bool EncryptPacket(QuicPathId path_id, + QuicPacketNumber packet_number, base::StringPiece associated_data, base::StringPiece plaintext, char* output, diff --git a/src/net/quic/crypto/proof_source.cc b/src/net/quic/crypto/proof_source.cc new file mode 100644 index 00000000..b695725a --- /dev/null +++ b/src/net/quic/crypto/proof_source.cc @@ -0,0 +1,14 @@ +// Copyright (c) 2016 The Chromium 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_source.h" + +namespace net { + +ProofSource::Chain::Chain(const std::vector& certs) + : certs(certs) {} + +ProofSource::Chain::~Chain() {} + +} // namespace net diff --git a/src/net/quic/crypto/proof_source.h b/src/net/quic/crypto/proof_source.h index 235b972b..d2b48590 100644 --- a/src/net/quic/crypto/proof_source.h +++ b/src/net/quic/crypto/proof_source.h @@ -8,15 +8,32 @@ #include #include -#include "net/base/ip_address_number.h" +#include "base/memory/ref_counted.h" #include "net/base/net_export.h" namespace net { +class IPAddress; + // ProofSource is an interface by which a QUIC server can obtain certificate // chains and signatures that prove its identity. class NET_EXPORT_PRIVATE ProofSource { public: + // Chain is a reference-counted wrapper for a std::vector of std::stringified + // certificates. + struct NET_EXPORT_PRIVATE Chain : public base::RefCounted { + explicit Chain(const std::vector& certs); + + const std::vector certs; + + private: + friend class base::RefCounted; + + virtual ~Chain(); + + DISALLOW_COPY_AND_ASSIGN(Chain); + }; + virtual ~ProofSource() {} // GetProof finds a certificate chain for |hostname|, sets |out_certs| to @@ -31,7 +48,8 @@ class NET_EXPORT_PRIVATE ProofSource { // If |ecdsa_ok| is true, the signature may use an ECDSA key. Otherwise, the // signature must use an RSA key. // - // |out_certs| is a pointer to a pointer, not a pointer to an array. + // |out_chain| is reference counted to avoid the (assumed) expense of copying + // out the certificates. // // The number of certificate chains is expected to be small and fixed thus // the ProofSource retains ownership of the contents of |out_certs|. The @@ -48,11 +66,11 @@ class NET_EXPORT_PRIVATE ProofSource { // |out_leaf_cert_sct| points to the signed timestamp (RFC6962) of the leaf // cert. // This function may be called concurrently. - virtual bool GetProof(const IPAddressNumber& server_ip, + virtual bool GetProof(const IPAddress& server_ip, const std::string& hostname, const std::string& server_config, bool ecdsa_ok, - const std::vector** out_certs, + scoped_refptr* out_chain, std::string* out_signature, std::string* out_leaf_cert_sct) = 0; }; diff --git a/src/net/quic/crypto/quic_crypto_client_config.cc b/src/net/quic/crypto/quic_crypto_client_config.cc index 9268ae5b..bb71fcc5 100644 --- a/src/net/quic/crypto/quic_crypto_client_config.cc +++ b/src/net/quic/crypto/quic_crypto_client_config.cc @@ -18,6 +18,9 @@ #include "net/quic/crypto/p256_key_exchange.h" #include "net/quic/crypto/proof_verifier.h" #include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/crypto/quic_random.h" +#include "net/quic/quic_bug_tracker.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" using base::StringPiece; @@ -339,7 +342,7 @@ void QuicCryptoClientConfig::CachedState::InitializeFrom( QuicConnectionId QuicCryptoClientConfig::CachedState::GetNextServerDesignatedConnectionId() { if (server_designated_connection_ids_.empty()) { - LOG(DFATAL) + QUIC_BUG << "Attempting to consume a connection id that was never designated."; return 0; } @@ -350,7 +353,7 @@ QuicCryptoClientConfig::CachedState::GetNextServerDesignatedConnectionId() { string QuicCryptoClientConfig::CachedState::GetNextServerNonce() { if (server_nonces_.empty()) { - LOG(DFATAL) + QUIC_BUG << "Attempting to consume a server nonce that was never designated."; return ""; } @@ -403,6 +406,7 @@ void QuicCryptoClientConfig::FillInchoateClientHello( const QuicServerId& server_id, const QuicVersion preferred_version, const CachedState* cached, + QuicRandom* rand, QuicCryptoNegotiatedParameters* out_params, CryptoHandshakeMessage* out) const { out->set_tag(kCHLO); @@ -419,6 +423,10 @@ void QuicCryptoClientConfig::FillInchoateClientHello( out->SetStringPiece(kUAID, user_agent_id_); } + char proof_nonce[32]; + rand->RandBytes(proof_nonce, arraysize(proof_nonce)); + out->SetStringPiece(kNONP, StringPiece(proof_nonce, arraysize(proof_nonce))); + // Even though this is an inchoate CHLO, send the SCID so that // the STK can be validated by the server. const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); @@ -477,8 +485,8 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( string* error_details) const { DCHECK(error_details != nullptr); - FillInchoateClientHello(server_id, preferred_version, cached, out_params, - out); + FillInchoateClientHello(server_id, preferred_version, cached, rand, + out_params, out); const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); if (!scfg) { @@ -644,9 +652,9 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( scoped_ptr output(new char[encrypted_len]); size_t output_size = 0; if (!crypters.encrypter->EncryptPacket( - 0 /* packet number */, StringPiece() /* associated data */, - cetv_plaintext.AsStringPiece(), output.get(), &output_size, - encrypted_len)) { + kDefaultPathId /* path id */, 0 /* packet number */, + StringPiece() /* associated data */, cetv_plaintext.AsStringPiece(), + output.get(), &output_size, encrypted_len)) { *error_details = "Packet encryption failed"; return QUIC_ENCRYPTION_FAILURE; } @@ -682,11 +690,15 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); hkdf_input.append(out_params->hkdf_input_suffix); - if (!CryptoUtils::DeriveKeys( - out_params->initial_premaster_secret, out_params->aead, - out_params->client_nonce, out_params->server_nonce, hkdf_input, - Perspective::IS_CLIENT, &out_params->initial_crypters, - nullptr /* subkey secret */)) { + string* subkey_secret = nullptr; + if (FLAGS_quic_save_initial_subkey_secret) { + subkey_secret = &out_params->initial_subkey_secret; + } + if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret, + out_params->aead, out_params->client_nonce, + out_params->server_nonce, hkdf_input, + Perspective::IS_CLIENT, + &out_params->initial_crypters, subkey_secret)) { *error_details = "Symmetric key setup failed"; return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; } diff --git a/src/net/quic/crypto/quic_crypto_client_config.h b/src/net/quic/crypto/quic_crypto_client_config.h index 34be5484..9782f103 100644 --- a/src/net/quic/crypto/quic_crypto_client_config.h +++ b/src/net/quic/crypto/quic_crypto_client_config.h @@ -210,6 +210,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { void FillInchoateClientHello(const QuicServerId& server_id, const QuicVersion preferred_version, const CachedState* cached, + QuicRandom* rand, QuicCryptoNegotiatedParameters* out_params, CryptoHandshakeMessage* out) const; diff --git a/src/net/quic/crypto/quic_crypto_server_config.cc b/src/net/quic/crypto/quic_crypto_server_config.cc index c7b83bfb..cd14bc1e 100644 --- a/src/net/quic/crypto/quic_crypto_server_config.cc +++ b/src/net/quic/crypto/quic_crypto_server_config.cc @@ -9,11 +9,12 @@ #include #include "base/macros.h" +#include "base/memory/ref_counted.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "crypto/hkdf.h" #include "crypto/secure_hash.h" -#include "net/base/net_util.h" +#include "net/base/ip_address.h" #include "net/quic/crypto/aes_128_gcm_12_decrypter.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/cert_compressor.h" @@ -35,6 +36,7 @@ #include "net/quic/crypto/strike_register.h" #include "net/quic/crypto/strike_register_client.h" #include "net/quic/proto/source_address_token.pb.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_clock.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" @@ -68,9 +70,9 @@ string DeriveSourceAddressTokenKey(StringPiece source_address_token_secret) { return hkdf.server_write_key().as_string(); } -IPAddressNumber DualstackIPAddress(const IPAddressNumber& ip) { - if (ip.size() == kIPv4AddressSize) { - return ConvertIPv4NumberToIPv6Number(ip); +IPAddress DualstackIPAddress(const IPAddress& ip) { + if (ip.IsIPv4()) { + return ConvertIPv4ToIPv4MappedIPv6(ip); } return ip; } @@ -84,7 +86,7 @@ class ValidateClientHelloHelper { : result_(result), done_cb_(done_cb) {} ~ValidateClientHelloHelper() { - LOG_IF(DFATAL, done_cb_ != nullptr) + QUIC_BUG_IF(done_cb_ != nullptr) << "Deleting ValidateClientHelloHelper with a pending callback."; } @@ -99,7 +101,7 @@ class ValidateClientHelloHelper { private: void DetachCallback() { - LOG_IF(DFATAL, done_cb_ == nullptr) << "Callback already detached."; + QUIC_BUG_IF(done_cb_ == nullptr) << "Callback already detached."; done_cb_ = nullptr; } @@ -148,7 +150,7 @@ class VerifyNonceIsValidAndUniqueCallback break; case NONCE_OK: default: - LOG(DFATAL) << "Unexpected client nonce error: " << nonce_error; + QUIC_BUG << "Unexpected client nonce error: " << nonce_error; client_nonce_error = CLIENT_NONCE_UNKNOWN_FAILURE; break; } @@ -167,7 +169,7 @@ class VerifyNonceIsValidAndUniqueCallback // static const char QuicCryptoServerConfig::TESTING[] = "secret string for testing"; -ClientHelloInfo::ClientHelloInfo(const IPAddressNumber& in_client_ip, +ClientHelloInfo::ClientHelloInfo(const IPAddress& in_client_ip, QuicWallTime in_now) : client_ip(in_client_ip), now(in_now), valid_source_address_token(false) {} @@ -179,7 +181,7 @@ PrimaryConfigChangedCallback::~PrimaryConfigChangedCallback() {} ValidateClientHelloResultCallback::Result::Result( const CryptoHandshakeMessage& in_client_hello, - IPAddressNumber in_client_ip, + IPAddress in_client_ip, QuicWallTime in_now) : client_hello(in_client_hello), info(in_client_ip, in_now), @@ -223,8 +225,8 @@ QuicCryptoServerConfig::QuicCryptoServerConfig( server_nonce_strike_register_window_secs_(120), enable_serving_sct_(false) { DCHECK(proof_source_.get()); - default_source_address_token_boxer_.SetKey( - DeriveSourceAddressTokenKey(source_address_token_secret)); + default_source_address_token_boxer_.SetKeys( + {DeriveSourceAddressTokenKey(source_address_token_secret)}); // Generate a random key and orbit for server nonces. server_nonce_entropy->RandBytes(server_nonce_orbit_, @@ -233,8 +235,8 @@ QuicCryptoServerConfig::QuicCryptoServerConfig( scoped_ptr key_bytes(new uint8_t[key_size]); server_nonce_entropy->RandBytes(key_bytes.get(), key_size); - server_nonce_boxer_.SetKey( - StringPiece(reinterpret_cast(key_bytes.get()), key_size)); + server_nonce_boxer_.SetKeys( + {string(reinterpret_cast(key_bytes.get()), key_size)}); } QuicCryptoServerConfig::~QuicCryptoServerConfig() { @@ -289,11 +291,15 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::GenerateConfig( } else { msg.SetTaglist(kKEXS, kC255, 0); } - if (FLAGS_quic_use_rfc7539 && - ChaCha20Poly1305Rfc7539Encrypter::IsSupported()) { - msg.SetTaglist(kAEAD, kAESG, kCC12, kCC20, 0); + if (FLAGS_quic_crypto_server_config_default_has_chacha20) { + if (FLAGS_quic_use_rfc7539 && + ChaCha20Poly1305Rfc7539Encrypter::IsSupported()) { + msg.SetTaglist(kAEAD, kAESG, kCC12, kCC20, 0); + } else { + msg.SetTaglist(kAEAD, kAESG, kCC12, 0); + } } else { - msg.SetTaglist(kAEAD, kAESG, kCC12, 0); + msg.SetTaglist(kAEAD, kAESG, 0); } msg.SetStringPiece(kPUBS, encoded_public_values); @@ -474,6 +480,11 @@ bool QuicCryptoServerConfig::SetConfigs( return ok; } +void QuicCryptoServerConfig::SetDefaultSourceAddressTokenKeys( + const vector& keys) { + default_source_address_token_boxer_.SetKeys(keys); +} + void QuicCryptoServerConfig::GetConfigIds(vector* scids) const { base::AutoLock locked(configs_lock_); for (ConfigMap::const_iterator it = configs_.begin(); it != configs_.end(); @@ -484,8 +495,8 @@ void QuicCryptoServerConfig::GetConfigIds(vector* scids) const { void QuicCryptoServerConfig::ValidateClientHello( const CryptoHandshakeMessage& client_hello, - const IPAddressNumber& client_ip, - const IPAddressNumber& server_ip, + const IPAddress& client_ip, + const IPAddress& server_ip, QuicVersion version, const QuicClock* clock, QuicCryptoProof* crypto_proof, @@ -521,7 +532,7 @@ void QuicCryptoServerConfig::ValidateClientHello( requested_config = GetConfigWithScid(requested_scid); primary_config = primary_config_; - crypto_proof->primary_scid = primary_config->id; + crypto_proof->config = primary_config_; } if (result->error_code == QUIC_NO_ERROR) { @@ -535,7 +546,7 @@ void QuicCryptoServerConfig::ValidateClientHello( QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( const ValidateClientHelloResultCallback::Result& validate_chlo_result, QuicConnectionId connection_id, - const IPAddressNumber& server_ip, + const IPAddress& server_ip, const IPEndPoint& client_address, QuicVersion version, const QuicVersionVector& supported_versions, @@ -579,20 +590,9 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( DCHECK_EQ(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. - if (FLAGS_quic_use_primary_config_for_proof) { - primary_config = GetConfigWithScid(crypto_proof->primary_scid); - if (!primary_config) { - *error_details = "Configuration not found"; - LOG(DFATAL) << "Primary config not found"; - return QUIC_CRYPTO_INTERNAL_ERROR; - } - } else { - primary_config = primary_config_; - } - + // Use the config that the client requested in order to do key-agreement. + // Otherwise give it a copy of |primary_config_| to use. + primary_config = crypto_proof->config; requested_config = GetConfigWithScid(requested_scid); } @@ -607,10 +607,10 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( bool x509_ecdsa_supported = false; ParseProofDemand(client_hello, &x509_supported, &x509_ecdsa_supported); DCHECK(proof_source_.get()); - if (!crypto_proof->certs && + if (!crypto_proof->chain && !proof_source_->GetProof(server_ip, info.sni.as_string(), primary_config->serialized, x509_ecdsa_supported, - &crypto_proof->certs, &crypto_proof->signature, + &crypto_proof->chain, &crypto_proof->signature, &crypto_proof->cert_sct)) { return QUIC_HANDSHAKE_FAILED; } @@ -706,11 +706,11 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( hkdf_suffix.append(requested_config->serialized); DCHECK(proof_source_.get()); if (version > QUIC_VERSION_25) { - if (crypto_proof->certs->empty()) { + if (crypto_proof->chain->certs.empty()) { *error_details = "Failed to get certs"; return QUIC_CRYPTO_INTERNAL_ERROR; } - hkdf_suffix.append(crypto_proof->certs->at(0)); + hkdf_suffix.append(crypto_proof->chain->certs.at(0)); } StringPiece cetv_ciphertext; @@ -743,11 +743,12 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( char plaintext[kMaxPacketSize]; size_t plaintext_length = 0; const bool success = crypters.decrypter->DecryptPacket( - 0 /* packet number */, StringPiece() /* associated data */, - cetv_ciphertext, plaintext, &plaintext_length, kMaxPacketSize); + kDefaultPathId, 0 /* packet number */, + StringPiece() /* associated data */, cetv_ciphertext, plaintext, + &plaintext_length, kMaxPacketSize); if (!success) { *error_details = "CETV decryption failure"; - return QUIC_PACKET_TOO_LARGE; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } scoped_ptr cetv( CryptoFramer::ParseMessage(StringPiece(plaintext, plaintext_length))); @@ -774,10 +775,14 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); hkdf_input.append(hkdf_suffix); - if (!CryptoUtils::DeriveKeys( - params->initial_premaster_secret, params->aead, info.client_nonce, - info.server_nonce, hkdf_input, Perspective::IS_SERVER, - ¶ms->initial_crypters, nullptr /* subkey secret */)) { + string* subkey_secret = nullptr; + if (FLAGS_quic_save_initial_subkey_secret) { + subkey_secret = ¶ms->initial_subkey_secret; + } + if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead, + info.client_nonce, info.server_nonce, hkdf_input, + Perspective::IS_SERVER, + ¶ms->initial_crypters, subkey_secret)) { *error_details = "Symmetric key setup failed"; return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; } @@ -890,9 +895,9 @@ void QuicCryptoServerConfig::SelectNewPrimaryConfig( if (configs.empty()) { if (primary_config_.get()) { - LOG(DFATAL) << "No valid QUIC server config. Keeping the current config."; + QUIC_BUG << "No valid QUIC server config. Keeping the current config."; } else { - LOG(DFATAL) << "No valid QUIC server config."; + QUIC_BUG << "No valid QUIC server config."; } return; } @@ -962,7 +967,7 @@ void QuicCryptoServerConfig::SelectNewPrimaryConfig( } void QuicCryptoServerConfig::EvaluateClientHello( - const IPAddressNumber& server_ip, + const IPAddress& server_ip, QuicVersion version, const uint8_t* primary_orbit, scoped_refptr requested_config, @@ -1055,15 +1060,10 @@ void QuicCryptoServerConfig::EvaluateClientHello( bool x509_supported = false; bool x509_ecdsa_supported = false; ParseProofDemand(client_hello, &x509_supported, &x509_ecdsa_supported); - string serialized_config; - if (FLAGS_quic_use_primary_config_for_proof) { - serialized_config = primary_config->serialized; - } else { - serialized_config = requested_config->serialized; - } + string serialized_config = primary_config->serialized; if (!proof_source_->GetProof(server_ip, info->sni.as_string(), serialized_config, x509_ecdsa_supported, - &crypto_proof->certs, &crypto_proof->signature, + &crypto_proof->chain, &crypto_proof->signature, &crypto_proof->cert_sct)) { found_error = true; info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE); @@ -1090,8 +1090,8 @@ void QuicCryptoServerConfig::EvaluateClientHello( // Server nonce is optional, and used for key derivation if present. client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce); - if (version > QUIC_VERSION_26) { - DVLOG(1) << "No 0-RTT replay protection in QUIC_VERSION_27 and higher."; + if (version > QUIC_VERSION_30) { + DVLOG(1) << "No 0-RTT replay protection in QUIC_VERSION_31 and higher."; // If the server nonce is empty and we're requiring handshake confirmation // for DoS reasons then we must reject the CHLO. if (FLAGS_quic_require_handshake_confirmation && @@ -1142,17 +1142,28 @@ void QuicCryptoServerConfig::EvaluateClientHello( base::AutoLock locked(strike_register_client_lock_); if (strike_register_client_.get() == nullptr) { - strike_register_client_.reset(new LocalStrikeRegisterClient( - strike_register_max_entries_, - static_cast(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 (!FLAGS_require_strike_register_or_server_nonce) { + strike_register_client_.reset(new LocalStrikeRegisterClient( + strike_register_max_entries_, + static_cast(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(); } + if (!strike_register_client) { + // Either a valid server nonces or a strike register is required. + // Since neither are present, reject the handshake which will send a + // server nonce to the client. + info->reject_reasons.push_back(SERVER_NONCE_REQUIRED_FAILURE); + helper.ValidationComplete(QUIC_NO_ERROR, ""); + return; + } + strike_register_client->VerifyNonceIsValidAndUnique( info->client_nonce, info->now, new VerifyNonceIsValidAndUniqueCallback(client_hello_state, done_cb)); @@ -1162,8 +1173,8 @@ void QuicCryptoServerConfig::EvaluateClientHello( bool QuicCryptoServerConfig::BuildServerConfigUpdateMessage( QuicVersion version, const SourceAddressTokens& previous_source_address_tokens, - const IPAddressNumber& server_ip, - const IPAddressNumber& client_ip, + const IPAddress& server_ip, + const IPAddress& client_ip, const QuicClock* clock, QuicRandom* rand, const QuicCryptoNegotiatedParameters& params, @@ -1178,19 +1189,19 @@ bool QuicCryptoServerConfig::BuildServerConfigUpdateMessage( previous_source_address_tokens, client_ip, rand, clock->WallNow(), cached_network_params)); - const vector* certs; + scoped_refptr chain; string signature; string cert_sct; if (!proof_source_->GetProof( server_ip, params.sni, primary_config_->serialized, - params.x509_ecdsa_supported, &certs, &signature, &cert_sct)) { + params.x509_ecdsa_supported, &chain, &signature, &cert_sct)) { DVLOG(1) << "Server: failed to get proof."; return false; } const string compressed = CertCompressor::CompressChain( - *certs, params.client_common_set_hashes, params.client_cached_cert_hashes, - primary_config_->common_cert_sets); + chain->certs, params.client_common_set_hashes, + params.client_cached_cert_hashes, primary_config_->common_cert_sets); out->SetStringPiece(kCertificateTag, compressed); out->SetStringPiece(kPROF, signature); @@ -1258,7 +1269,7 @@ void QuicCryptoServerConfig::BuildRejection( } const string compressed = CertCompressor::CompressChain( - *crypto_proof.certs, params->client_common_set_hashes, + crypto_proof.chain->certs, params->client_common_set_hashes, params->client_cached_cert_hashes, config.common_cert_sets); // kREJOverheadBytes is a very rough estimate of how much of a REJ @@ -1315,8 +1326,8 @@ QuicCryptoServerConfig::ParseConfigProtobuf( } else { // Create override boxer instance. CryptoSecretBoxer* boxer = new CryptoSecretBoxer; - boxer->SetKey(DeriveSourceAddressTokenKey( - protobuf->source_address_token_secret_override())); + boxer->SetKeys({DeriveSourceAddressTokenKey( + protobuf->source_address_token_secret_override())}); config->source_address_token_boxer_storage.reset(boxer); config->source_address_token_boxer = boxer; } @@ -1546,7 +1557,7 @@ void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb( string QuicCryptoServerConfig::NewSourceAddressToken( const Config& config, const SourceAddressTokens& previous_tokens, - const IPAddressNumber& ip, + const IPAddress& ip, QuicRandom* rand, QuicWallTime now, const CachedNetworkParameters* cached_network_params) const { @@ -1613,7 +1624,7 @@ HandshakeFailureReason QuicCryptoServerConfig::ParseSourceAddressToken( HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressTokens( const SourceAddressTokens& source_address_tokens, - const IPAddressNumber& ip, + const IPAddress& ip, QuicWallTime now, CachedNetworkParameters* cached_network_params) const { HandshakeFailureReason reason = @@ -1632,7 +1643,7 @@ HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressTokens( HandshakeFailureReason QuicCryptoServerConfig::ValidateSingleSourceAddressToken( const SourceAddressToken& source_address_token, - const IPAddressNumber& ip, + const IPAddress& ip, QuicWallTime now) const { if (source_address_token.ip() != IPAddressToPackedString(DualstackIPAddress(ip))) { @@ -1702,7 +1713,7 @@ HandshakeFailureReason QuicCryptoServerConfig::ValidateServerNonce( if (plaintext.size() != kServerNoncePlaintextSize) { // This should never happen because the value decrypted correctly. - LOG(DFATAL) << "Seemingly valid server nonce had incorrect length."; + QUIC_BUG << "Seemingly valid server nonce had incorrect length."; return SERVER_NONCE_INVALID_FAILURE; } @@ -1742,7 +1753,7 @@ HandshakeFailureReason QuicCryptoServerConfig::ValidateServerNonce( case STRIKE_REGISTER_TIMEOUT: case STRIKE_REGISTER_FAILURE: default: - LOG(DFATAL) << "Unexpected server nonce error: " << nonce_error; + QUIC_BUG << "Unexpected server nonce error: " << nonce_error; return SERVER_NONCE_NOT_UNIQUE_FAILURE; } } @@ -1750,7 +1761,7 @@ HandshakeFailureReason QuicCryptoServerConfig::ValidateServerNonce( bool QuicCryptoServerConfig::ValidateExpectedLeafCertificate( const CryptoHandshakeMessage& client_hello, const QuicCryptoProof& crypto_proof) const { - if (crypto_proof.certs->empty()) { + if (crypto_proof.chain->certs.empty()) { return false; } @@ -1758,7 +1769,7 @@ bool QuicCryptoServerConfig::ValidateExpectedLeafCertificate( if (client_hello.GetUint64(kXLCT, &hash_from_client) != QUIC_NO_ERROR) { return false; } - return CryptoUtils::ComputeLeafCertHash(crypto_proof.certs->at(0)) == + return CryptoUtils::ComputeLeafCertHash(crypto_proof.chain->certs.at(0)) == hash_from_client; } @@ -1799,4 +1810,6 @@ QuicCryptoServerConfig::Config::~Config() { STLDeleteElements(&key_exchanges); } +QuicCryptoProof::QuicCryptoProof() {} +QuicCryptoProof::~QuicCryptoProof() {} } // namespace net diff --git a/src/net/quic/crypto/quic_crypto_server_config.h b/src/net/quic/crypto/quic_crypto_server_config.h index 0aba62e2..617779c9 100644 --- a/src/net/quic/crypto/quic_crypto_server_config.h +++ b/src/net/quic/crypto/quic_crypto_server_config.h @@ -17,13 +17,14 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string_piece.h" #include "base/synchronization/lock.h" -#include "net/base/ip_address_number.h" +#include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" #include "net/base/net_export.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_handshake_message.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/crypto_secret_boxer.h" +#include "net/quic/crypto/proof_source.h" #include "net/quic/proto/cached_network_parameters.pb.h" #include "net/quic/proto/source_address_token.pb.h" #include "net/quic/quic_time.h" @@ -41,15 +42,16 @@ class QuicRandom; class QuicServerConfigProtobuf; class StrikeRegister; class StrikeRegisterClient; +struct QuicCryptoProof; // ClientHelloInfo contains information about a client hello message that is // only kept for as long as it's being processed. struct ClientHelloInfo { - ClientHelloInfo(const IPAddressNumber& in_client_ip, QuicWallTime in_now); + ClientHelloInfo(const IPAddress& in_client_ip, QuicWallTime in_now); ~ClientHelloInfo(); // Inputs to EvaluateClientHello. - const IPAddressNumber client_ip; + const IPAddress client_ip; const QuicWallTime now; // Outputs from EvaluateClientHello. @@ -87,7 +89,7 @@ class NET_EXPORT_PRIVATE ValidateClientHelloResultCallback { // its validity. Can be interpreted by calling ProcessClientHello. struct Result { Result(const CryptoHandshakeMessage& in_client_hello, - IPAddressNumber in_client_ip, + IPAddress in_client_ip, QuicWallTime in_now); ~Result(); @@ -192,6 +194,14 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { bool SetConfigs(const std::vector& protobufs, QuicWallTime now); + // SetDefaultSourceAddressTokenKeys sets the keys to be tried, in order, + // when decrypting a source address token. This modifies only the default + // boxer, which is to say, it is a no-op if a key was specified in the Config. + // Note that these keys are used *without* passing them through a KDF, in + // contradistinction to the |source_address_token_secret| argument to the + // constructor. + void SetDefaultSourceAddressTokenKeys(const std::vector& keys); + // Get the server config ids for all known configs. void GetConfigIds(std::vector* scids) const; @@ -219,8 +229,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // once, either under the current call stack, or after the // completion of an asynchronous operation. void ValidateClientHello(const CryptoHandshakeMessage& client_hello, - const IPAddressNumber& client_ip, - const IPAddressNumber& server_ip, + const IPAddress& client_ip, + const IPAddress& server_ip, QuicVersion version, const QuicClock* clock, QuicCryptoProof* crypto_proof, @@ -256,7 +266,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { QuicErrorCode ProcessClientHello( const ValidateClientHelloResultCallback::Result& validate_chlo_result, QuicConnectionId connection_id, - const IPAddressNumber& server_ip, + const IPAddress& server_ip, const IPEndPoint& client_address, QuicVersion version, const QuicVersionVector& supported_versions, @@ -278,8 +288,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { bool BuildServerConfigUpdateMessage( QuicVersion version, const SourceAddressTokens& previous_source_address_tokens, - const IPAddressNumber& server_ip, - const IPAddressNumber& client_ip, + const IPAddress& server_ip, + const IPAddress& client_ip, const QuicClock* clock, QuicRandom* rand, const QuicCryptoNegotiatedParameters& params, @@ -361,6 +371,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { private: friend class test::QuicCryptoServerConfigPeer; + friend struct QuicCryptoProof; // Config represents a server config: a collection of preferences and // Diffie-Hellman public values. @@ -444,7 +455,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // whether it can be shown to be fresh (i.e. not a replay). The results are // written to |info|. void EvaluateClientHello( - const IPAddressNumber& server_ip, + const IPAddress& server_ip, QuicVersion version, const uint8_t* primary_orbit, scoped_refptr requested_config, @@ -476,7 +487,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { std::string NewSourceAddressToken( const Config& config, const SourceAddressTokens& previous_tokens, - const IPAddressNumber& ip, + const IPAddress& ip, QuicRandom* rand, QuicWallTime now, const CachedNetworkParameters* cached_network_params) const; @@ -497,7 +508,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // token contains a CachedNetworkParameters proto. HandshakeFailureReason ValidateSourceAddressTokens( const SourceAddressTokens& tokens, - const IPAddressNumber& ip, + const IPAddress& ip, QuicWallTime now, CachedNetworkParameters* cached_network_params) const; @@ -507,7 +518,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // for failure. HandshakeFailureReason ValidateSingleSourceAddressToken( const SourceAddressToken& token, - const IPAddressNumber& ip, + const IPAddress& ip, QuicWallTime now) const; // Returns HANDSHAKE_OK if the source address token in |token| is a timely @@ -621,6 +632,19 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerConfig); }; +struct NET_EXPORT_PRIVATE QuicCryptoProof { + QuicCryptoProof(); + ~QuicCryptoProof(); + + std::string signature; + scoped_refptr chain; + std::string cert_sct; + // The server config that is used for this proof (and the rest of the + // request). + scoped_refptr config; + std::string primary_scid; +}; + } // namespace net #endif // NET_QUIC_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_ diff --git a/src/net/quic/crypto/quic_decrypter.h b/src/net/quic/crypto/quic_decrypter.h index 4363e709..3e55170d 100644 --- a/src/net/quic/crypto/quic_decrypter.h +++ b/src/net/quic/crypto/quic_decrypter.h @@ -50,7 +50,8 @@ class NET_EXPORT_PRIVATE QuicDecrypter { // to form the nonce. // TODO(wtc): add a way for DecryptPacket to report decryption failure due // to non-authentic inputs, as opposed to other reasons for failure. - virtual bool DecryptPacket(QuicPacketNumber packet_number, + virtual bool DecryptPacket(QuicPathId path_id, + QuicPacketNumber packet_number, const base::StringPiece& associated_data, const base::StringPiece& ciphertext, char* output, diff --git a/src/net/quic/crypto/quic_encrypter.h b/src/net/quic/crypto/quic_encrypter.h index 72701743..a4cc9efe 100644 --- a/src/net/quic/crypto/quic_encrypter.h +++ b/src/net/quic/crypto/quic_encrypter.h @@ -48,7 +48,8 @@ class NET_EXPORT_PRIVATE QuicEncrypter { // SetNoncePrefix() to form the nonce. |output| must not overlap with // |associated_data|. If |output| overlaps with |plaintext| then // |plaintext| must be <= |output|. - virtual bool EncryptPacket(QuicPacketNumber packet_number, + virtual bool EncryptPacket(QuicPathId path_id, + QuicPacketNumber packet_number, base::StringPiece associated_data, base::StringPiece plaintext, char* output, diff --git a/src/net/quic/crypto/quic_random.cc b/src/net/quic/crypto/quic_random.cc index ad130a08..cc8ef276 100644 --- a/src/net/quic/crypto/quic_random.cc +++ b/src/net/quic/crypto/quic_random.cc @@ -8,6 +8,7 @@ #include "base/macros.h" #include "base/memory/singleton.h" #include "crypto/random.h" +#include "net/quic/quic_bug_tracker.h" namespace net { diff --git a/src/net/quic/iovector.cc b/src/net/quic/iovector.cc index a6d2c617..effa52d4 100644 --- a/src/net/quic/iovector.cc +++ b/src/net/quic/iovector.cc @@ -8,6 +8,8 @@ namespace net { IOVector::IOVector() {} +IOVector::IOVector(const IOVector& other) = default; + IOVector::~IOVector() {} } // namespace net diff --git a/src/net/quic/iovector.h b/src/net/quic/iovector.h index 0ea57bb0..cf77a9ac 100644 --- a/src/net/quic/iovector.h +++ b/src/net/quic/iovector.h @@ -62,6 +62,7 @@ class NET_EXPORT_PRIVATE IOVector { // Provide a default constructor so it'll never be inhibited by adding other // constructors. IOVector(); + IOVector(const IOVector& other); ~IOVector(); // Provides a way to convert system call-like iovec representation to diff --git a/src/net/quic/quic_ack_listener_interface.h b/src/net/quic/quic_ack_listener_interface.h deleted file mode 100644 index c2bd6fee..00000000 --- a/src/net/quic/quic_ack_listener_interface.h +++ /dev/null @@ -1,38 +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_QUIC_ACK_LISTENER_INTERFACE_H_ -#define NET_QUIC_QUIC_ACK_LISTENER_INTERFACE_H_ - -#include "base/memory/ref_counted.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_time.h" - -namespace net { - -// Pure virtual class to listen for packet acknowledgements. -class NET_EXPORT_PRIVATE QuicAckListenerInterface - : public base::RefCounted { - public: - QuicAckListenerInterface() {} - - // Called when a packet is acked. Called once per packet. - // |acked_bytes| is the number of data bytes acked. - virtual void OnPacketAcked(int acked_bytes, - QuicTime::Delta delta_largest_observed) = 0; - - // Called when a packet is retransmitted. Called once per packet. - // |retransmitted_bytes| is the number of data bytes retransmitted. - virtual void OnPacketRetransmitted(int retransmitted_bytes) = 0; - - protected: - friend class base::RefCounted; - - // Delegates are ref counted. - virtual ~QuicAckListenerInterface() {} -}; - -} // namespace net - -#endif // NET_QUIC_QUIC_ACK_LISTENER_INTERFACE_H_ diff --git a/src/net/quic/quic_alarm.cc b/src/net/quic/quic_alarm.cc index 6df64926..b978a146 100644 --- a/src/net/quic/quic_alarm.cc +++ b/src/net/quic/quic_alarm.cc @@ -8,8 +8,8 @@ namespace net { -QuicAlarm::QuicAlarm(Delegate* delegate) - : delegate_(delegate), deadline_(QuicTime::Zero()) {} +QuicAlarm::QuicAlarm(QuicArenaScopedPtr delegate) + : delegate_(std::move(delegate)), deadline_(QuicTime::Zero()) {} QuicAlarm::~QuicAlarm() {} @@ -49,9 +49,11 @@ void QuicAlarm::Fire() { deadline_ = QuicTime::Zero(); QuicTime deadline = delegate_->OnAlarm(); - // delegate_->OnAlarm() might call Set(), in which case deadline_ will - // already contain the new value, so don't overwrite it. - if (!deadline_.IsInitialized() && deadline.IsInitialized()) { + // delegate_->OnAlarm() might call Set(), in which case deadline_ + // will already contain the new value, so don't overwrite it. Also, + // OnAlarm() might delete |this| so check |deadline| before + // |deadline_|. + if (deadline.IsInitialized() && !deadline_.IsInitialized()) { Set(deadline); } } diff --git a/src/net/quic/quic_alarm.h b/src/net/quic/quic_alarm.h index 48cd5ef0..a775cfb5 100644 --- a/src/net/quic/quic_alarm.h +++ b/src/net/quic/quic_alarm.h @@ -8,6 +8,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "net/base/net_export.h" +#include "net/quic/quic_arena_scoped_ptr.h" #include "net/quic/quic_time.h" namespace net { @@ -29,7 +30,7 @@ class NET_EXPORT_PRIVATE QuicAlarm { virtual QuicTime OnAlarm() = 0; }; - explicit QuicAlarm(Delegate* delegate); + explicit QuicAlarm(QuicArenaScopedPtr delegate); virtual ~QuicAlarm(); // Sets the alarm to fire at |deadline|. Must not be called while @@ -72,7 +73,7 @@ class NET_EXPORT_PRIVATE QuicAlarm { void Fire(); private: - scoped_ptr delegate_; + QuicArenaScopedPtr delegate_; QuicTime deadline_; DISALLOW_COPY_AND_ASSIGN(QuicAlarm); diff --git a/src/net/quic/quic_arena_scoped_ptr.h b/src/net/quic/quic_arena_scoped_ptr.h new file mode 100644 index 00000000..5083036f --- /dev/null +++ b/src/net/quic/quic_arena_scoped_ptr.h @@ -0,0 +1,210 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// unique_ptr-style pointer that stores values that may be from an arena. Takes +// up the same storage as the platform's native pointer type. Takes ownership +// of the value it's constructed with; if holding a value in an arena, and the +// type has a non-trivial destructor, the arena must outlive the +// QuicArenaScopedPtr. Does not support array overloads. + +#ifndef NET_QUIC_QUIC_ARENA_SCOPED_PTR_H_ +#define NET_QUIC_QUIC_ARENA_SCOPED_PTR_H_ + +#include // for uintptr_t + +#include "base/logging.h" +#include "base/macros.h" +#include "net/quic/quic_utils.h" + +namespace net { + +template +class QuicArenaScopedPtr { + static_assert(QUIC_ALIGN_OF(T*) > 1, + "QuicArenaScopedPtr can only store objects that are aligned to " + "greater than 1 byte."); + + public: + // Constructs an empty QuicArenaScopedPtr. + QuicArenaScopedPtr(); + + // Constructs a QuicArenaScopedPtr referencing the heap-allocated memory + // provided. + explicit QuicArenaScopedPtr(T* value); + + template + QuicArenaScopedPtr(QuicArenaScopedPtr&& other); // NOLINT + template + QuicArenaScopedPtr& operator=(QuicArenaScopedPtr&& other); + ~QuicArenaScopedPtr(); + + // Returns a pointer to the value. + T* get() const; + + // Returns a reference to the value. + T& operator*() const; + + // Returns a pointer to the value. + T* operator->() const; + + // Swaps the value of this pointer with |other|. + void swap(QuicArenaScopedPtr& other); + + // Resets the held value to |value|. + void reset(T* value = nullptr); + + // Returns true if |this| came from an arena. Primarily exposed for testing + // and assertions. + bool is_from_arena(); + + private: + // Friends with other derived types of QuicArenaScopedPtr, to support the + // derived-types case. + template + friend class QuicArenaScopedPtr; + // Also befriend all known arenas, only to prevent misuse. + template + friend class QuicOneBlockArena; + + // Tag to denote that a QuicArenaScopedPtr is being explicitly created by an + // arena. + enum class ConstructFrom { kHeap, kArena }; + + // Constructs a QuicArenaScopedPtr with the given representation. + QuicArenaScopedPtr(void* value, ConstructFrom from); + + // Low-order bits of value_ that determine if the pointer came from an arena. + static const uintptr_t kFromArenaMask = 0x1; + + // Every platform we care about has at least 4B aligned integers, so store the + // is_from_arena bit in the least significant bit. + void* value_; + + DISALLOW_COPY_AND_ASSIGN(QuicArenaScopedPtr); +}; + +template +bool operator==(const QuicArenaScopedPtr& left, + const QuicArenaScopedPtr& right) { + return left.get() == right.get(); +} + +template +bool operator!=(const QuicArenaScopedPtr& left, + const QuicArenaScopedPtr& right) { + return left.get() != right.get(); +} + +template +bool operator==(std::nullptr_t, const QuicArenaScopedPtr& right) { + return nullptr == right.get(); +} + +template +bool operator!=(std::nullptr_t, const QuicArenaScopedPtr& right) { + return nullptr != right.get(); +} + +template +bool operator==(const QuicArenaScopedPtr& left, std::nullptr_t) { + return left.get() == nullptr; +} + +template +bool operator!=(const QuicArenaScopedPtr& left, std::nullptr_t) { + return left.get() != nullptr; +} + +template +QuicArenaScopedPtr::QuicArenaScopedPtr() + : value_(nullptr) {} + +template +QuicArenaScopedPtr::QuicArenaScopedPtr(T* value) + : QuicArenaScopedPtr(value, ConstructFrom::kHeap) {} + +template +template +QuicArenaScopedPtr::QuicArenaScopedPtr(QuicArenaScopedPtr&& other) + : value_(other.value_) { + static_assert( + std::is_base_of::value || std::is_same::value, + "Cannot construct QuicArenaScopedPtr; type is not derived or same."); + other.value_ = nullptr; +} + +template +template +QuicArenaScopedPtr& QuicArenaScopedPtr::operator=( + QuicArenaScopedPtr&& other) { + static_assert( + std::is_base_of::value || std::is_same::value, + "Cannot assign QuicArenaScopedPtr; type is not derived or same."); + swap(other); + return *this; +} + +template +QuicArenaScopedPtr::~QuicArenaScopedPtr() { + reset(); +} + +template +T* QuicArenaScopedPtr::get() const { + return reinterpret_cast(reinterpret_cast(value_) & + ~kFromArenaMask); +} + +template +T& QuicArenaScopedPtr::operator*() const { + return *get(); +} + +template +T* QuicArenaScopedPtr::operator->() const { + return get(); +} + +template +void QuicArenaScopedPtr::swap(QuicArenaScopedPtr& other) { + using std::swap; + swap(value_, other.value_); +} + +template +bool QuicArenaScopedPtr::is_from_arena() { + return (reinterpret_cast(value_) & kFromArenaMask) != 0; +} + +template +void QuicArenaScopedPtr::reset(T* value) { + if (value_ != nullptr) { + if (is_from_arena()) { + // Manually invoke the destructor. + get()->~T(); + } else { + delete get(); + } + } + DCHECK_EQ(0u, reinterpret_cast(value) & kFromArenaMask); + value_ = value; +} + +template +QuicArenaScopedPtr::QuicArenaScopedPtr(void* value, ConstructFrom from_arena) + : value_(value) { + DCHECK_EQ(0u, reinterpret_cast(value_) & kFromArenaMask); + switch (from_arena) { + case ConstructFrom::kHeap: + break; + case ConstructFrom::kArena: + value_ = reinterpret_cast(reinterpret_cast(value_) | + QuicArenaScopedPtr::kFromArenaMask); + break; + } +} + +} // namespace net + +#endif // NET_QUIC_QUIC_ARENA_SCOPED_PTR_H_ diff --git a/src/net/quic/quic_blocked_writer_interface.h b/src/net/quic/quic_blocked_writer_interface.h index 78a270af..a3a2622a 100644 --- a/src/net/quic/quic_blocked_writer_interface.h +++ b/src/net/quic/quic_blocked_writer_interface.h @@ -24,20 +24,15 @@ class NET_EXPORT_PRIVATE QuicBlockedWriterInterface { virtual void OnCanWrite() = 0; }; -} // namespace net - -#if defined(COMPILER_GCC) -namespace BASE_HASH_NAMESPACE { // Hash pointers as if they were int's, but bring more entropy to the lower // bits. -template <> -struct hash { +struct QuicBlockedWriterInterfacePtrHash { std::size_t operator()(const net::QuicBlockedWriterInterface* ptr) const { size_t k = reinterpret_cast(ptr); return k + (k >> 6); } }; -} -#endif + +} // namespace net #endif // NET_QUIC_QUIC_BLOCKED_WRITER_INTERFACE_H_ diff --git a/src/net/quic/quic_bug_tracker.h b/src/net/quic/quic_bug_tracker.h new file mode 100644 index 00000000..280a537d --- /dev/null +++ b/src/net/quic/quic_bug_tracker.h @@ -0,0 +1,13 @@ +// Copyright (c) 2016 The Chromium 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_BUG_TRACKER_H_ +#define NET_QUIC_QUIC_BUG_TRACKER_H_ + +// For external QUIC, QUIC_BUG should be #defined to LOG(DFATAL) and +// QUIC_BUG_IF(condition) to LOG_IF(DFATAL, condition) as client-side log rate +// limiting is less important and chrome doesn't LOG_FIRST_N anyway. +#define QUIC_BUG LOG(DFATAL) +#define QUIC_BUG_IF(condition) LOG_IF(DFATAL, condition) + +#endif // NET_QUIC_QUIC_BUG_TRACKER_H_ diff --git a/src/net/quic/quic_client_promised_info.cc b/src/net/quic/quic_client_promised_info.cc new file mode 100644 index 00000000..3b8740c4 --- /dev/null +++ b/src/net/quic/quic_client_promised_info.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2016 The Chromium 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_promised_info.h" + +#include "base/logging.h" +#include "net/quic/spdy_utils.h" + +using net::SpdyHeaderBlock; +using net::kPushPromiseTimeoutSecs; + +namespace net { + +QuicClientPromisedInfo::QuicClientPromisedInfo(QuicClientSessionBase* session, + QuicStreamId id, + string url) + : session_(session), + helper_(session->connection()->helper()), + id_(id), + url_(url), + client_request_delegate_(nullptr) {} + +QuicClientPromisedInfo::~QuicClientPromisedInfo() {} + +QuicTime QuicClientPromisedInfo::CleanupAlarm::OnAlarm() { + DVLOG(1) << "self GC alarm for stream " << promised_->id_; + promised_->Reset(QUIC_STREAM_CANCELLED); + return QuicTime::Zero(); +} + +void QuicClientPromisedInfo::Init() { + cleanup_alarm_.reset( + helper_->CreateAlarm(new QuicClientPromisedInfo::CleanupAlarm(this))); + cleanup_alarm_->Set(helper_->GetClock()->ApproximateNow().Add( + QuicTime::Delta::FromSeconds(kPushPromiseTimeoutSecs))); +} + +void QuicClientPromisedInfo::OnPromiseHeaders(const SpdyHeaderBlock& headers) { + // RFC7540, Section 8.2, requests MUST be safe [RFC7231], Section + // 4.2.1. GET and HEAD are the methods that are safe and required. + SpdyHeaderBlock::const_iterator it = headers.find(":method"); + DCHECK(it != headers.end()); + if (!(it->second == "GET" || it->second == "HEAD")) { + DVLOG(1) << "Promise for stream " << id_ << " has invalid method " + << it->second; + Reset(QUIC_INVALID_PROMISE_METHOD); + return; + } + if (!SpdyUtils::UrlIsValid(headers)) { + DVLOG(1) << "Promise for stream " << id_ << " has invalid URL " << url_; + Reset(QUIC_INVALID_PROMISE_URL); + return; + } + if (!session_->IsAuthorized(SpdyUtils::GetHostNameFromHeaderBlock(headers))) { + Reset(QUIC_UNAUTHORIZED_PROMISE_URL); + return; + } + request_headers_.reset(new SpdyHeaderBlock(headers)); +} + +void QuicClientPromisedInfo::OnResponseHeaders(const SpdyHeaderBlock& headers) { + response_headers_.reset(new SpdyHeaderBlock(headers)); + if (client_request_delegate_) { + // We already have a client request waiting. + FinalValidation(); + } +} + +void QuicClientPromisedInfo::Reset(QuicRstStreamErrorCode error_code) { + QuicClientPushPromiseIndex::Delegate* delegate = client_request_delegate_; + session_->ResetPromised(id_, error_code); + session_->DeletePromised(this); + if (delegate) { + delegate->OnRendezvousResult(nullptr); + } +} + +QuicAsyncStatus QuicClientPromisedInfo::FinalValidation() { + if (!client_request_delegate_->CheckVary( + *client_request_headers_, *request_headers_, *response_headers_)) { + Reset(QUIC_PROMISE_VARY_MISMATCH); + return QUIC_FAILURE; + } + QuicSpdyStream* stream = session_->GetPromisedStream(id_); + if (!stream) { + // This shouldn't be possible, as |ClientRequest| guards against + // closed stream for the synchronous case. And in the + // asynchronous case, a RST can only be caught by |OnAlarm()|. + QUIC_BUG << "missing promised stream" << id_; + } + QuicClientPushPromiseIndex::Delegate* delegate = client_request_delegate_; + session_->DeletePromised(this); + // Stream can start draining now + if (delegate) { + delegate->OnRendezvousResult(stream); + } + return QUIC_SUCCESS; +} + +QuicAsyncStatus QuicClientPromisedInfo::HandleClientRequest( + const SpdyHeaderBlock& request_headers, + QuicClientPushPromiseIndex::Delegate* delegate) { + if (session_->IsClosedStream(id_)) { + // There was a RST on the response stream. + session_->DeletePromised(this); + return QUIC_FAILURE; + } + client_request_delegate_ = delegate; + client_request_headers_.reset(new SpdyHeaderBlock(request_headers)); + if (!response_headers_) { + return QUIC_PENDING; + } + return FinalValidation(); +} + +void QuicClientPromisedInfo::Cancel() { + // Don't fire OnRendezvousResult() for client initiated cancel. + client_request_delegate_ = nullptr; + Reset(QUIC_STREAM_CANCELLED); +} + +} // namespace net diff --git a/src/net/quic/quic_client_promised_info.h b/src/net/quic/quic_client_promised_info.h new file mode 100644 index 00000000..300c41f5 --- /dev/null +++ b/src/net/quic/quic_client_promised_info.h @@ -0,0 +1,113 @@ +// Copyright (c) 2016 The Chromium 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_PROMISED_INFO_H_ +#define NET_QUIC_QUIC_CLIENT_PROMISED_INFO_H_ + +#include +#include + +#include "net/quic/quic_alarm.h" +#include "net/quic/quic_client_push_promise_index.h" +#include "net/quic/quic_client_session_base.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_spdy_stream.h" +#include "net/spdy/spdy_framer.h" + +namespace net { + +class QuicClientSessionBase; +class QuicDataToResend; +class QuicConnectionHelperInterface; + +namespace test { +class QuicClientPromisedInfoPeer; +} // namespace test + +// QuicClientPromisedInfo tracks the client state of a server push +// stream from the time a PUSH_PROMISE is received until rendezvous +// between the promised response and the corresponding client request +// is complete. +class NET_EXPORT_PRIVATE QuicClientPromisedInfo + : public QuicClientPushPromiseIndex::TryHandle { + public: + // Interface to QuicSpdyClientStream + QuicClientPromisedInfo(QuicClientSessionBase* session, + QuicStreamId id, + std::string url); + virtual ~QuicClientPromisedInfo(); + + void Init(); + + // Validate promise headers etc. + void OnPromiseHeaders(const SpdyHeaderBlock& request_headers); + + // Store response, possibly proceed with final validation. + void OnResponseHeaders(const SpdyHeaderBlock& response_headers); + + // Rendezvous between this promised stream and a client request that + // has a matching URL. + virtual QuicAsyncStatus HandleClientRequest( + const SpdyHeaderBlock& headers, + QuicClientPushPromiseIndex::Delegate* delegate); + + void Cancel() override; + + // Client requests are initially associated to promises by matching + // URL in the client request against the URL in the promise headers, + // uing the |promised_by_url| map. The push can be cross-origin, so + // the client should validate that the session is authoritative for + // the promised URL. If not, it should call |RejectUnauthorized|. + QuicClientSessionBase* session() { return session_; } + + // If the promised response contains Vary header, then the fields + // specified by Vary must match between the client request header + // and the promise headers (see https://crbug.com//554220). Vary + // validation requires the response headers (for the actual Vary + // field list), the promise headers (taking the role of the "cached" + // request), and the client request headers. + SpdyHeaderBlock* request_headers() { return request_headers_.get(); } + + SpdyHeaderBlock* response_headers() { return response_headers_.get(); } + + QuicStreamId id() const { return id_; } + + const std::string url() const { return url_; } + + private: + friend class test::QuicClientPromisedInfoPeer; + + class CleanupAlarm : public QuicAlarm::Delegate { + public: + explicit CleanupAlarm(QuicClientPromisedInfo* promised) + : promised_(promised) {} + + QuicTime OnAlarm() override; + + QuicClientPromisedInfo* promised_; + }; + + void Reset(QuicRstStreamErrorCode error_code); + + QuicAsyncStatus FinalValidation(); + + QuicClientSessionBase* session_; + QuicConnectionHelperInterface* helper_; + QuicStreamId id_; + std::string url_; + std::unique_ptr request_headers_; + std::unique_ptr response_headers_; + std::unique_ptr client_request_headers_; + QuicClientPushPromiseIndex::Delegate* client_request_delegate_; + + // The promise will commit suicide eventually if it is not claimed + // by a GET first. + std::unique_ptr cleanup_alarm_; + + DISALLOW_COPY_AND_ASSIGN(QuicClientPromisedInfo); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CLIENT_PROMISED_INFO_H_ diff --git a/src/net/quic/quic_client_push_promise_index.cc b/src/net/quic/quic_client_push_promise_index.cc new file mode 100644 index 00000000..1c49960b --- /dev/null +++ b/src/net/quic/quic_client_push_promise_index.cc @@ -0,0 +1,46 @@ +// Copyright 2016 The Chromium 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_push_promise_index.h" + +#include "net/quic/quic_client_promised_info.h" +#include "net/quic/spdy_utils.h" + +using net::SpdyHeaderBlock; + +namespace net { + +QuicClientPushPromiseIndex::QuicClientPushPromiseIndex() {} + +QuicClientPushPromiseIndex::~QuicClientPushPromiseIndex() {} + +QuicClientPushPromiseIndex::TryHandle::~TryHandle() {} + +QuicClientPromisedInfo* QuicClientPushPromiseIndex::GetPromised( + const string& url) { + QuicPromisedByUrlMap::iterator it = promised_by_url_.find(url); + if (it == promised_by_url_.end()) { + return nullptr; + } + return it->second; +} + +QuicAsyncStatus QuicClientPushPromiseIndex::Try( + const SpdyHeaderBlock& request, + QuicClientPushPromiseIndex::Delegate* delegate, + TryHandle** handle) { + string url(SpdyUtils::GetUrlFromHeaderBlock(request)); + QuicPromisedByUrlMap::iterator it = promised_by_url_.find(url); + if (it != promised_by_url_.end()) { + QuicClientPromisedInfo* promised = it->second; + QuicAsyncStatus rv = promised->HandleClientRequest(request, delegate); + if (rv == QUIC_PENDING) { + *handle = promised; + } + return rv; + } + return QUIC_FAILURE; +} + +} // namespace net diff --git a/src/net/quic/quic_client_push_promise_index.h b/src/net/quic/quic_client_push_promise_index.h new file mode 100644 index 00000000..d0601825 --- /dev/null +++ b/src/net/quic/quic_client_push_promise_index.h @@ -0,0 +1,97 @@ +// Copyright 2016 The Chromium 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_PUSH_PROMISE_INDEX_H_ +#define NET_QUIC_QUIC_CLIENT_PUSH_PROMISE_INDEX_H_ + +#include "net/quic/quic_client_session_base.h" + +#include "net/quic/quic_types.h" + +namespace net { + +// QuicClientPushPromiseIndex is the interface to support rendezvous +// between client requests and resources delivered via server push. +// The same index can be shared across multiple sessions (e.g. for the +// same browser users profile), since cross-origin pushes are allowed +// (subject to authority constraints). + +class NET_EXPORT_PRIVATE QuicClientPushPromiseIndex { + public: + // Delegate is used to complete the rendezvous that began with + // |Try()|. + class NET_EXPORT_PRIVATE Delegate { + public: + virtual ~Delegate(){}; + + // The primary lookup matched request with push promise by URL. A + // secondary match is necessary to ensure Vary (RFC 2616, 14.14) + // is honored. If Vary is not present, return true. If Vary is + // present, return whether designated header fields of + // |promise_request| and |client_request| match. + virtual bool CheckVary(const SpdyHeaderBlock& client_request, + const SpdyHeaderBlock& promise_request, + const SpdyHeaderBlock& promise_response) = 0; + + // On rendezvous success, provides the promised |stream|. Callee + // does not inherit ownership of |stream|. On rendezvous failure, + // |stream| is |nullptr| and the client should retry the request. + // Rendezvous can fail due to promise validation failure or RST on + // promised stream. |url| will have been removed from the index + // before |OnRendezvousResult()| is invoked, so a recursive call to + // |Try()| will return |QUIC_FAILURE|, which may be convenient for + // retry purposes. + virtual void OnRendezvousResult(QuicSpdyStream* stream) = 0; + }; + + class NET_EXPORT_PRIVATE TryHandle { + public: + // Cancel the request. + virtual void Cancel() = 0; + + protected: + TryHandle() {} + ~TryHandle(); + + private: + DISALLOW_COPY_AND_ASSIGN(TryHandle); + }; + + QuicClientPushPromiseIndex(); + virtual ~QuicClientPushPromiseIndex(); + + // Called by client code, used to enforce affinity between requests + // for promised streams and the session the promise came from. + QuicClientPromisedInfo* GetPromised(const string& url); + + // Called by client code, to initiate rendezvous between a request + // and a server push stream. If |request|'s url is in the index, + // rendezvous will be attempted and may complete immediately or + // asynchronously. If the matching promise and response headers + // have already arrived, the delegate's methods will fire + // recursively from within |Try()|. Returns |QUIC_SUCCESS| if the + // rendezvous was a success. Returns |QUIC_FAILURE| if there was no + // matching promise, or if there was but the rendezvous has failed. + // Returns QUIC_PENDING if a matching promise was found, but the + // rendezvous needs to complete asynchronously because the promised + // response headers are not yet available. If result is + // QUIC_PENDING, then |*handle| will set so that the caller may + // cancel the request if need be. The caller does not inherit + // ownership of |*handle|, and it ceases to be valid if the caller + // invokes |handle->Cancel()| or if |delegate->OnReponse()| fires. + QuicAsyncStatus Try(const SpdyHeaderBlock& request, + Delegate* delegate, + TryHandle** handle); + + QuicPromisedByUrlMap* promised_by_url() { return &promised_by_url_; } + + private: + QuicPromisedByUrlMap promised_by_url_; + + DISALLOW_COPY_AND_ASSIGN(QuicClientPushPromiseIndex); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CLIENT_PUSH_PROMISE_INDEX_H_ diff --git a/src/net/quic/quic_client_session_base.cc b/src/net/quic/quic_client_session_base.cc index 740263d2..32b3093d 100644 --- a/src/net/quic/quic_client_session_base.cc +++ b/src/net/quic/quic_client_session_base.cc @@ -4,15 +4,27 @@ #include "net/quic/quic_client_session_base.h" +#include "net/quic/quic_client_promised_info.h" #include "net/quic/quic_flags.h" +#include "net/quic/spdy_utils.h" namespace net { -QuicClientSessionBase::QuicClientSessionBase(QuicConnection* connection, - const QuicConfig& config) - : QuicSpdySession(connection, config) {} +QuicClientSessionBase::QuicClientSessionBase( + QuicConnection* connection, + QuicClientPushPromiseIndex* push_promise_index, + const QuicConfig& config) + : QuicSpdySession(connection, config), + push_promise_index_(push_promise_index), + largest_promised_stream_id_(kInvalidStreamId) {} -QuicClientSessionBase::~QuicClientSessionBase() {} +QuicClientSessionBase::~QuicClientSessionBase() { + // all promised streams for this session + for (auto& it : promised_by_id_) { + DVLOG(1) << "erase stream " << it.first << " url " << it.second->url(); + push_promise_index_->promised_by_url()->erase(it.second->url()); + } +} void QuicClientSessionBase::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { QuicSession::OnCryptoHandshakeEvent(event); @@ -28,4 +40,143 @@ void QuicClientSessionBase::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { headers_stream()->set_fec_policy(FEC_PROTECT_ALWAYS); } +void QuicClientSessionBase::OnPromiseHeaders(QuicStreamId stream_id, + StringPiece headers_data) { + QuicSpdyStream* stream = GetSpdyDataStream(stream_id); + if (!stream) { + // It's quite possible to receive headers after a stream has been reset. + return; + } + stream->OnPromiseHeaders(headers_data); +} + +void QuicClientSessionBase::OnInitialHeadersComplete( + QuicStreamId stream_id, + const SpdyHeaderBlock& response_headers) { + // Note that the strong ordering of the headers stream means that + // QuicSpdyClientStream::OnPromiseHeadersComplete must have already + // been called (on the associated stream) if this is a promised + // stream. However, this stream may not have existed at this time, + // hence the need to query the session. + QuicClientPromisedInfo* promised = GetPromisedById(stream_id); + if (!promised) + return; + + promised->OnResponseHeaders(response_headers); +} + +void QuicClientSessionBase::OnPromiseHeadersComplete( + QuicStreamId stream_id, + QuicStreamId promised_stream_id, + size_t frame_len) { + if (promised_stream_id != kInvalidStreamId && + promised_stream_id <= largest_promised_stream_id_) { + connection()->SendConnectionCloseWithDetails( + QUIC_INVALID_STREAM_ID, + "Received push stream id lesser or equal to the" + " last accepted before"); + return; + } + largest_promised_stream_id_ = promised_stream_id; + + QuicSpdyStream* stream = GetSpdyDataStream(stream_id); + if (!stream) { + // It's quite possible to receive headers after a stream has been reset. + return; + } + stream->OnPromiseHeadersComplete(promised_stream_id, frame_len); +} + +void QuicClientSessionBase::HandlePromised(QuicStreamId id, + const SpdyHeaderBlock& headers) { + // Due to pathalogical packet re-ordering, it is possible that + // frames for the promised stream have already arrived, and the + // promised stream could be active or closed. + if (IsClosedStream(id)) { + // There was a RST on the data stream already, perhaps + // QUIC_REFUSED_STREAM? + DVLOG(1) << "Promise ignored for stream " << id + << " that is already closed"; + return; + } + + if (push_promise_index_->promised_by_url()->size() >= get_max_promises()) { + DVLOG(1) << "Too many promises, rejecting promise for stream " << id; + ResetPromised(id, QUIC_REFUSED_STREAM); + return; + } + + const string url = SpdyUtils::GetUrlFromHeaderBlock(headers); + QuicClientPromisedInfo* old_promised = GetPromisedByUrl(url); + if (old_promised) { + DVLOG(1) << "Promise for stream " << id << " is duplicate URL " << url + << " of previous promise for stream " << old_promised->id(); + ResetPromised(id, QUIC_DUPLICATE_PROMISE_URL); + return; + } + + if (GetPromisedById(id)) { + // OnPromiseHeadersComplete() would have closed the connection if + // promised id is a duplicate. + QUIC_BUG << "Duplicate promise for id " << id; + return; + } + + QuicClientPromisedInfo* promised = new QuicClientPromisedInfo(this, id, url); + std::unique_ptr promised_owner(promised); + promised->Init(); + DVLOG(1) << "stream " << id << " emplace url " << url; + (*push_promise_index_->promised_by_url())[url] = promised; + promised_by_id_[id] = std::move(promised_owner); + promised->OnPromiseHeaders(headers); +} + +QuicClientPromisedInfo* QuicClientSessionBase::GetPromisedByUrl( + const string& url) { + QuicPromisedByUrlMap::iterator it = + push_promise_index_->promised_by_url()->find(url); + if (it != push_promise_index_->promised_by_url()->end()) { + return it->second; + } + return nullptr; +} + +QuicClientPromisedInfo* QuicClientSessionBase::GetPromisedById( + const QuicStreamId id) { + QuicPromisedByIdMap::iterator it = promised_by_id_.find(id); + if (it != promised_by_id_.end()) { + return it->second.get(); + } + return nullptr; +} + +QuicSpdyStream* QuicClientSessionBase::GetPromisedStream( + const QuicStreamId id) { + if (IsClosedStream(id)) { + return nullptr; + } + StreamMap::iterator it = dynamic_streams().find(id); + if (it != dynamic_streams().end()) { + return static_cast(it->second); + } + QUIC_BUG << "Open promised stream " << id << " is missing!"; + return nullptr; +} + +void QuicClientSessionBase::DeletePromised(QuicClientPromisedInfo* promised) { + push_promise_index_->promised_by_url()->erase(promised->url()); + // Since promised_by_id_ contains the unique_ptr, this will destroy + // promised. + promised_by_id_.erase(promised->id()); +} + +void QuicClientSessionBase::ResetPromised(QuicStreamId id, + QuicRstStreamErrorCode error_code) { + SendRstStream(id, error_code, 0); + if (!IsOpenStream(id)) { + MaybeIncreaseLargestPeerStreamId(id); + InsertLocallyClosedStreamsHighestOffset(id, 0); + } +} + } // namespace net diff --git a/src/net/quic/quic_client_session_base.h b/src/net/quic/quic_client_session_base.h index ba7be853..3b3a180c 100644 --- a/src/net/quic/quic_client_session_base.h +++ b/src/net/quic/quic_client_session_base.h @@ -11,29 +11,112 @@ namespace net { +class QuicClientPromisedInfo; +class QuicClientPushPromiseIndex; +class QuicSpdyClientStream; + +// For client/http layer code. Lookup promised streams based on +// matching promised request url. The same map can be shared across +// multiple sessions, since cross-origin pushes are allowed (subject +// to authority constraints). Clients should use this map to enforce +// session affinity for requests corresponding to cross-origin push +// promised streams. +using QuicPromisedByUrlMap = + std::unordered_map; + +// The maximum time a promises stream can be reserved without being +// claimed by a client request. +const int64_t kPushPromiseTimeoutSecs = 60; + // Base class for all client-specific QuicSession subclasses. -class NET_EXPORT_PRIVATE QuicClientSessionBase : public QuicSpdySession { +class NET_EXPORT_PRIVATE QuicClientSessionBase + : public QuicSpdySession, + public QuicCryptoClientStream::ProofHandler { public: - QuicClientSessionBase(QuicConnection* connection, const QuicConfig& config); + // Caller retains ownership of |promised_by_url|. + QuicClientSessionBase(QuicConnection* connection, + QuicClientPushPromiseIndex* push_promise_index, + const QuicConfig& config); ~QuicClientSessionBase() override; - // 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. - 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; - // Override base class to set FEC policy before any data is sent by client. void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override; + // Called by |headers_stream_| when push promise headers have been + // received for a stream. + void OnPromiseHeaders(QuicStreamId stream_id, + StringPiece headers_data) override; + + // Called by |headers_stream_| when push promise headers have been + // completely received. |fin| will be true if the fin flag was set + // in the headers. + void OnPromiseHeadersComplete(QuicStreamId stream_id, + QuicStreamId promised_stream_id, + size_t frame_len) override; + + // Called by |QuicSpdyClientStream| on receipt of response headers, + // needed to detect promised server push streams, as part of + // client-request to push-stream rendezvous. + void OnInitialHeadersComplete(QuicStreamId stream_id, + const SpdyHeaderBlock& response_headers); + + // Called by |QuicSpdyClientStream| on receipt of PUSH_PROMISE, does + // some session level validation and creates the + // |QuicClientPromisedInfo| inserting into maps by id and url. + void HandlePromised(QuicStreamId id, const SpdyHeaderBlock& headers); + + // For cross-origin server push, this should verify the server is + // authoritative per [RFC2818], Section 3. Roughly, subjectAltName + // std::list in the certificate should contain a matching DNS name, or IP + // address. |hostname| is derived from the ":authority" header field of + // the PUSH_PROMISE frame, port if present there will be dropped. + virtual bool IsAuthorized(const std::string& hostname) = 0; + + // Session retains ownership. + QuicClientPromisedInfo* GetPromisedByUrl(const std::string& url); + // Session retains ownership. + QuicClientPromisedInfo* GetPromisedById(const QuicStreamId id); + + // + QuicSpdyStream* GetPromisedStream(const QuicStreamId id); + + // Removes |promised| from the maps by url. + void ErasePromisedByUrl(QuicClientPromisedInfo* promised); + + // Removes |promised| from the maps by url and id and destroys + // promised. + void DeletePromised(QuicClientPromisedInfo* promised); + + // Sends Rst for the stream, and makes sure that future calls to + // IsClosedStream(id) return true, which ensures that any subsequent + // frames related to this stream will be ignored (modulo flow + // control accounting). + void ResetPromised(QuicStreamId id, QuicRstStreamErrorCode error_code); + + size_t get_max_promises() const { + return max_open_incoming_streams() * kMaxPromisedStreamsMultiplier; + } + + QuicClientPushPromiseIndex* push_promise_index() { + return push_promise_index_; + } + private: + // For QuicSpdyClientStream to detect that a response corresponds to a + // promise. + using QuicPromisedByIdMap = + std::unordered_map>; + + // As per rfc7540, section 10.5: track promise streams in "reserved + // (remote)". The primary key is URL from he promise request + // headers. The promised stream id is a secondary key used to get + // promise info when the response headers of the promised stream + // arrive. + QuicClientPushPromiseIndex* push_promise_index_; + QuicPromisedByIdMap promised_by_id_; + QuicStreamId largest_promised_stream_id_; + DISALLOW_COPY_AND_ASSIGN(QuicClientSessionBase); }; diff --git a/src/net/quic/quic_config.cc b/src/net/quic/quic_config.cc index 51e0a9af..c968be9e 100644 --- a/src/net/quic/quic_config.cc +++ b/src/net/quic/quic_config.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "net/quic/crypto/crypto_handshake_message.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_utils.h" using std::min; @@ -203,8 +204,8 @@ bool QuicFixedUint32::HasSendValue() const { } uint32_t QuicFixedUint32::GetSendValue() const { - LOG_IF(DFATAL, !has_send_value_) << "No send value to get for tag:" - << QuicUtils::TagToString(tag_); + QUIC_BUG_IF(!has_send_value_) << "No send value to get for tag:" + << QuicUtils::TagToString(tag_); return send_value_; } @@ -218,8 +219,8 @@ bool QuicFixedUint32::HasReceivedValue() const { } uint32_t QuicFixedUint32::GetReceivedValue() const { - LOG_IF(DFATAL, !has_receive_value_) << "No receive value to get for tag:" - << QuicUtils::TagToString(tag_); + QUIC_BUG_IF(!has_receive_value_) << "No receive value to get for tag:" + << QuicUtils::TagToString(tag_); return receive_value_; } @@ -263,6 +264,9 @@ QuicFixedTagVector::QuicFixedTagVector(QuicTag name, has_send_values_(false), has_receive_values_(false) {} +QuicFixedTagVector::QuicFixedTagVector(const QuicFixedTagVector& other) = + default; + QuicFixedTagVector::~QuicFixedTagVector() {} bool QuicFixedTagVector::HasSendValues() const { @@ -270,8 +274,8 @@ bool QuicFixedTagVector::HasSendValues() const { } QuicTagVector QuicFixedTagVector::GetSendValues() const { - LOG_IF(DFATAL, !has_send_values_) << "No send values to get for tag:" - << QuicUtils::TagToString(tag_); + QUIC_BUG_IF(!has_send_values_) << "No send values to get for tag:" + << QuicUtils::TagToString(tag_); return send_values_; } @@ -285,8 +289,8 @@ bool QuicFixedTagVector::HasReceivedValues() const { } QuicTagVector QuicFixedTagVector::GetReceivedValues() const { - LOG_IF(DFATAL, !has_receive_values_) << "No receive value to get for tag:" - << QuicUtils::TagToString(tag_); + QUIC_BUG_IF(!has_receive_values_) << "No receive value to get for tag:" + << QuicUtils::TagToString(tag_); return receive_values_; } @@ -343,10 +347,13 @@ QuicConfig::QuicConfig() initial_round_trip_time_us_(kIRTT, PRESENCE_OPTIONAL), initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL), initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL), - socket_receive_buffer_(kSRBF, PRESENCE_OPTIONAL) { + socket_receive_buffer_(kSRBF, PRESENCE_OPTIONAL), + multipath_enabled_(kMPTH, PRESENCE_OPTIONAL) { SetDefaults(); } +QuicConfig::QuicConfig(const QuicConfig& other) = default; + QuicConfig::~QuicConfig() {} bool QuicConfig::SetInitialReceivedConnectionOptions( @@ -465,9 +472,9 @@ uint32_t QuicConfig::GetInitialRoundTripTimeUsToSend() const { void QuicConfig::SetInitialStreamFlowControlWindowToSend( uint32_t window_bytes) { if (window_bytes < kMinimumFlowControlSendWindow) { - LOG(DFATAL) << "Initial stream flow control receive window (" - << window_bytes << ") cannot be set lower than default (" - << kMinimumFlowControlSendWindow << ")."; + QUIC_BUG << "Initial stream flow control receive window (" << window_bytes + << ") cannot be set lower than default (" + << kMinimumFlowControlSendWindow << ")."; window_bytes = kMinimumFlowControlSendWindow; } initial_stream_flow_control_window_bytes_.SetSendValue(window_bytes); @@ -488,9 +495,9 @@ uint32_t QuicConfig::ReceivedInitialStreamFlowControlWindowBytes() const { void QuicConfig::SetInitialSessionFlowControlWindowToSend( uint32_t window_bytes) { if (window_bytes < kMinimumFlowControlSendWindow) { - LOG(DFATAL) << "Initial session flow control receive window (" - << window_bytes << ") cannot be set lower than default (" - << kMinimumFlowControlSendWindow << ")."; + QUIC_BUG << "Initial session flow control receive window (" << window_bytes + << ") cannot be set lower than default (" + << kMinimumFlowControlSendWindow << ")."; window_bytes = kMinimumFlowControlSendWindow; } initial_session_flow_control_window_bytes_.SetSendValue(window_bytes); @@ -520,6 +527,15 @@ uint32_t QuicConfig::ReceivedSocketReceiveBuffer() const { return socket_receive_buffer_.GetReceivedValue(); } +void QuicConfig::SetMultipathEnabled(bool multipath_enabled) { + uint32_t value = multipath_enabled ? 1 : 0; + multipath_enabled_.set(value, value); +} + +bool QuicConfig::MultipathEnabled() const { + return multipath_enabled_.GetUint32() > 0; +} + bool QuicConfig::negotiated() const { // TODO(ianswett): Add the negotiated parameters once and iterate over all // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and diff --git a/src/net/quic/quic_config.h b/src/net/quic/quic_config.h index 3609f2d0..bd6e5d8a 100644 --- a/src/net/quic/quic_config.h +++ b/src/net/quic/quic_config.h @@ -75,6 +75,8 @@ class NET_EXPORT_PRIVATE QuicNegotiableValue : public QuicConfigValue { }; class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue { + // TODO(fayang): some negotiated values use uint32 as bool (e.g., silent + // close). Consider adding a QuicNegotiableBool type. public: // Default and max values default to 0. QuicNegotiableUint32(QuicTag name, QuicConfigPresence presence); @@ -181,6 +183,7 @@ class NET_EXPORT_PRIVATE QuicFixedUint32 : public QuicConfigValue { class NET_EXPORT_PRIVATE QuicFixedTagVector : public QuicConfigValue { public: QuicFixedTagVector(QuicTag name, QuicConfigPresence presence); + QuicFixedTagVector(const QuicFixedTagVector& other); ~QuicFixedTagVector() override; bool HasSendValues() const; @@ -217,6 +220,7 @@ class NET_EXPORT_PRIVATE QuicFixedTagVector : public QuicConfigValue { class NET_EXPORT_PRIVATE QuicConfig { public: QuicConfig(); + QuicConfig(const QuicConfig& other); ~QuicConfig(); void SetConnectionOptionsToSend(const QuicTagVector& connection_options); @@ -327,6 +331,10 @@ class NET_EXPORT_PRIVATE QuicConfig { uint32_t ReceivedSocketReceiveBuffer() const; + void SetMultipathEnabled(bool multipath_enabled); + + bool MultipathEnabled() const; + bool negotiated() const; // ToHandshakeMessage serialises the settings in this object as a series of @@ -373,6 +381,9 @@ class NET_EXPORT_PRIVATE QuicConfig { // Socket receive buffer in bytes. QuicFixedUint32 socket_receive_buffer_; + + // Whether to support multipath for this connection. + QuicNegotiableUint32 multipath_enabled_; }; } // namespace net diff --git a/src/net/quic/quic_connection.cc b/src/net/quic/quic_connection.cc index 538964b5..1dd656eb 100644 --- a/src/net/quic/quic_connection.cc +++ b/src/net/quic/quic_connection.cc @@ -18,14 +18,18 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" +#include "net/base/address_family.h" +#include "net/base/ip_address.h" #include "net/base/net_errors.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/proto/cached_network_parameters.pb.h" #include "net/quic/quic_bandwidth.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_config.h" #include "net/quic/quic_fec_group.h" #include "net/quic/quic_flags.h" @@ -77,6 +81,11 @@ bool Near(QuicPacketNumber a, QuicPacketNumber b) { return delta <= kMaxPacketGap; } +bool IsInitializedIPEndPoint(const IPEndPoint& address) { + return net::GetAddressFamily(address.address()) != + net::ADDRESS_FAMILY_UNSPECIFIED; +} + // An alarm that is scheduled to send an ack if a timeout occurs. class AckAlarm : public QuicAlarm::Delegate { public: @@ -152,7 +161,7 @@ class PingAlarm : public QuicAlarm::Delegate { explicit PingAlarm(QuicConnection* connection) : connection_(connection) {} QuicTime OnAlarm() override { - connection_->SendPing(); + connection_->OnPingTimeout(); return QuicTime::Zero(); } @@ -204,7 +213,7 @@ class MtuDiscoveryAckListener : public QuicAckListenerInterface { : connection_(connection), probe_size_(probe_size) {} void OnPacketAcked(int /*acked_bytes*/, - QuicTime::Delta /*delta_largest_observed*/) override { + QuicTime::Delta /*ack delay time*/) override { // MTU discovery packets are not retransmittable, so it must be acked. MaybeIncreaseMtu(); } @@ -230,26 +239,13 @@ class MtuDiscoveryAckListener : public QuicAckListenerInterface { } // namespace -QuicConnection::QueuedPacket::QueuedPacket(SerializedPacket packet) - : serialized_packet(packet), - transmission_type(NOT_RETRANSMISSION), - original_packet_number(0) {} - -QuicConnection::QueuedPacket::QueuedPacket( - SerializedPacket packet, - TransmissionType transmission_type, - QuicPacketNumber original_packet_number) - : serialized_packet(packet), - transmission_type(transmission_type), - original_packet_number(original_packet_number) {} - #define ENDPOINT \ (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ") QuicConnection::QuicConnection(QuicConnectionId connection_id, IPEndPoint address, QuicConnectionHelperInterface* helper, - const PacketWriterFactory& writer_factory, + QuicPacketWriter* writer, bool owns_writer, Perspective perspective, const QuicVersionVector& supported_versions) @@ -257,7 +253,8 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, helper->GetClock()->ApproximateNow(), perspective), helper_(helper), - writer_(writer_factory.Create(this)), + per_packet_options_(nullptr), + writer_(writer), owns_writer_(owns_writer), encryption_level_(ENCRYPTION_NONE), has_forward_secure_encrypter_(false), @@ -286,13 +283,18 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, ack_decimation_enabled_(false), delay_setting_retransmission_alarm_(false), pending_retransmission_alarm_(false), - 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))), - mtu_discovery_alarm_(helper->CreateAlarm(new MtuDiscoveryAlarm(this))), + arena_(), + ack_alarm_(helper->CreateAlarm(arena_.New(this), &arena_)), + retransmission_alarm_( + helper->CreateAlarm(arena_.New(this), &arena_)), + send_alarm_(helper->CreateAlarm(arena_.New(this), &arena_)), + resume_writes_alarm_( + helper->CreateAlarm(arena_.New(this), &arena_)), + timeout_alarm_( + helper->CreateAlarm(arena_.New(this), &arena_)), + ping_alarm_(helper->CreateAlarm(arena_.New(this), &arena_)), + mtu_discovery_alarm_( + helper->CreateAlarm(arena_.New(this), &arena_)), visitor_(nullptr), debug_visitor_(nullptr), packet_generator_(connection_id_, @@ -300,9 +302,10 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, random_generator_, helper->GetBufferAllocator(), this), - fec_alarm_(helper->CreateAlarm(new FecAlarm(&packet_generator_))), + fec_alarm_(helper->CreateAlarm(arena_.New(&packet_generator_), + &arena_)), idle_network_timeout_(QuicTime::Delta::Infinite()), - overall_connection_timeout_(QuicTime::Delta::Infinite()), + handshake_timeout_(QuicTime::Delta::Infinite()), time_of_last_received_packet_(clock_->ApproximateNow()), time_of_last_sent_new_packet_(clock_->ApproximateNow()), last_send_for_timeout_(clock_->ApproximateNow()), @@ -313,7 +316,8 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, clock_, &stats_, FLAGS_quic_use_bbr_congestion_control ? kBBR : kCubic, - FLAGS_quic_use_time_loss_detection ? kTime : kNack), + FLAGS_quic_use_time_loss_detection ? kTime : kNack, + /*delegate=*/nullptr), version_negotiation_state_(START_NEGOTIATION), perspective_(perspective), connected_(true), @@ -328,7 +332,8 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, next_mtu_probe_at_(kPacketsBetweenMtuProbesBase), largest_received_packet_size_(0), goaway_sent_(false), - goaway_received_(false) { + goaway_received_(false), + multipath_enabled_(false) { DVLOG(1) << ENDPOINT << "Created connection with connection_id: " << connection_id; framer_.set_visitor(this); @@ -358,19 +363,25 @@ QuicConnection::~QuicConnection() { void QuicConnection::ClearQueuedPackets() { for (QueuedPacketList::iterator it = queued_packets_.begin(); it != queued_packets_.end(); ++it) { - delete it->serialized_packet.retransmittable_frames; - delete it->serialized_packet.packet; + // Delete the buffer before calling ClearSerializedPacket, which sets + // encrypted_buffer to nullptr. + delete[] it->encrypted_buffer; + QuicUtils::ClearSerializedPacket(&(*it)); } queued_packets_.clear(); } void QuicConnection::SetFromConfig(const QuicConfig& config) { if (config.negotiated()) { + // Handshake complete, set handshake timeout to Infinite. SetNetworkTimeouts(QuicTime::Delta::Infinite(), config.IdleConnectionStateLifetime()); if (config.SilentClose()) { silent_close_enabled_ = true; } + if (FLAGS_quic_enable_multipath && config.MultipathEnabled()) { + multipath_enabled_ = true; + } } else { SetNetworkTimeouts(config.max_time_before_crypto_handshake(), config.max_idle_time_before_crypto_handshake()); @@ -403,8 +414,7 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { if (debug_visitor_ != nullptr) { debug_visitor_->OnSetFromConfig(config); } - if (FLAGS_quic_ack_decimation && - config.HasClientSentConnectionOption(kACKD, perspective_)) { + if (config.HasClientSentConnectionOption(kACKD, perspective_)) { ack_decimation_enabled_ = true; } } @@ -416,12 +426,16 @@ void QuicConnection::OnSendConnectionState( } } +void QuicConnection::OnReceiveConnectionState( + const CachedNetworkParameters& cached_network_params) { + if (debug_visitor_ != nullptr) { + debug_visitor_->OnReceiveConnectionState(cached_network_params); + } +} + void QuicConnection::ResumeConnectionState( const CachedNetworkParameters& cached_network_params, bool max_bandwidth_resumption) { - if (debug_visitor_ != nullptr) { - debug_visitor_->OnResumeConnectionState(cached_network_params); - } sent_packet_manager_.ResumeConnectionState(cached_network_params, max_bandwidth_resumption); } @@ -480,7 +494,7 @@ void QuicConnection::OnPublicResetPacket(const QuicPublicResetPacket& packet) { if (debug_visitor_ != nullptr) { debug_visitor_->OnPublicResetPacket(packet); } - CloseConnection(QUIC_PUBLIC_RESET, true); + CloseConnection(QUIC_PUBLIC_RESET, ConnectionCloseSource::FROM_PEER); DVLOG(1) << ENDPOINT << "Connection " << connection_id() << " closed via QUIC_PUBLIC_RESET from peer."; @@ -491,9 +505,9 @@ bool QuicConnection::OnProtocolVersionMismatch(QuicVersion received_version) { << received_version; // TODO(satyamshekhar): Implement no server state in this mode. if (perspective_ == Perspective::IS_CLIENT) { - LOG(DFATAL) << ENDPOINT << "Framer called OnProtocolVersionMismatch. " - << "Closing connection."; - CloseConnection(QUIC_INTERNAL_ERROR, false); + QUIC_BUG << ENDPOINT << "Framer called OnProtocolVersionMismatch. " + << "Closing connection."; + CloseConnection(QUIC_INTERNAL_ERROR, ConnectionCloseSource::FROM_SELF); return false; } DCHECK_NE(version(), received_version); @@ -551,9 +565,9 @@ void QuicConnection::OnVersionNegotiationPacket( // here. (Check for a bug regression.) DCHECK_EQ(connection_id_, packet.connection_id); if (perspective_ == Perspective::IS_SERVER) { - LOG(DFATAL) << ENDPOINT << "Framer parsed VersionNegotiationPacket." - << " Closing connection."; - CloseConnection(QUIC_INTERNAL_ERROR, false); + QUIC_BUG << ENDPOINT << "Framer parsed VersionNegotiationPacket." + << " Closing connection."; + CloseConnection(QUIC_INTERNAL_ERROR, ConnectionCloseSource::FROM_SELF); return; } if (debug_visitor_ != nullptr) { @@ -569,7 +583,8 @@ void QuicConnection::OnVersionNegotiationPacket( DLOG(WARNING) << ENDPOINT << "The server already supports our version. " << "It should have accepted our connection."; // Just drop the connection. - CloseConnection(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, false); + CloseConnection(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, + ConnectionCloseSource::FROM_SELF); return; } @@ -617,10 +632,19 @@ bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) { // here. DCHECK_EQ(connection_id_, header.public_header.connection_id); + // Multipath is not enabled, but a packet with multipath flag on is received. + if (!multipath_enabled_ && header.public_header.multipath_flag) { + LOG(DFATAL) << "Received a packet with multipath flag on when multipath is " + "not enabled."; + SendConnectionCloseWithDetails(QUIC_BAD_MULTIPATH_FLAG, + "receive a packet with multipath flag on " + "when multipath is not enabled."); + return false; + } + // If this packet has already been seen, or the sender has told us that it // will not be retransmitted, then stop processing the packet. - if (FLAGS_quic_drop_non_awaited_packets && - !received_packet_manager_.IsAwaitingPacket(header.packet_number)) { + if (!received_packet_manager_.IsAwaitingPacket(header.packet_number)) { DVLOG(1) << ENDPOINT << "Packet " << header.packet_number << " no longer being waited for. Discarding."; if (debug_visitor_ != nullptr) { @@ -656,6 +680,8 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { return false; } + MaybeMigrateConnectionToNewPeerAddress(); + --stats_.packets_dropped; DVLOG(1) << ENDPOINT << "Received packet header: " << header; last_header_ = header; @@ -679,13 +705,16 @@ bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { } if (frame.stream_id != kCryptoStreamId && last_decrypted_packet_level_ == ENCRYPTION_NONE) { - DLOG(WARNING) << ENDPOINT - << "Received an unencrypted data frame: closing connection"; + QUIC_BUG << ENDPOINT + << "Received an unencrypted data frame: closing connection" + << " packet_number:" << last_header_.packet_number + << " received_packets:" << received_packet_manager_.ack_frame(); SendConnectionCloseWithDetails(QUIC_UNENCRYPTED_STREAM_DATA, "Unencrypted stream data seen"); return false; } visitor_->OnStreamFrame(frame); + visitor_->PostProcessAfterData(); stats_.stream_bytes_received += frame.frame_length; should_last_packet_instigate_acks_ = true; return connected_; @@ -819,7 +848,9 @@ const char* QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { if (!sent_entropy_manager_.IsValidEntropy(incoming_ack.largest_observed, incoming_ack.missing_packets, incoming_ack.entropy_hash)) { - LOG(WARNING) << ENDPOINT << "Peer sent invalid entropy."; + LOG(WARNING) << ENDPOINT << "Peer sent invalid entropy." + << " largest_observed:" << incoming_ack.largest_observed + << " last_received:" << last_header_.packet_number; return "Invalid entropy"; } @@ -827,7 +858,8 @@ const char* QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { !incoming_ack.missing_packets.Contains( incoming_ack.latest_revived_packet)) { LOG(WARNING) << ENDPOINT - << "Peer specified revived packet which was not missing."; + << "Peer specified revived packet which was not missing." + << " revived_packet:" << incoming_ack.latest_revived_packet; return "Invalid revived packet"; } return nullptr; @@ -874,6 +906,7 @@ bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { << " with error: " << QuicUtils::StreamErrorToString(frame.error_code); visitor_->OnRstStream(frame); + visitor_->PostProcessAfterData(); should_last_packet_instigate_acks_ = true; return connected_; } @@ -888,7 +921,7 @@ bool QuicConnection::OnConnectionCloseFrame( << connection_id() << " with error: " << QuicUtils::ErrorToString(frame.error_code) << " " << frame.error_details; - CloseConnection(frame.error_code, true); + CloseConnection(frame.error_code, ConnectionCloseSource::FROM_PEER); return connected_; } @@ -904,6 +937,7 @@ bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { goaway_received_ = true; visitor_->OnGoAway(frame); + visitor_->PostProcessAfterData(); should_last_packet_instigate_acks_ = true; return connected_; } @@ -917,6 +951,7 @@ bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { << "WINDOW_UPDATE_FRAME received for stream: " << frame.stream_id << " with byte offset: " << frame.byte_offset; visitor_->OnWindowUpdateFrame(frame); + visitor_->PostProcessAfterData(); should_last_packet_instigate_acks_ = true; return connected_; } @@ -929,10 +964,22 @@ bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) { DVLOG(1) << ENDPOINT << "BLOCKED_FRAME received for stream: " << frame.stream_id; visitor_->OnBlockedFrame(frame); + visitor_->PostProcessAfterData(); should_last_packet_instigate_acks_ = true; return connected_; } +bool QuicConnection::OnPathCloseFrame(const QuicPathCloseFrame& frame) { + DCHECK(connected_); + if (debug_visitor_ != nullptr) { + debug_visitor_->OnPathCloseFrame(frame); + } + DVLOG(1) << ENDPOINT + << "PATH_CLOSE_FRAME received for path: " << frame.path_id; + OnPathClosed(frame.path_id); + return connected_; +} + void QuicConnection::OnPacketComplete() { // Don't do anything if this packet closed the connection. if (!connected_) { @@ -1091,9 +1138,9 @@ void QuicConnection::SendVersionNegotiationPacket() { scoped_ptr version_packet( packet_generator_.SerializeVersionNegotiationPacket( framer_.supported_versions())); - WriteResult result = - writer_->WritePacket(version_packet->data(), version_packet->length(), - self_address().address(), peer_address()); + WriteResult result = writer_->WritePacket( + version_packet->data(), version_packet->length(), + self_address().address(), peer_address(), per_packet_options_); if (result.status == WRITE_STATUS_ERROR) { OnWriteError(result.error_code); @@ -1118,7 +1165,7 @@ QuicConsumedData QuicConnection::SendStreamData( FecProtection fec_protection, QuicAckListenerInterface* listener) { if (!fin && iov.total_length == 0) { - LOG(DFATAL) << "Attempt to send empty stream frame"; + QUIC_BUG << "Attempt to send empty stream frame"; return QuicConsumedData(0, false); } @@ -1159,21 +1206,19 @@ void QuicConnection::SendRstStream(QuicStreamId id, // Remove all queued packets which only contain data for the reset stream. QueuedPacketList::iterator packet_iterator = queued_packets_.begin(); while (packet_iterator != queued_packets_.end()) { - RetransmittableFrames* retransmittable_frames = - packet_iterator->serialized_packet.retransmittable_frames; - if (!retransmittable_frames) { + QuicFrames* retransmittable_frames = + &packet_iterator->retransmittable_frames; + if (retransmittable_frames->empty()) { ++packet_iterator; continue; } - retransmittable_frames->RemoveFramesForStream(id); - if (!retransmittable_frames->frames().empty()) { + QuicUtils::RemoveFramesForStream(retransmittable_frames, id); + if (!retransmittable_frames->empty()) { ++packet_iterator; continue; } - delete packet_iterator->serialized_packet.retransmittable_frames; - delete packet_iterator->serialized_packet.packet; - packet_iterator->serialized_packet.retransmittable_frames = nullptr; - packet_iterator->serialized_packet.packet = nullptr; + delete[] packet_iterator->encrypted_buffer; + QuicUtils::ClearSerializedPacket(&(*packet_iterator)); packet_iterator = queued_packets_.erase(packet_iterator); } } @@ -1192,6 +1237,13 @@ void QuicConnection::SendBlocked(QuicStreamId id) { packet_generator_.AddControlFrame(QuicFrame(new QuicBlockedFrame(id))); } +void QuicConnection::SendPathClose(QuicPathId path_id) { + // Opportunistically bundle an ack with this outgoing packet. + ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK); + packet_generator_.AddControlFrame(QuicFrame(new QuicPathCloseFrame(path_id))); + OnPathClosed(path_id); +} + const QuicConnectionStats& QuicConnection::GetStats() { const RttStats* rtt_stats = sent_packet_manager_.GetRttStats(); @@ -1227,7 +1279,18 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, } last_size_ = packet.length(); - CheckForAddressMigration(self_address, peer_address); + if (FLAGS_check_peer_address_change_after_decryption) { + last_packet_destination_address_ = self_address; + last_packet_source_address_ = peer_address; + if (!IsInitializedIPEndPoint(self_address_)) { + self_address_ = last_packet_destination_address_; + } + if (!IsInitializedIPEndPoint(peer_address_)) { + peer_address_ = last_packet_source_address_; + } + } else { + CheckForAddressMigration(self_address, peer_address); + } stats_.bytes_received += packet.length(); ++stats_.packets_received; @@ -1282,14 +1345,6 @@ void QuicConnection::CheckForAddressMigration(const IPEndPoint& self_address, self_ip_changed_ = (self_address.address() != self_address_.address()); self_port_changed_ = (self_address.port() != self_address_.port()); } - - // TODO(vasilvv): reset maximum packet size on connection migration. Whenever - // the connection is migrated, it usually ends up being on a different path, - // with possibly smaller MTU. This means the max packet size has to be reset - // and MTU discovery mechanism re-initialized. The main reason the code does - // not do it now is that the retransmission code currently cannot deal with - // the case when it needs to resend a packet created with larger MTU (see - // b/22172803). } void QuicConnection::OnCanWrite() { @@ -1309,6 +1364,7 @@ void QuicConnection::OnCanWrite() { { // Limit the scope of the bundler. ACK inclusion happens elsewhere. ScopedPacketBundler bundler(this, NO_ACK); visitor_->OnCanWrite(); + visitor_->PostProcessAfterData(); } // After the visitor writes, it may have caused the socket to become write @@ -1329,20 +1385,22 @@ void QuicConnection::WriteIfNotBlocked() { } bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { - if (self_ip_changed_ || self_port_changed_) { - SendConnectionCloseWithDetails(QUIC_ERROR_MIGRATING_ADDRESS, - "Self address migration is not supported."); - return false; - } - - PeerAddressChangeType type = NO_CHANGE; - if (peer_ip_changed_ || peer_port_changed_) { - type = DeterminePeerAddressChangeType(); - if (type != NO_CHANGE && type != UNKNOWN && - (FLAGS_quic_disable_non_nat_address_migration && - type != NAT_PORT_REBINDING && type != IPV4_SUBNET_REBINDING)) { - SendConnectionCloseWithDetails(QUIC_ERROR_MIGRATING_ADDRESS, - "Invalid peer address migration."); + if (FLAGS_check_peer_address_change_after_decryption) { + if (perspective_ == Perspective::IS_SERVER && + IsInitializedIPEndPoint(self_address_) && + IsInitializedIPEndPoint(last_packet_destination_address_) && + (!(self_address_ == last_packet_destination_address_))) { + SendConnectionCloseWithDetails( + QUIC_ERROR_MIGRATING_ADDRESS, + "Self address migration is not supported at the server."); + return false; + } + } else { + if (perspective_ == Perspective::IS_SERVER && + (self_ip_changed_ || self_port_changed_)) { + SendConnectionCloseWithDetails( + QUIC_ERROR_MIGRATING_ADDRESS, + "Self address migration is not supported at the server."); return false; } } @@ -1355,26 +1413,17 @@ bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { return false; } - // If this packet has already been seen, or the sender has told us that it - // will not be retransmitted, then stop processing the packet. - if (!FLAGS_quic_drop_non_awaited_packets && - !received_packet_manager_.IsAwaitingPacket(header.packet_number)) { - DVLOG(1) << ENDPOINT << "Packet " << header.packet_number - << " no longer being waited for. Discarding."; - if (debug_visitor_ != nullptr) { - debug_visitor_->OnDuplicatePacket(header.packet_number); - } - return false; - } - if (version_negotiation_state_ != NEGOTIATED_VERSION) { if (perspective_ == Perspective::IS_SERVER) { if (!header.public_header.version_flag) { - DLOG(WARNING) << ENDPOINT << "Packet " << header.packet_number - << " without version flag before version negotiated."; // Packets should have the version flag till version negotiation is // done. - CloseConnection(QUIC_INVALID_VERSION, false); + string error_details = + StringPrintf("%s Packet %" PRIu64 + " without version flag before version negotiated.", + ENDPOINT, header.packet_number); + DLOG(WARNING) << error_details; + SendConnectionCloseWithDetails(QUIC_INVALID_VERSION, error_details); return false; } else { DCHECK_EQ(1u, header.public_header.versions.size()); @@ -1400,21 +1449,6 @@ bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { DCHECK_EQ(NEGOTIATED_VERSION, version_negotiation_state_); - if (peer_ip_changed_ || peer_port_changed_) { - IPEndPoint old_peer_address = peer_address_; - peer_address_ = IPEndPoint( - peer_ip_changed_ ? migrating_peer_ip_ : peer_address_.address(), - peer_port_changed_ ? migrating_peer_port_ : peer_address_.port()); - - DVLOG(1) << ENDPOINT << "Peer's ip:port changed from " - << old_peer_address.ToString() << " to " - << peer_address_.ToString() << ", migrating connection."; - - visitor_->OnConnectionMigration(); - DCHECK_NE(type, NO_CHANGE); - sent_packet_manager_.OnConnectionMigration(type); - } - time_of_last_received_packet_ = clock_->Now(); DVLOG(1) << ENDPOINT << "time of last received packet: " << time_of_last_received_packet_.ToDebuggingValue(); @@ -1441,6 +1475,8 @@ void QuicConnection::WriteQueuedPackets() { QueuedPacketList::iterator packet_iterator = queued_packets_.begin(); while (packet_iterator != queued_packets_.end() && WritePacket(&(*packet_iterator))) { + delete[] packet_iterator->encrypted_buffer; + QuicUtils::ClearSerializedPacket(&(*packet_iterator)); packet_iterator = queued_packets_.erase(packet_iterator); } } @@ -1449,7 +1485,7 @@ void QuicConnection::WritePendingRetransmissions() { // Keep writing as long as there's a pending retransmission which can be // written. while (sent_packet_manager_.HasPendingRetransmissions()) { - const QuicSentPacketManager::PendingRetransmission pending = + const PendingRetransmission pending = sent_packet_manager_.NextPendingRetransmission(); if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) { break; @@ -1464,20 +1500,7 @@ void QuicConnection::WritePendingRetransmissions() { // does not require the creator to be flushed. packet_generator_.FlushAllQueuedFrames(); char buffer[kMaxPacketSize]; - SerializedPacket serialized_packet = packet_generator_.ReserializeAllFrames( - pending.retransmittable_frames, pending.encryption_level, - pending.packet_number_length, buffer, kMaxPacketSize); - if (serialized_packet.packet == nullptr) { - // We failed to serialize the packet, so close the connection. - // CloseConnection does not send close packet, so no infinite loop here. - CloseConnection(QUIC_ENCRYPTION_FAILURE, false); - return; - } - - DVLOG(1) << ENDPOINT << "Retransmitting " << pending.packet_number << " as " - << serialized_packet.packet_number; - SendOrQueuePacket(QueuedPacket(serialized_packet, pending.transmission_type, - pending.packet_number)); + packet_generator_.ReserializeAllFrames(pending, buffer, kMaxPacketSize); } } @@ -1550,23 +1573,10 @@ bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) { return true; } -bool QuicConnection::WritePacket(QueuedPacket* packet) { - if (!WritePacketInner(packet)) { - return false; - } - delete packet->serialized_packet.retransmittable_frames; - delete packet->serialized_packet.packet; - packet->serialized_packet.retransmittable_frames = nullptr; - packet->serialized_packet.packet = nullptr; - return true; -} - -bool QuicConnection::WritePacketInner(QueuedPacket* packet) { - if (packet->serialized_packet.packet_number < - sent_packet_manager_.largest_sent_packet()) { - LOG(DFATAL) << "Attempt to write packet:" - << packet->serialized_packet.packet_number - << " after:" << sent_packet_manager_.largest_sent_packet(); +bool QuicConnection::WritePacket(SerializedPacket* packet) { + if (packet->packet_number < sent_packet_manager_.largest_sent_packet()) { + QUIC_BUG << "Attempt to write packet:" << packet->packet_number + << " after:" << sent_packet_manager_.largest_sent_packet(); SendConnectionCloseWithDetails(QUIC_INTERNAL_ERROR, "Packet written out of order."); return true; @@ -1581,19 +1591,21 @@ bool QuicConnection::WritePacketInner(QueuedPacket* packet) { return false; } - QuicPacketNumber packet_number = packet->serialized_packet.packet_number; + QuicPacketNumber packet_number = packet->packet_number; DCHECK_LE(packet_number_of_last_sent_packet_, packet_number); packet_number_of_last_sent_packet_ = packet_number; - QuicEncryptedPacket* encrypted = packet->serialized_packet.packet; + QuicPacketLength encrypted_length = packet->encrypted_length; // Termination packets are eventually owned by TimeWaitListManager. // Others are deleted at the end of this call. if (is_termination_packet) { if (termination_packets_.get() == nullptr) { termination_packets_.reset(new std::vector); } - // Clone the packet so it's owned in the future. - termination_packets_->push_back(encrypted->Clone()); + // Copy the buffer so it's owned in the future. + char* buffer_copy = QuicUtils::CopyBuffer(*packet); + termination_packets_->push_back( + new QuicEncryptedPacket(buffer_copy, encrypted_length, true)); // This assures we won't try to write *forced* packets when blocked. // Return true to stop processing. if (writer_->IsWriteBlocked()) { @@ -1602,28 +1614,28 @@ bool QuicConnection::WritePacketInner(QueuedPacket* packet) { } } - DCHECK_LE(encrypted->length(), kMaxPacketSize); - DCHECK_LE(encrypted->length(), packet_generator_.GetMaxPacketLength()); + DCHECK_LE(encrypted_length, kMaxPacketSize); + DCHECK_LE(encrypted_length, packet_generator_.GetMaxPacketLength()); DVLOG(1) << ENDPOINT << "Sending packet " << packet_number << " : " - << (packet->serialized_packet.is_fec_packet + << (packet->is_fec_packet ? "FEC " : (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA ? "data bearing " : " ack only ")) << ", encryption level: " - << QuicUtils::EncryptionLevelToString( - packet->serialized_packet.encryption_level) - << ", encrypted length:" << encrypted->length(); + << QuicUtils::EncryptionLevelToString(packet->encryption_level) + << ", encrypted length:" << encrypted_length; DVLOG(2) << ENDPOINT << "packet(" << packet_number << "): " << std::endl - << QuicUtils::StringToHexASCIIDump(encrypted->AsStringPiece()); + << QuicUtils::StringToHexASCIIDump( + StringPiece(packet->encrypted_buffer, encrypted_length)); // Measure the RTT from before the write begins to avoid underestimating the // min_rtt_, especially in cases where the thread blocks or gets swapped out // during the WritePacket below. QuicTime packet_send_time = clock_->Now(); - WriteResult result = - writer_->WritePacket(encrypted->data(), encrypted->length(), - self_address().address(), peer_address()); + WriteResult result = writer_->WritePacket( + packet->encrypted_buffer, encrypted_length, self_address().address(), + peer_address(), per_packet_options_); if (result.error_code == ERR_IO_PENDING) { DCHECK_EQ(WRITE_STATUS_BLOCKED, result.status); } @@ -1640,9 +1652,8 @@ bool QuicConnection::WritePacketInner(QueuedPacket* packet) { } if (result.status != WRITE_STATUS_ERROR && debug_visitor_ != nullptr) { // Pass the write result to the visitor. - debug_visitor_->OnPacketSent( - packet->serialized_packet, packet->original_packet_number, - packet->transmission_type, encrypted->length(), packet_send_time); + debug_visitor_->OnPacketSent(*packet, packet->original_packet_number, + packet->transmission_type, packet_send_time); } if (packet->transmission_type == NOT_RETRANSMISSION) { time_of_last_sent_new_packet_ = packet_send_time; @@ -1665,9 +1676,8 @@ bool QuicConnection::WritePacketInner(QueuedPacket* packet) { sent_packet_manager_.EstimateMaxPacketsInFlight(max_packet_length())); bool reset_retransmission_alarm = sent_packet_manager_.OnPacketSent( - &packet->serialized_packet, packet->original_packet_number, - packet_send_time, encrypted->length(), packet->transmission_type, - IsRetransmittable(*packet)); + packet, packet->original_packet_number, packet_send_time, + packet->transmission_type, IsRetransmittable(*packet)); if (reset_retransmission_alarm || !retransmission_alarm_->IsSet()) { SetRetransmissionAlarm(); @@ -1682,7 +1692,7 @@ bool QuicConnection::WritePacketInner(QueuedPacket* packet) { if (result.status == WRITE_STATUS_ERROR) { OnWriteError(result.error_code); - DLOG(ERROR) << ENDPOINT << "failed writing " << encrypted->length() + DLOG(ERROR) << ENDPOINT << "failed writing " << encrypted_length << " bytes " << " from host " << (self_address().address().empty() ? " empty address " @@ -1694,15 +1704,15 @@ bool QuicConnection::WritePacketInner(QueuedPacket* packet) { return true; } -bool QuicConnection::ShouldDiscardPacket(const QueuedPacket& packet) { +bool QuicConnection::ShouldDiscardPacket(const SerializedPacket& packet) { if (!connected_) { DVLOG(1) << ENDPOINT << "Not sending packet as connection is disconnected."; return true; } - QuicPacketNumber packet_number = packet.serialized_packet.packet_number; + QuicPacketNumber packet_number = packet.packet_number; if (encryption_level_ == ENCRYPTION_FORWARD_SECURE && - packet.serialized_packet.encryption_level == ENCRYPTION_NONE) { + packet.encryption_level == ENCRYPTION_NONE) { // Drop packets that are NULL encrypted since the peer won't accept them // anymore. DVLOG(1) << ENDPOINT << "Dropping NULL encrypted packet: " << packet_number @@ -1728,22 +1738,31 @@ void QuicConnection::OnWriteError(int error_code) { DVLOG(1) << ENDPOINT << "Write failed with error: " << error_code << " (" << ErrorToString(error_code) << ")"; // We can't send an error as the socket is presumably borked. - CloseConnection(QUIC_PACKET_WRITE_ERROR, false); + CloseConnection(QUIC_PACKET_WRITE_ERROR, ConnectionCloseSource::FROM_SELF); } void QuicConnection::OnSerializedPacket(SerializedPacket* serialized_packet) { DCHECK_NE(kInvalidPathId, serialized_packet->path_id); - if (serialized_packet->packet == nullptr) { + if (serialized_packet->encrypted_buffer == nullptr) { // We failed to serialize the packet, so close the connection. // CloseConnection does not send close packet, so no infinite loop here. - CloseConnection(QUIC_ENCRYPTION_FAILURE, false); + // TODO(ianswett): This is actually an internal error, not an encryption + // failure. + CloseConnection(QUIC_ENCRYPTION_FAILURE, ConnectionCloseSource::FROM_SELF); return; } if (serialized_packet->is_fec_packet && fec_alarm_->IsSet()) { // If an FEC packet is serialized with the FEC alarm set, cancel the alarm. fec_alarm_->Cancel(); } - SendOrQueuePacket(QueuedPacket(*serialized_packet)); + SendOrQueuePacket(serialized_packet); +} + +void QuicConnection::OnUnrecoverableError(QuicErrorCode error, + ConnectionCloseSource source) { + // The packet creator or generator encountered an unrecoverable error: tear + // down local connection state immediately. + CloseConnection(error, source); } void QuicConnection::OnResetFecGroup() { @@ -1772,6 +1791,10 @@ void QuicConnection::OnRttChange() { packet_generator_.OnRttChange(rtt); } +void QuicConnection::OnPathDegrading() { + visitor_->OnPathDegrading(); +} + void QuicConnection::OnHandshakeComplete() { sent_packet_manager_.SetHandshakeConfirmed(); // The client should immediately ack the SHLO to confirm the handshake is @@ -1783,50 +1806,46 @@ void QuicConnection::OnHandshakeComplete() { } } -void QuicConnection::SendOrQueuePacket(QueuedPacket packet) { +void QuicConnection::SendOrQueuePacket(SerializedPacket* packet) { // The caller of this function is responsible for checking CanWrite(). - if (packet.serialized_packet.packet == nullptr) { - LOG(DFATAL) - << "packet.serialized_packet.packet == nullptr in to SendOrQueuePacket"; + if (packet->encrypted_buffer == nullptr) { + QUIC_BUG << "packet.encrypted_buffer == nullptr in to SendOrQueuePacket"; return; } - sent_entropy_manager_.RecordPacketEntropyHash( - packet.serialized_packet.packet_number, - packet.serialized_packet.entropy_hash); + sent_entropy_manager_.RecordPacketEntropyHash(packet->packet_number, + packet->entropy_hash); // If there are already queued packets, queue this one immediately to ensure // it's written in sequence number order. - if (!queued_packets_.empty() || !WritePacket(&packet)) { + if (!queued_packets_.empty() || !WritePacket(packet)) { // Take ownership of the underlying encrypted packet. - if (!packet.serialized_packet.packet->owns_buffer()) { - scoped_ptr encrypted_deleter( - packet.serialized_packet.packet); - packet.serialized_packet.packet = - packet.serialized_packet.packet->Clone(); - } - queued_packets_.push_back(packet); + packet->encrypted_buffer = QuicUtils::CopyBuffer(*packet); + queued_packets_.push_back(*packet); + packet->retransmittable_frames.clear(); } + QuicUtils::ClearSerializedPacket(packet); // If a forward-secure encrypter is available but is not being used and the // next packet number is the first packet which requires // forward security, start using the forward-secure encrypter. if (encryption_level_ != ENCRYPTION_FORWARD_SECURE && has_forward_secure_encrypter_ && - packet.serialized_packet.packet_number >= - first_required_forward_secure_packet_ - 1) { + packet->packet_number >= first_required_forward_secure_packet_ - 1) { SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); } } -PeerAddressChangeType QuicConnection::DeterminePeerAddressChangeType() { - return UNKNOWN; +void QuicConnection::OnPingTimeout() { + if (!retransmission_alarm_->IsSet()) { + SendPing(); + } } void QuicConnection::SendPing() { - if (retransmission_alarm_->IsSet()) { - return; - } + ScopedPacketBundler bundler(this, ack_queued_ ? SEND_ACK : NO_ACK); packet_generator_.AddControlFrame(QuicFrame(QuicPingFrame())); + // Send PING frame immediately, without checking for congestion window bounds. + packet_generator_.FlushAllQueuedFrames(); } void QuicConnection::SendAck() { @@ -1958,6 +1977,13 @@ void QuicConnection::MaybeProcessRevivedPacket() { QuicPacketHeader revived_header; char revived_payload[kMaxPacketSize]; size_t len = group->Revive(&revived_header, revived_payload, kMaxPacketSize); + if (!received_packet_manager_.IsAwaitingPacket( + revived_header.packet_number)) { + // Close this FEC group because all packets in the group has been received. + group_map_.erase(last_header_.fec_group); + delete group; + return; + } revived_header.public_header.connection_id = connection_id_; revived_header.public_header.connection_id_length = last_header_.public_header.connection_id_length; @@ -1987,8 +2013,7 @@ void QuicConnection::MaybeProcessRevivedPacket() { QuicFecGroup* QuicConnection::GetFecGroup() { QuicFecGroupNumber fec_group_num = last_header_.fec_group; if (fec_group_num == 0 || - (FLAGS_quic_drop_non_awaited_packets && - fec_group_num < + (fec_group_num < received_packet_manager_.peer_least_packet_awaiting_ack() && !ContainsKey(group_map_, fec_group_num))) { // If the group number is below peer_least_packet_awaiting_ack and this @@ -2013,16 +2038,16 @@ QuicFecGroup* QuicConnection::GetFecGroup() { return group_map_[fec_group_num]; } -void QuicConnection::SendConnectionClose(QuicErrorCode error) { - SendConnectionCloseWithDetails(error, string()); -} - void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error, const string& details) { + if (!connected_) { + DVLOG(1) << "Connection is already closed."; + return; + } // If we're write blocked, WritePacket() will not send, but will capture the // serialized packet. SendConnectionClosePacket(error, details); - CloseConnection(error, false); + CloseConnection(error, ConnectionCloseSource::FROM_SELF); } void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, @@ -2030,12 +2055,6 @@ void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, DVLOG(1) << ENDPOINT << "Force closing " << connection_id() << " with error " << QuicUtils::ErrorToString(error) << " (" << error << ") " << details; - // Don't send explicit connection close packets for timeouts. - // This is particularly important on mobile, where connections are short. - if (silent_close_enabled_ && - error == QuicErrorCode::QUIC_CONNECTION_TIMED_OUT) { - return; - } ClearQueuedPackets(); ScopedPacketBundler ack_bundler(this, SEND_ACK); QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(); @@ -2045,16 +2064,24 @@ void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, packet_generator_.FlushAllQueuedFrames(); } -void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) { +void QuicConnection::CloseConnection(QuicErrorCode error, + ConnectionCloseSource source) { if (!connected_) { DVLOG(1) << "Connection is already closed."; return; } connected_ = false; DCHECK(visitor_ != nullptr); - visitor_->OnConnectionClosed(error, from_peer); + // TODO(rtenneti): crbug.com/546668. A temporary fix. Added a check for null + // |visitor_| to fix crash bug. Delete |visitor_| check and histogram after + // fix is merged. + if (visitor_ != nullptr) { + visitor_->OnConnectionClosed(error, source); + } else { + UMA_HISTOGRAM_BOOLEAN("Net.QuicCloseConnection.NullVisitor", true); + } if (debug_visitor_ != nullptr) { - debug_visitor_->OnConnectionClosed(error, from_peer); + debug_visitor_->OnConnectionClosed(error, source); } // Cancel the alarms so they don't trigger any action now that the // connection is closed. @@ -2089,9 +2116,7 @@ void QuicConnection::CloseFecGroupsBefore(QuicPacketNumber packet_number) { FecGroupMap::iterator it = group_map_.begin(); while (it != group_map_.end()) { // If the group doesn't protect this packet we can ignore it. - if ((!FLAGS_quic_drop_non_awaited_packets && - last_header_.fec_group == it->first) || - !it->second->IsWaitingForPacketBefore(packet_number)) { + if (!it->second->IsWaitingForPacketBefore(packet_number)) { ++it; continue; } @@ -2139,11 +2164,11 @@ bool QuicConnection::CanWriteStreamData() { return ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, pending_handshake); } -void QuicConnection::SetNetworkTimeouts(QuicTime::Delta overall_timeout, +void QuicConnection::SetNetworkTimeouts(QuicTime::Delta handshake_timeout, QuicTime::Delta idle_timeout) { - LOG_IF(DFATAL, idle_timeout > overall_timeout) + QUIC_BUG_IF(idle_timeout > handshake_timeout) << "idle_timeout:" << idle_timeout.ToMilliseconds() - << " overall_timeout:" << overall_timeout.ToMilliseconds(); + << " handshake_timeout:" << handshake_timeout.ToMilliseconds(); // Adjust the idle timeout on client and server to prevent clients from // sending requests to servers which have already closed the connection. if (perspective_ == Perspective::IS_SERVER) { @@ -2151,7 +2176,7 @@ void QuicConnection::SetNetworkTimeouts(QuicTime::Delta overall_timeout, } else if (idle_timeout > QuicTime::Delta::FromSeconds(1)) { idle_timeout = idle_timeout.Subtract(QuicTime::Delta::FromSeconds(1)); } - overall_connection_timeout_ = overall_timeout; + handshake_timeout_ = handshake_timeout; idle_network_timeout_ = idle_timeout; SetTimeoutAlarm(); @@ -2180,23 +2205,27 @@ void QuicConnection::CheckForTimeout() { << idle_network_timeout_.ToMicroseconds(); if (idle_duration >= idle_network_timeout_) { DVLOG(1) << ENDPOINT << "Connection timedout due to no network activity."; - SendConnectionCloseWithDetails(QUIC_CONNECTION_TIMED_OUT, - "No recent network activity"); + if (silent_close_enabled_) { + // Just clean up local state, don't send a connection close packet. + CloseConnection(QUIC_NETWORK_IDLE_TIMEOUT, + ConnectionCloseSource::FROM_SELF); + } else { + SendConnectionCloseWithDetails(QUIC_NETWORK_IDLE_TIMEOUT, + "No recent network activity"); + } return; } - if (!overall_connection_timeout_.IsInfinite()) { + if (!handshake_timeout_.IsInfinite()) { QuicTime::Delta connected_duration = now.Subtract(stats_.connection_creation_time); DVLOG(1) << ENDPOINT << "connection time: " << connected_duration.ToMicroseconds() - << " overall timeout: " - << overall_connection_timeout_.ToMicroseconds(); - if (connected_duration >= overall_connection_timeout_) { - DVLOG(1) << ENDPOINT - << "Connection timedout due to overall connection timeout."; - SendConnectionCloseWithDetails(QUIC_CONNECTION_OVERALL_TIMED_OUT, - "Overall timeout expired"); + << " handshake timeout: " << handshake_timeout_.ToMicroseconds(); + if (connected_duration >= handshake_timeout_) { + DVLOG(1) << ENDPOINT << "Connection timedout due to handshake timeout."; + SendConnectionCloseWithDetails(QUIC_HANDSHAKE_TIMEOUT, + "Handshake timeout expired"); return; } } @@ -2209,10 +2238,9 @@ void QuicConnection::SetTimeoutAlarm() { max(time_of_last_received_packet_, time_of_last_sent_new_packet_); QuicTime deadline = time_of_last_packet.Add(idle_network_timeout_); - if (!overall_connection_timeout_.IsInfinite()) { + if (!handshake_timeout_.IsInfinite()) { deadline = - min(deadline, - stats_.connection_creation_time.Add(overall_connection_timeout_)); + min(deadline, stats_.connection_creation_time.Add(handshake_timeout_)); } timeout_alarm_->Cancel(); @@ -2327,24 +2355,22 @@ QuicConnection::ScopedRetransmissionScheduler:: } HasRetransmittableData QuicConnection::IsRetransmittable( - const QueuedPacket& packet) { + const SerializedPacket& packet) { // Retransmitted packets retransmittable frames are owned by the unacked // packet map, but are not present in the serialized packet. if (packet.transmission_type != NOT_RETRANSMISSION || - packet.serialized_packet.retransmittable_frames != nullptr) { + !packet.retransmittable_frames.empty()) { return HAS_RETRANSMITTABLE_DATA; } else { return NO_RETRANSMITTABLE_DATA; } } -bool QuicConnection::IsTerminationPacket(const QueuedPacket& packet) { - const RetransmittableFrames* retransmittable_frames = - packet.serialized_packet.retransmittable_frames; - if (retransmittable_frames == nullptr) { +bool QuicConnection::IsTerminationPacket(const SerializedPacket& packet) { + if (packet.retransmittable_frames.empty()) { return false; } - for (const QuicFrame& frame : retransmittable_frames->frames()) { + for (const QuicFrame& frame : packet.retransmittable_frames) { if (frame.type == CONNECTION_CLOSE_FRAME) { return true; } @@ -2364,7 +2390,7 @@ void QuicConnection::SetMtuDiscoveryTarget(QuicByteCount target) { QuicByteCount QuicConnection::LimitMaxPacketSize( QuicByteCount suggested_max_packet_size) { if (peer_address_.address().empty()) { - LOG(DFATAL) << "Attempted to use a connection without a valid peer address"; + QUIC_BUG << "Attempted to use a connection without a valid peer address"; return suggested_max_packet_size; } @@ -2419,6 +2445,59 @@ void QuicConnection::DiscoverMtu() { DCHECK(!mtu_discovery_alarm_->IsSet()); } +void QuicConnection::MaybeMigrateConnectionToNewPeerAddress() { + IPEndPoint last_peer_address; + if (FLAGS_check_peer_address_change_after_decryption) { + last_peer_address = last_packet_source_address_; + } else { + last_peer_address = IPEndPoint( + peer_ip_changed_ ? migrating_peer_ip_ : peer_address_.address(), + peer_port_changed_ ? migrating_peer_port_ : peer_address_.port()); + } + PeerAddressChangeType peer_address_change_type = + QuicUtils::DetermineAddressChangeType(peer_address_, last_peer_address); + // TODO(fayang): Currently, all peer address change type are allowed. Need to + // add a method ShouldAllowPeerAddressChange(PeerAddressChangeType type) to + // determine whehter |type| is allowed. + if (FLAGS_check_peer_address_change_after_decryption) { + if (peer_address_change_type == NO_CHANGE) { + return; + } + + IPEndPoint old_peer_address = peer_address_; + peer_address_ = last_packet_source_address_; + + DVLOG(1) << ENDPOINT << "Peer's ip:port changed from " + << old_peer_address.ToString() << " to " + << peer_address_.ToString() << ", migrating connection."; + + visitor_->OnConnectionMigration(); + sent_packet_manager_.OnConnectionMigration(peer_address_change_type); + + return; + } + + if (peer_ip_changed_ || peer_port_changed_) { + IPEndPoint old_peer_address = peer_address_; + peer_address_ = IPEndPoint( + peer_ip_changed_ ? migrating_peer_ip_ : peer_address_.address(), + peer_port_changed_ ? migrating_peer_port_ : peer_address_.port()); + + DVLOG(1) << ENDPOINT << "Peer's ip:port changed from " + << old_peer_address.ToString() << " to " + << peer_address_.ToString() << ", migrating connection."; + + visitor_->OnConnectionMigration(); + DCHECK_NE(peer_address_change_type, NO_CHANGE); + sent_packet_manager_.OnConnectionMigration(peer_address_change_type); + } +} + +void QuicConnection::OnPathClosed(QuicPathId path_id) { + // Stop receiving packets on this path. + framer_.OnPathClosed(path_id); +} + bool QuicConnection::ack_frame_updated() const { return received_packet_manager_.ack_frame_updated(); } diff --git a/src/net/quic/quic_connection.h b/src/net/quic/quic_connection.h index ac1b284c..df649b59 100644 --- a/src/net/quic/quic_connection.h +++ b/src/net/quic/quic_connection.h @@ -30,12 +30,14 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_piece.h" +#include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/quic_alarm.h" #include "net/quic/quic_blocked_writer_interface.h" #include "net/quic/quic_fec_group.h" #include "net/quic/quic_framer.h" +#include "net/quic/quic_one_block_arena.h" #include "net/quic/quic_packet_creator.h" #include "net/quic/quic_packet_generator.h" #include "net/quic/quic_packet_writer.h" @@ -114,7 +116,8 @@ 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, + ConnectionCloseSource source) = 0; // Called when the connection failed to write because the socket was blocked. virtual void OnWriteBlocked() = 0; @@ -131,6 +134,14 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { // Called when the connection receives a packet from a migrated client. virtual void OnConnectionMigration() = 0; + // Called when the peer seems unreachable over the current path. + virtual void OnPathDegrading() = 0; + + // Called after OnStreamFrame, OnRstStream, OnGoAway, OnWindowUpdateFrame, + // OnBlockedFrame, and OnCanWrite to allow post-processing once the work has + // been done. + virtual void PostProcessAfterData() = 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). @@ -150,8 +161,7 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { // points. Implementations must not mutate the state of the connection // as a result of these callbacks. class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor - : public QuicPacketCreator::DebugDelegate, - public QuicSentPacketManager::DebugDelegate { + : public QuicSentPacketManager::DebugDelegate { public: ~QuicConnectionDebugVisitor() override {} @@ -159,7 +169,6 @@ class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor virtual void OnPacketSent(const SerializedPacket& serialized_packet, QuicPacketNumber original_packet_number, TransmissionType transmission_type, - size_t encrypted_length, QuicTime sent_time) {} // Called when a packet has been received, but before it is @@ -215,6 +224,9 @@ class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor // Called when a BlockedFrame has been parsed. virtual void OnBlockedFrame(const QuicBlockedFrame& frame) {} + // Called when a PathCloseFrame has been parsed. + virtual void OnPathCloseFrame(const QuicPathCloseFrame& frame) {} + // Called when a public reset packet has been received. virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) {} @@ -228,7 +240,8 @@ class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor base::StringPiece payload) {} // Called when the connection is closed. - virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) {} + virtual void OnConnectionClosed(QuicErrorCode error, + ConnectionCloseSource source) {} // Called when the version negotiation is successful. virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) {} @@ -237,8 +250,8 @@ class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor virtual void OnSendConnectionState( const CachedNetworkParameters& cached_network_params) {} - // Called when resuming previous connection state. - virtual void OnResumeConnectionState( + // Called when a CachedNetworkParameters are recieved from the client. + virtual void OnReceiveConnectionState( const CachedNetworkParameters& cached_network_params) {} // Called when the connection parameters are set from the supplied @@ -250,6 +263,10 @@ class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor virtual void OnRttChanged(QuicTime::Delta rtt) const {} }; +// QuicConnections currently use around 1KB of polymorphic types which would +// ordinarily be on the heap. Instead, store them inline in an arena. +using QuicConnectionArena = QuicOneBlockArena<1024>; + class NET_EXPORT_PRIVATE QuicConnectionHelperInterface { public: virtual ~QuicConnectionHelperInterface() {} @@ -260,11 +277,20 @@ class NET_EXPORT_PRIVATE QuicConnectionHelperInterface { // Returns a QuicRandom to be used for all random number related functions. virtual QuicRandom* GetRandomGenerator() = 0; - // Creates a new platform-specific alarm which will be configured to - // notify |delegate| when the alarm fires. Caller takes ownership - // of the new alarm, which will not yet be "set" to fire. + // Creates a new platform-specific alarm which will be configured to notify + // |delegate| when the alarm fires. Returns an alarm allocated on the heap. + // Caller takes ownership of the new alarm, which will not yet be "set" to + // fire. virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) = 0; + // Creates a new platform-specific alarm which will be configured to notify + // |delegate| when the alarm fires. Caller takes ownership of the new alarm, + // which will not yet be "set" to fire. If |arena| is null, then the alarm + // will be created on the heap. Otherwise, it will be created in |arena|. + virtual QuicArenaScopedPtr CreateAlarm( + QuicArenaScopedPtr delegate, + QuicConnectionArena* arena) = 0; + // Returns a QuicBufferAllocator to be used for all stream frame buffers. virtual QuicBufferAllocator* GetBufferAllocator() = 0; }; @@ -281,21 +307,13 @@ class NET_EXPORT_PRIVATE QuicConnection BUNDLE_PENDING_ACK = 2, }; - class PacketWriterFactory { - public: - virtual ~PacketWriterFactory() {} - - virtual QuicPacketWriter* Create(QuicConnection* connection) const = 0; - }; - - // Constructs a new QuicConnection for |connection_id| and |address|. Invokes - // writer_factory->Create() to get a writer; |owns_writer| specifies whether - // the connection takes ownership of the returned writer. |helper| must - // outlive this connection. + // Constructs a new QuicConnection for |connection_id| and |address| using + // |writer| to write packets. |owns_writer| specifies whether the connection + // takes ownership of |writer|. |helper| must outlive this connection. QuicConnection(QuicConnectionId connection_id, IPEndPoint address, QuicConnectionHelperInterface* helper, - const PacketWriterFactory& writer_factory, + QuicPacketWriter* writer, bool owns_writer, Perspective perspective, const QuicVersionVector& supported_versions); @@ -308,6 +326,10 @@ class NET_EXPORT_PRIVATE QuicConnection virtual void OnSendConnectionState( const CachedNetworkParameters& cached_network_params); + // Called by the session when receiving connection state from the client. + virtual void OnReceiveConnectionState( + const CachedNetworkParameters& cached_network_params); + // Called by the Session when the client has provided CachedNetworkParameters. virtual void ResumeConnectionState( const CachedNetworkParameters& cached_network_params, @@ -327,12 +349,12 @@ class NET_EXPORT_PRIVATE QuicConnection // If |listener| is provided, then it will be informed once ACKs have been // received for all the packets written in this call. // The |listener| is not owned by the QuicConnection and must outlive it. - QuicConsumedData SendStreamData(QuicStreamId id, - QuicIOVector iov, - QuicStreamOffset offset, - bool fin, - FecProtection fec_protection, - QuicAckListenerInterface* listener); + virtual QuicConsumedData SendStreamData(QuicStreamId id, + QuicIOVector iov, + QuicStreamOffset offset, + bool fin, + FecProtection fec_protection, + QuicAckListenerInterface* listener); // Send a RST_STREAM frame to the peer. virtual void SendRstStream(QuicStreamId id, @@ -345,6 +367,9 @@ class NET_EXPORT_PRIVATE QuicConnection // Send a WINDOW_UPDATE frame to the peer. virtual void SendWindowUpdate(QuicStreamId id, QuicStreamOffset byte_offset); + // Send a PATH_CLOSE frame to the peer. + virtual void SendPathClose(QuicPathId path_id); + // Sends the connection close packet without affecting the state of the // connection. This should only be called if the session is actively being // destroyed: otherwise call SendConnectionCloseWithDetails instead. @@ -353,11 +378,10 @@ class NET_EXPORT_PRIVATE QuicConnection // Sends a connection close frame to the peer, and closes the connection by // calling CloseConnection(notifying the visitor as it does so). - virtual void SendConnectionClose(QuicErrorCode error); virtual void SendConnectionCloseWithDetails(QuicErrorCode error, const std::string& details); // Notifies the visitor of the close and marks the connection as disconnected. - void CloseConnection(QuicErrorCode error, bool from_peer) override; + void CloseConnection(QuicErrorCode error, ConnectionCloseSource source); // Sends a GOAWAY frame. Does nothing if a GOAWAY frame has already been sent. virtual void SendGoAway(QuicErrorCode error, @@ -390,6 +414,10 @@ class NET_EXPORT_PRIVATE QuicConnection // Set the packet writer. void SetQuicPacketWriter(QuicPacketWriter* writer, bool owns_writer) { + DCHECK(writer != nullptr); + if (writer_ != nullptr && owns_writer_) { + delete writer_; + } writer_ = writer; owns_writer_ = owns_writer; } @@ -428,6 +456,7 @@ class NET_EXPORT_PRIVATE QuicConnection bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override; bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override; bool OnBlockedFrame(const QuicBlockedFrame& frame) override; + bool OnPathCloseFrame(const QuicPathCloseFrame& frame) override; void OnFecData(base::StringPiece redundnancy) override; void OnPacketComplete() override; @@ -439,11 +468,14 @@ class NET_EXPORT_PRIVATE QuicConnection // QuicPacketCreator::DelegateInterface void OnSerializedPacket(SerializedPacket* packet) override; + void OnUnrecoverableError(QuicErrorCode error, + ConnectionCloseSource source) override; void OnResetFecGroup() override; // QuicSentPacketManager::NetworkChangeVisitor void OnCongestionWindowChange() override; void OnRttChange() override; + void OnPathDegrading() override; // Called by the crypto stream when the handshake completes. In the server's // case this is when the SHLO has been ACKed. Clients call this on receipt of @@ -456,9 +488,12 @@ class NET_EXPORT_PRIVATE QuicConnection } 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); } + // Used in Chromium, but not internally. + void set_creator_debug_delegate(QuicPacketCreator::DebugDelegate* visitor) { + packet_generator_.set_debug_delegate(visitor); + } const IPEndPoint& self_address() const { return self_address_; } const IPEndPoint& peer_address() const { return peer_address_; } QuicConnectionId connection_id() const { return connection_id_; } @@ -498,15 +533,19 @@ class NET_EXPORT_PRIVATE QuicConnection // Returns true if the connection has queued packets or frames. bool HasQueuedData() const; - // Sets the overall and idle state connection timeouts. - void SetNetworkTimeouts(QuicTime::Delta overall_timeout, + // Sets the handshake and idle state connection timeouts. + void SetNetworkTimeouts(QuicTime::Delta handshake_timeout, QuicTime::Delta idle_timeout); // If the connection has timed out, this will close the connection. // Otherwise, it will reschedule the timeout alarm. void CheckForTimeout(); - // Sends a ping, and resets the ping alarm. + // Called when the ping alarm fires. Causes a ping frame to be sent only + // if the retransmission alarm is not running. + void OnPingTimeout(); + + // Sends a ping frame. void SendPing(); // Sets up a packet with an QuicAckFrame and sends it out. @@ -635,35 +674,16 @@ class NET_EXPORT_PRIVATE QuicConnection bool ack_frame_updated() const; - protected: - // Packets which have not been written to the wire. - struct QueuedPacket { - explicit QueuedPacket(SerializedPacket packet); - QueuedPacket(SerializedPacket packet, - TransmissionType transmission_type, - QuicPacketNumber original_packet_number); - - SerializedPacket serialized_packet; - TransmissionType transmission_type; - // The packet's original packet number if it is a retransmission. - // Otherwise it must be 0. - QuicPacketNumber original_packet_number; - }; - - // 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 - // can be handled, false otherwise. Also migrates the connection if the packet - // can be handled and peer address changes. - virtual bool ProcessValidatedPacket(const QuicPacketHeader& header); + QuicConnectionHelperInterface* helper() { return helper_; } + protected: // Send a packet to the peer, and takes ownership of the packet if the packet // cannot be written immediately. - virtual void SendOrQueuePacket(QueuedPacket packet); - - QuicConnectionHelperInterface* helper() { return helper_; } + virtual void SendOrQueuePacket(SerializedPacket* packet); - // On peer address changes, determine and return the change type. - virtual PeerAddressChangeType DeterminePeerAddressChangeType(); + // Migrate the connection if peer address changes. This function should only + // be called after the packet is validated. + virtual void MaybeMigrateConnectionToNewPeerAddress(); // Selects and updates the version of the protocol being used by selecting a // version from |available_versions| which is also supported. Returns true if @@ -674,15 +694,28 @@ class NET_EXPORT_PRIVATE QuicConnection bool peer_port_changed() const { return peer_port_changed_; } - const IPAddressNumber& migrating_peer_ip() const { - return migrating_peer_ip_; + const IPAddress& migrating_peer_ip() const { return migrating_peer_ip_; } + + uint16_t migrating_peer_port() const { return migrating_peer_port_; } + + const IPEndPoint& last_packet_source_address() const { + return last_packet_source_address_; + } + + // Returns the current per-packet options for the connection. + PerPacketOptions* per_packet_options() { return per_packet_options_; } + // Sets the current per-packet options for the connection. The QuicConnection + // does not take ownership of |options|; |options| must live for as long as + // the QuicConnection is in use. + void set_per_packet_options(PerPacketOptions* options) { + per_packet_options_ = options; } private: friend class test::QuicConnectionPeer; friend class test::PacketSavingConnection; - typedef std::list QueuedPacketList; + typedef std::list QueuedPacketList; typedef std::map FecGroupMap; // Writes the given packet to socket, encrypted with packet's @@ -692,11 +725,7 @@ class NET_EXPORT_PRIVATE QuicConnection // retransmittable frames to nullptr. // Saves the connection close packet for later transmission, even if the // writer is write blocked. - bool WritePacket(QueuedPacket* packet); - - // Does the main work of WritePacket, but does not delete the packet or - // retransmittable frames upon success. - bool WritePacketInner(QueuedPacket* packet); + bool WritePacket(SerializedPacket* packet); // Make sure an ack we got from our peer is sane. // Returns nullptr for valid acks or an error std::string if it was invalid. @@ -714,7 +743,7 @@ class NET_EXPORT_PRIVATE QuicConnection // Clears any accumulated frames from the last received packet. void ClearLastFrames(); - // Deletes and clears any QueuedPackets. + // Deletes and clears any queued packets. void ClearQueuedPackets(); // Closes the connection if the sent or received packet manager are tracking @@ -729,7 +758,7 @@ class NET_EXPORT_PRIVATE QuicConnection void WritePendingRetransmissions(); // Returns true if the packet should be discarded and not sent. - bool ShouldDiscardPacket(const QueuedPacket& packet); + bool ShouldDiscardPacket(const SerializedPacket& packet); // Queues |packet| in the hopes that it can be decrypted in the // future, when a new key is installed. @@ -782,8 +811,8 @@ class NET_EXPORT_PRIVATE QuicConnection void CheckForAddressMigration(const IPEndPoint& self_address, const IPEndPoint& peer_address); - HasRetransmittableData IsRetransmittable(const QueuedPacket& packet); - bool IsTerminationPacket(const QueuedPacket& packet); + HasRetransmittableData IsRetransmittable(const SerializedPacket& packet); + bool IsTerminationPacket(const SerializedPacket& packet); // Set the size of the packet we are targeting while doing path MTU discovery. void SetMtuDiscoveryTarget(QuicByteCount target); @@ -792,8 +821,23 @@ class NET_EXPORT_PRIVATE QuicConnection // the largest supported by the protocol or the packet writer. QuicByteCount LimitMaxPacketSize(QuicByteCount suggested_max_packet_size); + // Called when |path_id| is considered as closed because either a PATH_CLOSE + // frame is sent or received. Stops receiving packets on closed path. Drops + // receive side of a closed path, and packets with retransmittable frames on a + // closed path are marked as retransmissions which will be transmitted on + // other paths. + // TODO(fayang): complete OnPathClosed once QuicMultipathSentPacketManager and + // QuicMultipathReceivedPacketManager are landed in QuicConnection. + void OnPathClosed(QuicPathId path_id); + + // Do any work which logically would be done in OnPacket but can not be + // safely done until the packet is validated. Returns true if packet can be + // handled, false otherwise. + bool ProcessValidatedPacket(const QuicPacketHeader& header); + QuicFramer framer_; QuicConnectionHelperInterface* helper_; // Not owned. + PerPacketOptions* per_packet_options_; // Not owned. QuicPacketWriter* writer_; // Owned or not depending on |owns_writer_|. bool owns_writer_; // Encryption level for new packets. Should only be changed via @@ -814,7 +858,7 @@ class NET_EXPORT_PRIVATE QuicConnection IPEndPoint peer_address_; // Used to store latest peer IP address for IP address migration. - IPAddressNumber migrating_peer_ip_; + IPAddress migrating_peer_ip_; // Used to store latest peer port to possibly migrate to later. uint16_t migrating_peer_port_; @@ -849,8 +893,10 @@ class NET_EXPORT_PRIVATE QuicConnection bool pending_version_negotiation_packet_; // When packets could not be sent because the socket was not writable, - // they are added to this list. All corresponding frames are in - // unacked_packets_ if they are to be retransmitted. + // they are added to this std::list. All corresponding frames are in + // unacked_packets_ if they are to be retransmitted. Packets encrypted_buffer + // fields are owned by the QueuedPacketList, in order to ensure they outlast + // the original scope of the SerializedPacket. QueuedPacketList queued_packets_; // If true, then crypto packets will be saved as termination packets. @@ -859,7 +905,9 @@ class NET_EXPORT_PRIVATE QuicConnection // Contains the connection close packets if the connection has been closed. scoped_ptr> termination_packets_; - // When true, the connection does not send a close packet on timeout. + // When true, the connection does not send a close packet on idle timeout due + // to lack of network activity. + // This is particularly important on mobile, where connections are short. bool silent_close_enabled_; FecGroupMap group_map_; @@ -885,22 +933,25 @@ class NET_EXPORT_PRIVATE QuicConnection // Indicates the retransmission alarm needs to be set. bool pending_retransmission_alarm_; + // Arena to store class implementations within the QuicConnection. + QuicConnectionArena arena_; + // An alarm that fires when an ACK should be sent to the peer. - scoped_ptr ack_alarm_; + QuicArenaScopedPtr ack_alarm_; // An alarm that fires when a packet needs to be retransmitted. - scoped_ptr retransmission_alarm_; + QuicArenaScopedPtr retransmission_alarm_; // An alarm that is scheduled when the SentPacketManager requires a delay // before sending packets and fires when the packet may be sent. - scoped_ptr send_alarm_; + QuicArenaScopedPtr send_alarm_; // An alarm that is scheduled when the connection can still write and there // may be more data to send. - scoped_ptr resume_writes_alarm_; + QuicArenaScopedPtr resume_writes_alarm_; // An alarm that fires when the connection may have timed out. - scoped_ptr timeout_alarm_; + QuicArenaScopedPtr timeout_alarm_; // An alarm that fires when a ping should be sent. - scoped_ptr ping_alarm_; + QuicArenaScopedPtr ping_alarm_; // An alarm that fires when an MTU probe should be sent. - scoped_ptr mtu_discovery_alarm_; + QuicArenaScopedPtr mtu_discovery_alarm_; // Neither visitor is owned by this class. QuicConnectionVisitorInterface* visitor_; @@ -909,12 +960,12 @@ class NET_EXPORT_PRIVATE QuicConnection QuicPacketGenerator packet_generator_; // An alarm that fires when an FEC packet should be sent. - scoped_ptr fec_alarm_; + QuicArenaScopedPtr fec_alarm_; - // Network idle time before we kill of this connection. + // Network idle time before this connection is closed. QuicTime::Delta idle_network_timeout_; - // Overall connection timeout. - QuicTime::Delta overall_connection_timeout_; + // The connection will wait this long for the handshake to complete. + QuicTime::Delta handshake_timeout_; // Statistics for this session. QuicConnectionStats stats_; @@ -964,6 +1015,12 @@ class NET_EXPORT_PRIVATE QuicConnection // We do not support connection migration when the self port changed. bool self_port_changed_; + // Destination address of the last received packet. + IPEndPoint last_packet_destination_address_; + + // Source address of the last received packet. + IPEndPoint last_packet_source_address_; + // Set to false if the connection should not send truncated connection IDs to // the peer, even if the peer supports it. bool can_truncate_connection_ids_; @@ -994,6 +1051,9 @@ class NET_EXPORT_PRIVATE QuicConnection // Whether a GoAway has been received. bool goaway_received_; + // If true, multipath is enabled for this connection. + bool multipath_enabled_; + DISALLOW_COPY_AND_ASSIGN(QuicConnection); }; diff --git a/src/net/quic/quic_connection_stats.cc b/src/net/quic/quic_connection_stats.cc index 2c98fcca..f3144522 100644 --- a/src/net/quic/quic_connection_stats.cc +++ b/src/net/quic/quic_connection_stats.cc @@ -41,6 +41,9 @@ QuicConnectionStats::QuicConnectionStats() tcp_loss_events(0), connection_creation_time(QuicTime::Zero()) {} +QuicConnectionStats::QuicConnectionStats(const QuicConnectionStats& other) = + default; + QuicConnectionStats::~QuicConnectionStats() {} } // namespace net diff --git a/src/net/quic/quic_connection_stats.h b/src/net/quic/quic_connection_stats.h index 655ab321..27876129 100644 --- a/src/net/quic/quic_connection_stats.h +++ b/src/net/quic/quic_connection_stats.h @@ -19,6 +19,7 @@ namespace net { // Structure to hold stats for a QuicConnection. struct NET_EXPORT_PRIVATE QuicConnectionStats { QuicConnectionStats(); + QuicConnectionStats(const QuicConnectionStats& other); ~QuicConnectionStats(); NET_EXPORT_PRIVATE friend std::ostream& operator<<( diff --git a/src/net/quic/quic_crypto_client_stream.cc b/src/net/quic/quic_crypto_client_stream.cc index 2942eef7..3e52079f 100644 --- a/src/net/quic/quic_crypto_client_stream.cc +++ b/src/net/quic/quic_crypto_client_stream.cc @@ -12,7 +12,6 @@ #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/null_encrypter.h" -#include "net/quic/quic_client_session_base.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" @@ -42,8 +41,7 @@ void AppendFixed(CryptoHandshakeMessage* message) { } // namespace -QuicCryptoClientStreamBase::QuicCryptoClientStreamBase( - QuicClientSessionBase* session) +QuicCryptoClientStreamBase::QuicCryptoClientStreamBase(QuicSession* session) : QuicCryptoStream(session) {} QuicCryptoClientStream::ChannelIDSourceCallbackImpl:: @@ -103,9 +101,10 @@ void QuicCryptoClientStream::ProofVerifierCallbackImpl::Cancel() { QuicCryptoClientStream::QuicCryptoClientStream( const QuicServerId& server_id, - QuicClientSessionBase* session, + QuicSession* session, ProofVerifyContext* verify_context, - QuicCryptoClientConfig* crypto_config) + QuicCryptoClientConfig* crypto_config, + ProofHandler* proof_handler) : QuicCryptoClientStreamBase(session), next_state_(STATE_IDLE), num_client_hellos_(0), @@ -117,6 +116,7 @@ QuicCryptoClientStream::QuicCryptoClientStream( channel_id_source_callback_(nullptr), verify_context_(verify_context), proof_verify_callback_(nullptr), + proof_handler_(proof_handler), stateless_reject_received_(false) { DCHECK_EQ(Perspective::IS_CLIENT, session->connection()->perspective()); } @@ -258,6 +258,8 @@ void QuicCryptoClientStream::DoInitialize( // expiration because it may have been a while since we last verified // the proof. DCHECK(crypto_config_->proof_verifier()); + // Track proof verification time when cached server config is used. + proof_verify_start_time_ = base::TimeTicks::Now(); // If the cached state needs to be verified, do it now. next_state_ = STATE_VERIFY_PROOF; } else { @@ -275,7 +277,8 @@ void QuicCryptoClientStream::DoSendCHLO( next_state_ = STATE_NONE; if (session()->connection()->connected()) { session()->connection()->CloseConnection( - QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, false); + QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, + ConnectionCloseSource::FROM_SELF); } return; } @@ -298,15 +301,14 @@ void QuicCryptoClientStream::DoSendCHLO( // inchoate or subsequent hello. session()->config()->ToHandshakeMessage(&out); - // This block and function should be removed after removing QUIC_VERSION_25. - if (FLAGS_quic_require_fix) { - AppendFixed(&out); - } + // This call and function should be removed after removing QUIC_VERSION_25. + AppendFixed(&out); if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { crypto_config_->FillInchoateClientHello( server_id_, session()->connection()->supported_versions().front(), - cached, &crypto_negotiated_params_, &out); + cached, session()->connection()->random_generator(), + &crypto_negotiated_params_, &out); // Pad the inchoate client hello to fill up a packet. const QuicByteCount kFramingOverhead = 50; // A rough estimate. const QuicByteCount max_packet_size = @@ -346,7 +348,6 @@ void QuicCryptoClientStream::DoSendCHLO( session()->connection()->clock()->WallNow(), session()->connection()->random_generator(), channel_id_key_.get(), &crypto_negotiated_params_, &out, &error_details); - if (error != QUIC_NO_ERROR) { // Flush the cached config so that, if it's bad, the server has a // chance to send us another in the future. @@ -356,7 +357,7 @@ void QuicCryptoClientStream::DoSendCHLO( } channel_id_sent_ = (channel_id_key_.get() != nullptr); if (cached->proof_verify_details()) { - client_session()->OnProofVerifyDetailsAvailable( + proof_handler_->OnProofVerifyDetailsAvailable( *cached->proof_verify_details()); } next_state_ = STATE_RECV_SHLO; @@ -480,9 +481,13 @@ QuicAsyncStatus QuicCryptoClientStream::DoVerifyProof( void QuicCryptoClientStream::DoVerifyProofComplete( QuicCryptoClientConfig::CachedState* cached) { + if (!proof_verify_start_time_.is_null()) { + UMA_HISTOGRAM_TIMES("Net.QuicSession.VerifyProofTime.CachedServerConfig", + base::TimeTicks::Now() - proof_verify_start_time_); + } if (!verify_ok_) { if (verify_details_.get()) { - client_session()->OnProofVerifyDetailsAvailable(*verify_details_); + proof_handler_->OnProofVerifyDetailsAvailable(*verify_details_); } if (num_client_hellos_ == 0) { cached->Clear(); @@ -645,7 +650,7 @@ void QuicCryptoClientStream::DoInitializeServerConfigUpdate( void QuicCryptoClientStream::SetCachedProofValid( QuicCryptoClientConfig::CachedState* cached) { cached->SetProofValid(); - client_session()->OnProofValid(*cached); + proof_handler_->OnProofValid(*cached); } bool QuicCryptoClientStream::RequiresChannelID( @@ -672,8 +677,4 @@ bool QuicCryptoClientStream::RequiresChannelID( return false; } -QuicClientSessionBase* QuicCryptoClientStream::client_session() { - return reinterpret_cast(session()); -} - } // namespace net diff --git a/src/net/quic/quic_crypto_client_stream.h b/src/net/quic/quic_crypto_client_stream.h index 01fd8d3b..16f0da9e 100644 --- a/src/net/quic/quic_crypto_client_stream.h +++ b/src/net/quic/quic_crypto_client_stream.h @@ -19,8 +19,6 @@ namespace net { -class QuicClientSessionBase; - namespace test { class CryptoTestUtils; class QuicChromiumClientSessionPeer; @@ -28,7 +26,7 @@ class QuicChromiumClientSessionPeer; class NET_EXPORT_PRIVATE QuicCryptoClientStreamBase : public QuicCryptoStream { public: - explicit QuicCryptoClientStreamBase(QuicClientSessionBase* session); + explicit QuicCryptoClientStreamBase(QuicSession* session); ~QuicCryptoClientStreamBase() override{}; @@ -51,10 +49,31 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream // the server being unwilling to send it without a valid source-address // token. static const int kMaxClientHellos = 3; + + // ProofHandler is an interface that handles callbacks from the crypto + // stream when the client has proof verification details of the server. + class NET_EXPORT_PRIVATE ProofHandler { + public: + virtual ~ProofHandler() {} + + // 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. + 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; + }; + QuicCryptoClientStream(const QuicServerId& server_id, - QuicClientSessionBase* session, + QuicSession* session, ProofVerifyContext* verify_context, - QuicCryptoClientConfig* crypto_config); + QuicCryptoClientConfig* crypto_config, + ProofHandler* proof_handler); ~QuicCryptoClientStream() override; @@ -183,8 +202,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream // and the client config settings also allow sending a ChannelID. bool RequiresChannelID(QuicCryptoClientConfig::CachedState* cached); - QuicClientSessionBase* client_session(); - State next_state_; // num_client_hellos_ contains the number of client hello messages that this // connection has sent. @@ -223,6 +240,9 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream // proof_verify_callback_ contains the callback object that we passed to an // asynchronous proof verification. The ProofVerifier owns this object. ProofVerifierCallbackImpl* proof_verify_callback_; + // proof_handler_ contains the callback object used by a quic client + // for proof verification. It is not owned by this class. + ProofHandler* proof_handler_; // These members are used to store the result of an asynchronous proof // verification. These members must not be used after @@ -236,6 +256,8 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream // STATE_VERIFY_PROOF*, and subsequent STATE_SEND_CHLO state. bool stateless_reject_received_; + base::TimeTicks proof_verify_start_time_; + DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientStream); }; diff --git a/src/net/quic/quic_crypto_server_stream.cc b/src/net/quic/quic_crypto_server_stream.cc index fc8281ea..f53602e1 100644 --- a/src/net/quic/quic_crypto_server_stream.cc +++ b/src/net/quic/quic_crypto_server_stream.cc @@ -38,9 +38,8 @@ bool HasFixedTag(const CryptoHandshakeMessage& message) { } } // namespace -void ServerHelloNotifier::OnPacketAcked( - int acked_bytes, - QuicTime::Delta delta_largest_observed) { +void ServerHelloNotifier::OnPacketAcked(int acked_bytes, + QuicTime::Delta ack_delay_time) { // The SHLO is sent in one packet. server_stream_->OnServerHelloAcked(); } @@ -70,9 +69,7 @@ void QuicCryptoServerStream::CancelOutstandingCallbacks() { // Detach from the validation callback. Calling this multiple times is safe. if (validate_client_hello_cb_ != nullptr) { validate_client_hello_cb_->Cancel(); - if (FLAGS_quic_set_client_hello_cb_nullptr) { - validate_client_hello_cb_ = nullptr; - } + validate_client_hello_cb_ = nullptr; } } @@ -159,7 +156,8 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage( << session()->connection()->connection_id() << " because of a stateless reject."; session()->connection()->CloseConnection( - QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, /* from_peer */ false); + QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, + ConnectionCloseSource::FROM_SELF); } return; } diff --git a/src/net/quic/quic_crypto_server_stream.h b/src/net/quic/quic_crypto_server_stream.h index e8e708a5..fc9a7f38 100644 --- a/src/net/quic/quic_crypto_server_stream.h +++ b/src/net/quic/quic_crypto_server_stream.h @@ -36,8 +36,7 @@ class NET_EXPORT_PRIVATE ServerHelloNotifier : public QuicAckListenerInterface { explicit ServerHelloNotifier(QuicCryptoServerStreamBase* stream) : server_stream_(stream) {} - void OnPacketAcked(int acked_bytes, - QuicTime::Delta delta_largest_observed) override; + void OnPacketAcked(int acked_bytes, QuicTime::Delta ack_delay_time) override; void OnPacketRetransmitted(int retransmitted_bytes) override; diff --git a/src/net/quic/quic_crypto_stream.cc b/src/net/quic/quic_crypto_stream.cc index be4a3ca8..560f1790 100644 --- a/src/net/quic/quic_crypto_stream.cc +++ b/src/net/quic/quic_crypto_stream.cc @@ -60,10 +60,6 @@ void QuicCryptoStream::OnDataAvailable() { } } -SpdyPriority QuicCryptoStream::Priority() const { - return net::kV3HighestPriority; -} - void QuicCryptoStream::SendHandshakeMessage( const CryptoHandshakeMessage& message) { SendHandshakeMessage(message, nullptr); @@ -93,6 +89,20 @@ bool QuicCryptoStream::ExportKeyingMaterial(StringPiece label, result); } +bool QuicCryptoStream::ExportTokenBindingKeyingMaterial(string* result) const { + if (!encryption_established()) { + QUIC_BUG << "ExportTokenBindingKeyingMaterial was called before initial" + << "encryption was established."; + return false; + } + if (!FLAGS_quic_save_initial_subkey_secret) { + return false; + } + return CryptoUtils::ExportKeyingMaterial( + crypto_negotiated_params_.initial_subkey_secret, "EXPORTER-Token-Binding", + /* context= */ "", 32, result); +} + const QuicCryptoNegotiatedParameters& QuicCryptoStream::crypto_negotiated_params() const { return crypto_negotiated_params_; diff --git a/src/net/quic/quic_crypto_stream.h b/src/net/quic/quic_crypto_stream.h index e794cb80..a5bf812a 100644 --- a/src/net/quic/quic_crypto_stream.h +++ b/src/net/quic/quic_crypto_stream.h @@ -41,7 +41,6 @@ class NET_EXPORT_PRIVATE QuicCryptoStream // ReliableQuicStream implementation void OnDataAvailable() override; - SpdyPriority Priority() const override; // Sends |message| to the peer. // TODO(wtc): return a success/failure status. @@ -60,6 +59,16 @@ class NET_EXPORT_PRIVATE QuicCryptoStream size_t result_len, std::string* result) const; + // Performs key extraction for Token Binding. Unlike ExportKeyingMaterial, + // this function can be called before forward-secure encryption is + // established. Returns false if initial encryption has not been established, + // and true on success. + // + // Since this depends only on the initial keys, a signature over it can be + // repurposed by an attacker who obtains the client's or server's DH private + // value. + bool ExportTokenBindingKeyingMaterial(std::string* result) const; + bool encryption_established() const { return encryption_established_; } bool handshake_confirmed() const { return handshake_confirmed_; } diff --git a/src/net/quic/quic_flags.cc b/src/net/quic/quic_flags.cc index e3302308..66868e71 100644 --- a/src/net/quic/quic_flags.cc +++ b/src/net/quic/quic_flags.cc @@ -14,7 +14,7 @@ bool FLAGS_use_early_return_when_verifying_chlo = true; // If true, QUIC connections will support FEC protection of data while sending // packets, to reduce latency of data delivery to the application. The client // must also request FEC protection for the server to use FEC. -bool FLAGS_enable_quic_fec = false; +bool FLAGS_enable_quic_fec = true; // When true, defaults to BBR congestion control instead of Cubic. bool FLAGS_quic_use_bbr_congestion_control = false; @@ -41,15 +41,14 @@ int64_t FLAGS_quic_time_wait_list_max_connections = 600000; // Enables server-side support for QUIC stateless rejects. bool FLAGS_enable_quic_stateless_reject_support = true; -// If ture, allow Ack Decimation to be used for QUIC when requested by the -// client connection option ACKD. -bool FLAGS_quic_ack_decimation = true; +// This flag is not in use, just to keep consistency for shared code. +bool FLAGS_quic_always_log_bugs_for_tests = false; // If true, flow controller may grow the receive window size if necessary. bool FLAGS_quic_auto_tune_receive_window = true; -// Limits QUIC's max CWND to 200 packets. -bool FLAGS_quic_limit_max_cwnd = true; +// If true, multipath is enabled for the connection. +bool FLAGS_quic_enable_multipath = false; // If true, require handshake confirmation for QUIC connections, functionally // disabling 0-rtt handshakes. @@ -68,14 +67,6 @@ bool FLAGS_quic_measure_headers_hol_blocking_time = true; // Disable QUIC's userspace pacing. bool FLAGS_quic_disable_pacing = false; -// If true, Use QUIC's GeneralLossAlgorithm implementation instead of -// TcpLossAlgorithm or TimeLossAlgorithm. -bool FLAGS_quic_general_loss_algorithm = true; - -// If true, only migrate QUIC connections when client address changes are -// considered to be caused by NATs. -bool FLAGS_quic_disable_non_nat_address_migration = true; - // If true, QUIC connections will timeout when packets are not being recieved, // even if they are being sent. bool FLAGS_quic_use_new_idle_timeout = true; @@ -87,14 +78,6 @@ bool FLAGS_quic_use_stream_sequencer_buffer = true; // If true, don't send QUIC packets if the send alarm is set. bool FLAGS_quic_respect_send_alarm2 = true; -// If ture, sets callback pointer to nullptr after calling Cancel() in -// QuicCryptoServerStream::CancelOutstandingCallbacks. -bool FLAGS_quic_set_client_hello_cb_nullptr = true; - -// If treu, Only track a single retransmission in QUIC's TransmissionInfo -// struct. -bool FLAGS_quic_track_single_retransmission = true; - // If true, allow each quic stream to write 16k blocks rather than doing a round // robin of one packet per session when ack clocked or paced. bool FLAGS_quic_batch_writes = true; @@ -106,15 +89,18 @@ bool FLAGS_quic_block_unencrypted_writes = true; // If true, Close the connection instead of writing unencrypted stream data. bool FLAGS_quic_never_write_unencrypted_data = true; +// If true, clear the FEC group instead of sending it with ENCRYPTION_NONE. +// Close the connection if we ever try to serialized unencrypted FEC. +bool FLAGS_quic_no_unencrypted_fec = true; + // If true, reject any incoming QUIC which does not have the FIXD tag. bool FLAGS_quic_require_fix = true; // If true, QUIC supports sending trailers from Server to Client. bool FLAGS_quic_supports_trailers = true; -// Fixes a bug in QUIC_VERSION_26 by always using the primary config when -// getting the proof of possession. -bool FLAGS_quic_use_primary_config_for_proof = true; +// If true, headers stream will support receiving PUSH_PROMISE frames. +bool FLAGS_quic_supports_push_promise = false; // Enable counters for incoming/outgoing streams which are used as condition // check while creating a new stream. @@ -125,17 +111,45 @@ bool FLAGS_quic_distinguish_incoming_outgoing_streams = true; // is present in the client hello. bool FLAGS_quic_validate_stk_without_scid = true; -// If true, use the new write blocked list for QUIC. -bool FLAGS_quic_new_blocked_list = true; - -// If true, use inplace encryption for QUIC. -bool FLAGS_quic_inplace_encryption = true; - // If true, QUIC will support RFC 7539 variants of ChaCha20 Poly1305. bool FLAGS_quic_use_rfc7539 = true; -// If true, drop not awaited QUIC packets before decrypting them. -bool FLAGS_quic_drop_non_awaited_packets = false; +// If true, require QUIC connections to use a valid server nonce or a non-local +// strike register. +bool FLAGS_require_strike_register_or_server_nonce = true; + +// When turn on, log packet loss into transport connection stats LossEvent. +bool FLAGS_quic_log_loss_event = true; + +// If true, for QUIC authenticated encryption algorithms, last 8 bytes +// of IV comprise packet path id and lower 7 bytes of packet number. +bool FLAGS_quic_include_path_id_in_iv = true; + +// If true, make sure new incoming streams correctly cede to higher +// priority (or batch) streams when doing QUIC writes. +bool FLAGS_quic_cede_correctly = true; + +// If on, max number of incoming and outgoing streams will be different. +// Incoming will be a little higher than outgoing to tolerate race condition. +bool FLAGS_quic_different_max_num_open_streams = true; + +// If true, QUIC should correctly report if it supports ChaCha20. Otherwise, +// QUIC will lie and claim that it does not support ChaCha20. The primary use +// case for this is places where ChaCha20 is prohibitively expensive compared to +// AES-GCM. +bool FLAGS_quic_crypto_server_config_default_has_chacha20 = true; + +// If true, checking for peer address change is postponed after the packet gets +// decrypted. +bool FLAGS_check_peer_address_change_after_decryption = true; + +// If true, always log the cached network parameters, regardless of whether +// bandwidth-resumption has been enabled. +bool FLAGS_quic_log_received_parameters = true; + +// If true, QUIC will use newly refactored TCP sender code. +bool FLAGS_quic_use_new_tcp_sender = true; -// If true, use the fast implementation of IncrementalHash/FNV1a_128_Hash. -bool FLAGS_quic_utils_use_fast_incremental_hash = true; +// Saves the initial subkey secret in QUIC crypto when deriving keys from the +// initial premaster secret. +bool FLAGS_quic_save_initial_subkey_secret = true; diff --git a/src/net/quic/quic_flags.h b/src/net/quic/quic_flags.h index c890f6fd..a991a59f 100644 --- a/src/net/quic/quic_flags.h +++ b/src/net/quic/quic_flags.h @@ -17,31 +17,36 @@ NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_bbr; NET_EXPORT_PRIVATE extern int64_t FLAGS_quic_time_wait_list_seconds; NET_EXPORT_PRIVATE extern int64_t FLAGS_quic_time_wait_list_max_connections; NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_stateless_reject_support; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_ack_decimation; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_always_log_bugs_for_tests; NET_EXPORT_PRIVATE extern bool FLAGS_quic_auto_tune_receive_window; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_limit_max_cwnd; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_enable_multipath; NET_EXPORT_PRIVATE extern bool FLAGS_quic_require_handshake_confirmation; NET_EXPORT_PRIVATE extern bool FLAGS_shift_quic_cubic_epoch_when_app_limited; NET_EXPORT_PRIVATE extern bool FLAGS_quic_measure_headers_hol_blocking_time; NET_EXPORT_PRIVATE extern bool FLAGS_quic_disable_pacing; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_general_loss_algorithm; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_disable_non_nat_address_migration; NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_new_idle_timeout; NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_stream_sequencer_buffer; NET_EXPORT_PRIVATE extern bool FLAGS_quic_respect_send_alarm2; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_set_client_hello_cb_nullptr; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_track_single_retransmission; NET_EXPORT_PRIVATE extern bool FLAGS_quic_batch_writes; NET_EXPORT_PRIVATE extern bool FLAGS_quic_block_unencrypted_writes; NET_EXPORT_PRIVATE extern bool FLAGS_quic_never_write_unencrypted_data; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_no_unencrypted_fec; NET_EXPORT_PRIVATE extern bool FLAGS_quic_require_fix; NET_EXPORT_PRIVATE extern bool FLAGS_quic_supports_trailers; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_primary_config_for_proof; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_supports_push_promise; NET_EXPORT_PRIVATE extern bool FLAGS_quic_distinguish_incoming_outgoing_streams; NET_EXPORT_PRIVATE extern bool FLAGS_quic_validate_stk_without_scid; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_new_blocked_list; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_inplace_encryption; NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_rfc7539; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_drop_non_awaited_packets; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_utils_use_fast_incremental_hash; +NET_EXPORT_PRIVATE extern bool FLAGS_require_strike_register_or_server_nonce; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_log_loss_event; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_include_path_id_in_iv; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_cede_correctly; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_different_max_num_open_streams; +NET_EXPORT_PRIVATE extern bool + FLAGS_quic_crypto_server_config_default_has_chacha20; +NET_EXPORT_PRIVATE extern bool FLAGS_check_peer_address_change_after_decryption; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_log_received_parameters; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_new_tcp_sender; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_save_initial_subkey_secret; + #endif // NET_QUIC_QUIC_FLAGS_H_ diff --git a/src/net/quic/quic_flow_controller.cc b/src/net/quic/quic_flow_controller.cc index bf0c5ff2..259032fa 100644 --- a/src/net/quic/quic_flow_controller.cc +++ b/src/net/quic/quic_flow_controller.cc @@ -5,6 +5,7 @@ #include "net/quic/quic_flow_controller.h" #include "base/strings/stringprintf.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" @@ -67,9 +68,9 @@ bool QuicFlowController::UpdateHighestReceivedOffset( void QuicFlowController::AddBytesSent(QuicByteCount bytes_sent) { 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_; + QUIC_BUG << 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. @@ -245,8 +246,8 @@ void QuicFlowController::UpdateReceiveWindowSize(QuicStreamOffset size) { DVLOG(1) << ENDPOINT << "UpdateReceiveWindowSize for stream " << id_ << ": " << size; if (receive_window_size_ != receive_window_offset_) { - LOG(DFATAL) << "receive_window_size_:" << receive_window_size_ - << " != receive_window_offset:" << receive_window_offset_; + QUIC_BUG << "receive_window_size_:" << receive_window_size_ + << " != receive_window_offset:" << receive_window_offset_; return; } receive_window_size_ = size; diff --git a/src/net/quic/quic_frame_list.cc b/src/net/quic/quic_frame_list.cc index 862e34f7..50dcdc03 100644 --- a/src/net/quic/quic_frame_list.cc +++ b/src/net/quic/quic_frame_list.cc @@ -246,4 +246,4 @@ size_t QuicFrameList::BytesBuffered() const { return num_bytes_buffered_; } -} // namespace net_quic +} // namespace net diff --git a/src/net/quic/quic_framer.cc b/src/net/quic/quic_framer.cc index d4660bfa..7d75b8fd 100644 --- a/src/net/quic/quic_framer.cc +++ b/src/net/quic/quic_framer.cc @@ -14,6 +14,7 @@ #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_bug_tracker.h" #include "net/quic/quic_data_reader.h" #include "net/quic/quic_data_writer.h" #include "net/quic/quic_flags.h" @@ -123,7 +124,7 @@ QuicPacketNumberLength ReadSequenceNumberLength(uint8_t flags) { case PACKET_FLAGS_1BYTE_PACKET: return PACKET_1BYTE_PACKET_NUMBER; default: - LOG(DFATAL) << "Unreachable case statement."; + QUIC_BUG << "Unreachable case statement."; return PACKET_6BYTE_PACKET_NUMBER; } } @@ -213,6 +214,11 @@ size_t QuicFramer::GetBlockedFrameSize() { return kQuicFrameTypeSize + kQuicMaxStreamIdSize; } +// static +size_t QuicFramer::GetPathCloseFrameSize() { + return kQuicFrameTypeSize + kQuicPathIdSize; +} + // static size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) { // Sizes are 1 through 4 bytes. @@ -222,7 +228,7 @@ size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) { return i; } } - LOG(DFATAL) << "Failed to determine StreamIDSize."; + QUIC_BUG << "Failed to determine StreamIDSize."; return 4; } @@ -240,7 +246,7 @@ size_t QuicFramer::GetStreamOffsetSize(QuicStreamOffset offset) { return i; } } - LOG(DFATAL) << "Failed to determine StreamOffsetSize."; + QUIC_BUG << "Failed to determine StreamOffsetSize."; return 8; } @@ -269,12 +275,11 @@ size_t QuicFramer::GetSerializedFrameLength( // Prevent a rare crash reported in b/19458523. if ((frame.type == STREAM_FRAME || frame.type == ACK_FRAME) && frame.stream_frame == nullptr) { - LOG(DFATAL) << "Cannot compute the length of a null frame. " - << "type:" << frame.type << "free_bytes:" << free_bytes - << " first_frame:" << first_frame - << " last_frame:" << last_frame - << " is_in_fec:" << is_in_fec_group - << " seq num length:" << packet_number_length; + QUIC_BUG << "Cannot compute the length of a null frame. " + << "type:" << frame.type << "free_bytes:" << free_bytes + << " first_frame:" << first_frame << " last_frame:" << last_frame + << " is_in_fec:" << is_in_fec_group + << " seq num length:" << packet_number_length; set_error(QUIC_INTERNAL_ERROR); visitor_->OnError(this); return 0; @@ -308,6 +313,8 @@ size_t QuicFramer::GetSerializedFrameLength( QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) {} +QuicFramer::AckFrameInfo::AckFrameInfo(const AckFrameInfo& other) = default; + QuicFramer::AckFrameInfo::~AckFrameInfo() {} // static @@ -322,7 +329,7 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, size_t packet_length) { QuicDataWriter writer(packet_length, buffer); if (!AppendPacketHeader(header, &writer)) { - LOG(DFATAL) << "AppendPacketHeader failed"; + QUIC_BUG << "AppendPacketHeader failed"; return 0; } @@ -333,7 +340,7 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, (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"; + QUIC_BUG << "AppendTypeByte failed"; return 0; } @@ -344,20 +351,20 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, case STREAM_FRAME: if (!AppendStreamFrame(*frame.stream_frame, no_stream_frame_length, &writer)) { - LOG(DFATAL) << "AppendStreamFrame failed"; + QUIC_BUG << "AppendStreamFrame failed"; return 0; } break; case ACK_FRAME: if (!AppendAckFrameAndTypeByte(header, *frame.ack_frame, &writer)) { - LOG(DFATAL) << "AppendAckFrameAndTypeByte failed"; + QUIC_BUG << "AppendAckFrameAndTypeByte failed"; return 0; } break; case STOP_WAITING_FRAME: if (!AppendStopWaitingFrame(header, *frame.stop_waiting_frame, &writer)) { - LOG(DFATAL) << "AppendStopWaitingFrame failed"; + QUIC_BUG << "AppendStopWaitingFrame failed"; return 0; } break; @@ -368,38 +375,44 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, break; case RST_STREAM_FRAME: if (!AppendRstStreamFrame(*frame.rst_stream_frame, &writer)) { - LOG(DFATAL) << "AppendRstStreamFrame failed"; + QUIC_BUG << "AppendRstStreamFrame failed"; return 0; } break; case CONNECTION_CLOSE_FRAME: if (!AppendConnectionCloseFrame(*frame.connection_close_frame, &writer)) { - LOG(DFATAL) << "AppendConnectionCloseFrame failed"; + QUIC_BUG << "AppendConnectionCloseFrame failed"; return 0; } break; case GOAWAY_FRAME: if (!AppendGoAwayFrame(*frame.goaway_frame, &writer)) { - LOG(DFATAL) << "AppendGoAwayFrame failed"; + QUIC_BUG << "AppendGoAwayFrame failed"; return 0; } break; case WINDOW_UPDATE_FRAME: if (!AppendWindowUpdateFrame(*frame.window_update_frame, &writer)) { - LOG(DFATAL) << "AppendWindowUpdateFrame failed"; + QUIC_BUG << "AppendWindowUpdateFrame failed"; return 0; } break; case BLOCKED_FRAME: if (!AppendBlockedFrame(*frame.blocked_frame, &writer)) { - LOG(DFATAL) << "AppendBlockedFrame failed"; + QUIC_BUG << "AppendBlockedFrame failed"; + return 0; + } + break; + case PATH_CLOSE_FRAME: + if (!AppendPathCloseFrame(*frame.path_close_frame, &writer)) { + QUIC_BUG << "AppendPathCloseFrame failed"; return 0; } break; default: RaiseError(QUIC_INVALID_FRAME_DATA); - LOG(DFATAL) << "QUIC_INVALID_FRAME_DATA"; + QUIC_BUG << "QUIC_INVALID_FRAME_DATA"; return 0; } ++i; @@ -418,19 +431,19 @@ QuicPacket* QuicFramer::BuildFecPacket(const QuicPacketHeader& header, scoped_ptr buffer(new char[len]); QuicDataWriter writer(len, buffer.get()); if (!AppendPacketHeader(header, &writer)) { - LOG(DFATAL) << "AppendPacketHeader failed"; + QUIC_BUG << "AppendPacketHeader failed"; return nullptr; } if (!writer.WriteBytes(redundancy.data(), redundancy.length())) { - LOG(DFATAL) << "Failed to add FEC"; + QUIC_BUG << "Failed to add FEC"; return nullptr; } - return new QuicPacket(buffer.release(), len, true, - header.public_header.connection_id_length, - header.public_header.version_flag, - header.public_header.packet_number_length); + return new QuicPacket( + buffer.release(), len, true, header.public_header.connection_id_length, + header.public_header.version_flag, header.public_header.multipath_flag, + header.public_header.packet_number_length); } // static @@ -546,9 +559,8 @@ bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) { scoped_ptr large_buffer(new char[packet.length()]); rv = ProcessDataPacket(&reader, public_header, packet, large_buffer.get(), packet.length()); - LOG_IF(DFATAL, rv) << "QUIC should never successfully process packets " - << "larger than kMaxPacketSize. packet size:" - << packet.length(); + QUIC_BUG_IF(rv) << "QUIC should never successfully process packets larger" + << "than kMaxPacketSize. packet size:" << packet.length(); } return rv; @@ -603,7 +615,7 @@ bool QuicFramer::ProcessDataPacket(QuicDataReader* encrypted_reader, if (packet.length() > kMaxPacketSize) { // If the packet has gotten this far, it should not be too large. - LOG(DFATAL) << "Packet too large:" << packet.length(); + QUIC_BUG << "Packet too large:" << packet.length(); return RaiseError(QUIC_PACKET_TOO_LARGE); } @@ -1000,7 +1012,7 @@ uint8_t QuicFramer::GetSequenceNumberFlags( case PACKET_6BYTE_PACKET_NUMBER: return PACKET_FLAGS_6BYTE_PACKET; default: - LOG(DFATAL) << "Unreachable case statement."; + QUIC_BUG << "Unreachable case statement."; return PACKET_FLAGS_6BYTE_PACKET; } } @@ -1042,6 +1054,7 @@ QuicFramer::AckFrameInfo QuicFramer::GetAckFrameInfo( bool QuicFramer::ProcessUnauthenticatedHeader(QuicDataReader* encrypted_reader, QuicPacketHeader* header) { + header->path_id = kDefaultPathId; if (header->public_header.multipath_flag && !ProcessPathId(encrypted_reader, &header->path_id)) { set_detailed_error("Unable to read path id."); @@ -1289,6 +1302,18 @@ bool QuicFramer::ProcessFrameData(QuicDataReader* reader, } continue; } + case PATH_CLOSE_FRAME: { + QuicPathCloseFrame path_close_frame; + if (!ProcessPathCloseFrame(reader, &path_close_frame)) { + return RaiseError(QUIC_INVALID_PATH_CLOSE_DATA); + } + if (!visitor_->OnPathCloseFrame(path_close_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."); @@ -1381,17 +1406,17 @@ bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, return false; } - uint64_t delta_time_largest_observed_us; - if (!reader->ReadUFloat16(&delta_time_largest_observed_us)) { - set_detailed_error("Unable to read delta time largest observed."); + uint64_t ack_delay_time_us; + if (!reader->ReadUFloat16(&ack_delay_time_us)) { + set_detailed_error("Unable to read ack delay time."); return false; } - if (delta_time_largest_observed_us == kUFloat16MaxValue) { - ack_frame->delta_time_largest_observed = QuicTime::Delta::Infinite(); + if (ack_delay_time_us == kUFloat16MaxValue) { + ack_frame->ack_delay_time = QuicTime::Delta::Infinite(); } else { - ack_frame->delta_time_largest_observed = - QuicTime::Delta::FromMicroseconds(delta_time_largest_observed_us); + ack_frame->ack_delay_time = + QuicTime::Delta::FromMicroseconds(ack_delay_time_us); } if (!ProcessTimestampsInAckFrame(reader, ack_frame)) { @@ -1638,17 +1663,28 @@ bool QuicFramer::ProcessBlockedFrame(QuicDataReader* reader, return true; } +bool QuicFramer::ProcessPathCloseFrame(QuicDataReader* reader, + QuicPathCloseFrame* frame) { + if (!reader->ReadBytes(&frame->path_id, 1)) { + set_detailed_error("Unable to read path_id."); + return false; + } + + return true; +} + // static StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket( const QuicEncryptedPacket& encrypted, QuicConnectionIdLength connection_id_length, bool includes_version, + bool includes_path_id, QuicPacketNumberLength packet_number_length) { // TODO(ianswett): This is identical to QuicData::AssociatedData. return StringPiece( encrypted.data(), GetStartOfEncryptedData(connection_id_length, includes_version, - packet_number_length)); + includes_path_id, packet_number_length)); } void QuicFramer::SetDecrypter(EncryptionLevel level, QuicDecrypter* decrypter) { @@ -1681,6 +1717,7 @@ void QuicFramer::SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter) { } size_t QuicFramer::EncryptPayload(EncryptionLevel level, + QuicPathId path_id, QuicPacketNumber packet_number, const QuicPacket& packet, char* buffer, @@ -1691,10 +1728,10 @@ size_t QuicFramer::EncryptPayload(EncryptionLevel level, // Copy in the header, because the encrypter only populates the encrypted // plaintext content. const size_t ad_len = associated_data.length(); - memcpy(buffer, associated_data.data(), ad_len); + memmove(buffer, associated_data.data(), ad_len); // Encrypt the plaintext into the buffer. size_t output_length = 0; - if (!encrypter_[level]->EncryptPacket(packet_number, associated_data, + if (!encrypter_[level]->EncryptPacket(path_id, packet_number, associated_data, packet.Plaintext(), buffer + ad_len, &output_length, buffer_len - ad_len)) { RaiseError(QUIC_ENCRYPTION_FAILURE); @@ -1731,17 +1768,17 @@ bool QuicFramer::DecryptPayload(QuicDataReader* encrypted_reader, DCHECK(decrypter_.get() != nullptr); const StringPiece& associated_data = GetAssociatedDataFromEncryptedPacket( packet, header.public_header.connection_id_length, - header.public_header.version_flag, + header.public_header.version_flag, header.public_header.multipath_flag, header.public_header.packet_number_length); bool success = decrypter_->DecryptPacket( - header.packet_number, associated_data, encrypted, decrypted_buffer, - decrypted_length, buffer_length); + header.path_id, header.packet_number, associated_data, encrypted, + decrypted_buffer, decrypted_length, buffer_length); if (success) { visitor_->OnDecryptedPacket(decrypter_level_); } else if (alternative_decrypter_.get() != nullptr) { success = alternative_decrypter_->DecryptPacket( - header.packet_number, associated_data, encrypted, decrypted_buffer, - decrypted_length, buffer_length); + header.path_id, header.packet_number, associated_data, encrypted, + decrypted_buffer, decrypted_length, buffer_length); if (success) { visitor_->OnDecryptedPacket(alternative_decrypter_level_); if (alternative_decrypter_latch_) { @@ -1839,6 +1876,8 @@ size_t QuicFramer::ComputeFrameLength( return GetWindowUpdateFrameSize(); case BLOCKED_FRAME: return GetBlockedFrameSize(); + case PATH_CLOSE_FRAME: + return GetPathCloseFrameSize(); case PADDING_FRAME: DCHECK(false); return 0; @@ -1859,7 +1898,7 @@ bool QuicFramer::AppendTypeByte(const QuicFrame& frame, switch (frame.type) { case STREAM_FRAME: { if (frame.stream_frame == nullptr) { - LOG(DFATAL) << "Failed to append STREAM frame with no stream_frame."; + QUIC_BUG << "Failed to append STREAM frame with no stream_frame."; } // Fin bit. type_byte |= frame.stream_frame->fin ? kQuicStreamFinMask : 0; @@ -1927,23 +1966,23 @@ bool QuicFramer::AppendStreamFrame(const QuicStreamFrame& frame, bool no_stream_frame_length, QuicDataWriter* writer) { if (!writer->WriteBytes(&frame.stream_id, GetStreamIdSize(frame.stream_id))) { - LOG(DFATAL) << "Writing stream id size failed."; + QUIC_BUG << "Writing stream id size failed."; return false; } if (!writer->WriteBytes(&frame.offset, GetStreamOffsetSize(frame.offset))) { - LOG(DFATAL) << "Writing offset size failed."; + QUIC_BUG << "Writing offset size failed."; return false; } if (!no_stream_frame_length) { if ((frame.frame_length > numeric_limits::max()) || !writer->WriteUInt16(static_cast(frame.frame_length))) { - LOG(DFATAL) << "Writing stream frame length failed"; + QUIC_BUG << "Writing stream frame length failed"; return false; } } if (!writer->WriteBytes(frame.frame_buffer, frame.frame_length)) { - LOG(DFATAL) << "Writing frame data failed."; + QUIC_BUG << "Writing frame data failed."; return false; } return true; @@ -2025,14 +2064,13 @@ bool QuicFramer::AppendAckFrameAndTypeByte(const QuicPacketHeader& header, return false; } - uint64_t delta_time_largest_observed_us = kUFloat16MaxValue; - if (!frame.delta_time_largest_observed.IsInfinite()) { - DCHECK_LE(0u, frame.delta_time_largest_observed.ToMicroseconds()); - delta_time_largest_observed_us = - frame.delta_time_largest_observed.ToMicroseconds(); + uint64_t ack_delay_time_us = kUFloat16MaxValue; + if (!frame.ack_delay_time.IsInfinite()) { + DCHECK_LE(0u, frame.ack_delay_time.ToMicroseconds()); + ack_delay_time_us = frame.ack_delay_time.ToMicroseconds(); } - if (!writer->WriteUFloat16(delta_time_largest_observed_us)) { + if (!writer->WriteUFloat16(ack_delay_time_us)) { return false; } @@ -2085,8 +2123,7 @@ bool QuicFramer::AppendAckFrameAndTypeByte(const QuicPacketHeader& header, } if (num_revived_packets > 0) { - LOG_IF(DFATAL, - !frame.missing_packets.Contains(frame.latest_revived_packet)); + QUIC_BUG_IF(!frame.missing_packets.Contains(frame.latest_revived_packet)); if (!AppendPacketSequenceNumber(largest_observed_length, frame.latest_revived_packet, writer)) { return false; @@ -2170,20 +2207,20 @@ bool QuicFramer::AppendStopWaitingFrame(const QuicPacketHeader& header, const QuicPacketNumber length_shift = header.public_header.packet_number_length * 8; if (!writer->WriteUInt8(frame.entropy_hash)) { - LOG(DFATAL) << " hash failed"; + QUIC_BUG << " hash failed"; return false; } if (least_unacked_delta >> length_shift > 0) { - LOG(DFATAL) << "packet_number_length " - << header.public_header.packet_number_length - << " is too small for least_unacked_delta: " - << least_unacked_delta; + QUIC_BUG << "packet_number_length " + << header.public_header.packet_number_length + << " is too small for least_unacked_delta: " + << least_unacked_delta; return false; } if (!AppendPacketSequenceNumber(header.public_header.packet_number_length, least_unacked_delta, writer)) { - LOG(DFATAL) << " seq failed: " << header.public_header.packet_number_length; + QUIC_BUG << " seq failed: " << header.public_header.packet_number_length; return false; } @@ -2258,6 +2295,15 @@ bool QuicFramer::AppendBlockedFrame(const QuicBlockedFrame& frame, return true; } +bool QuicFramer::AppendPathCloseFrame(const QuicPathCloseFrame& frame, + QuicDataWriter* writer) { + uint8_t path_id = static_cast(frame.path_id); + if (!writer->WriteUInt8(path_id)) { + return false; + } + return true; +} + bool QuicFramer::RaiseError(QuicErrorCode error) { DVLOG(1) << "Error: " << QuicUtils::ErrorToString(error) << " detail: " << detailed_error_; diff --git a/src/net/quic/quic_framer.h b/src/net/quic/quic_framer.h index a7fd9fa3..456f2b09 100644 --- a/src/net/quic/quic_framer.h +++ b/src/net/quic/quic_framer.h @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include "base/logging.h" @@ -144,6 +146,9 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface { // Called when a BlockedFrame has been parsed. virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) = 0; + // Called when a PathCloseFrame has been parsed. + virtual bool OnPathCloseFrame(const QuicPathCloseFrame& frame) = 0; + // Called when FEC data has been parsed. virtual void OnFecData(base::StringPiece redundancy) = 0; @@ -255,6 +260,8 @@ class NET_EXPORT_PRIVATE QuicFramer { static size_t GetWindowUpdateFrameSize(); // Size in bytes of all Blocked frame fields. static size_t GetBlockedFrameSize(); + // Size in bytes of all PathClose frame fields. + static size_t GetPathCloseFrameSize(); // 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. @@ -278,6 +285,7 @@ class NET_EXPORT_PRIVATE QuicFramer { const QuicEncryptedPacket& encrypted, QuicConnectionIdLength connection_id_length, bool includes_version, + bool includes_path_id, QuicPacketNumberLength packet_number_length); // Serializes a packet containing |frames| into |buffer|. @@ -330,6 +338,7 @@ class NET_EXPORT_PRIVATE QuicFramer { // Returns the length of the data encrypted into |buffer| if |buffer_len| is // long enough, and otherwise 0. size_t EncryptPayload(EncryptionLevel level, + QuicPathId path_id, QuicPacketNumber packet_number, const QuicPacket& packet, char* buffer, @@ -367,6 +376,7 @@ class NET_EXPORT_PRIVATE QuicFramer { struct AckFrameInfo { AckFrameInfo(); + AckFrameInfo(const AckFrameInfo& other); ~AckFrameInfo(); // The maximum delta between ranges. @@ -423,6 +433,7 @@ class NET_EXPORT_PRIVATE QuicFramer { bool ProcessWindowUpdateFrame(QuicDataReader* reader, QuicWindowUpdateFrame* frame); bool ProcessBlockedFrame(QuicDataReader* reader, QuicBlockedFrame* frame); + bool ProcessPathCloseFrame(QuicDataReader* reader, QuicPathCloseFrame* frame); bool DecryptPayload(QuicDataReader* encrypted_reader, const QuicPacketHeader& header, @@ -498,6 +509,8 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicDataWriter* writer); bool AppendBlockedFrame(const QuicBlockedFrame& frame, QuicDataWriter* writer); + bool AppendPathCloseFrame(const QuicPathCloseFrame& frame, + QuicDataWriter* writer); bool RaiseError(QuicErrorCode error); @@ -513,10 +526,10 @@ class NET_EXPORT_PRIVATE QuicFramer { // has been sent/received. // TODO(fayang): this set is never cleaned up. A possible improvement is to // use intervals. - base::hash_set closed_paths_; + std::unordered_set closed_paths_; // Map mapping path id to packet number of last successfully decrypted/revived // received packet. - base::hash_map last_packet_numbers_; + std::unordered_map last_packet_numbers_; // Updated by ProcessPacketHeader when it succeeds. QuicPacketNumber last_packet_number_; // The path on which last successfully decrypted/revived packet was received. diff --git a/src/net/quic/quic_headers_stream.cc b/src/net/quic/quic_headers_stream.cc index 0676b54e..922c7035 100644 --- a/src/net/quic/quic_headers_stream.cc +++ b/src/net/quic/quic_headers_stream.cc @@ -7,6 +7,7 @@ #include "base/macros.h" #include "base/metrics/histogram_macros.h" #include "base/strings/stringprintf.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_headers_stream.h" #include "net/quic/quic_spdy_session.h" @@ -19,12 +20,6 @@ using std::string; namespace net { -namespace { - -const QuicStreamId kInvalidStreamId = 0; - -} // namespace - // A SpdyFramer visitor which passed SYN_STREAM and SYN_REPLY frames to // the QuicSpdyStream, and closes the connection if any unexpected frames // are received. @@ -143,7 +138,14 @@ class QuicHeadersStream::SpdyFramerVisitor void OnPushPromise(SpdyStreamId stream_id, SpdyStreamId promised_stream_id, bool end) override { - CloseConnection("SPDY PUSH_PROMISE frame received."); + if (!stream_->supports_push_promise()) { + CloseConnection("PUSH_PROMISE not supported."); + return; + } + if (!stream_->IsConnected()) { + return; + } + stream_->OnPushPromise(stream_id, promised_stream_id, end); } void OnContinuation(SpdyStreamId stream_id, bool end) override {} @@ -185,10 +187,13 @@ QuicHeadersStream::QuicHeadersStream(QuicSpdySession* session) : ReliableQuicStream(kHeadersStreamId, session), spdy_session_(session), stream_id_(kInvalidStreamId), + promised_stream_id_(kInvalidStreamId), fin_(false), frame_len_(0), measure_headers_hol_blocking_time_( FLAGS_quic_measure_headers_hol_blocking_time), + supports_push_promise_(session->perspective() == Perspective::IS_CLIENT && + FLAGS_quic_supports_push_promise), cur_max_timestamp_(QuicTime::Zero()), prev_max_timestamp_(QuicTime::Zero()), spdy_framer_(HTTP2), @@ -226,7 +231,7 @@ size_t QuicHeadersStream::WritePushPromise( const SpdyHeaderBlock& headers, QuicAckListenerInterface* ack_listener) { if (session()->perspective() == Perspective::IS_CLIENT) { - LOG(DFATAL) << "Client shouldn't send PUSH_PROMISE"; + QUIC_BUG << "Client shouldn't send PUSH_PROMISE"; return 0; } @@ -272,10 +277,6 @@ void QuicHeadersStream::OnDataAvailable() { } } -SpdyPriority QuicHeadersStream::Priority() const { - return net::kV3HighestPriority; -} - void QuicHeadersStream::OnHeaders(SpdyStreamId stream_id, bool has_priority, SpdyPriority priority, @@ -295,10 +296,20 @@ void QuicHeadersStream::OnHeaders(SpdyStreamId stream_id, } } DCHECK_EQ(kInvalidStreamId, stream_id_); + DCHECK_EQ(kInvalidStreamId, promised_stream_id_); stream_id_ = stream_id; fin_ = fin; } +void QuicHeadersStream::OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id, + bool end) { + DCHECK_EQ(kInvalidStreamId, stream_id_); + DCHECK_EQ(kInvalidStreamId, promised_stream_id_); + stream_id_ = stream_id; + promised_stream_id_ = promised_stream_id; +} + void QuicHeadersStream::OnControlFrameHeaderData(SpdyStreamId stream_id, const char* header_data, size_t len) { @@ -321,13 +332,24 @@ void QuicHeadersStream::OnControlFrameHeaderData(SpdyStreamId stream_id, prev_max_timestamp_ = std::max(prev_max_timestamp_, cur_max_timestamp_); cur_max_timestamp_ = QuicTime::Zero(); } - spdy_session_->OnStreamHeadersComplete(stream_id_, fin_, frame_len_); + if (promised_stream_id_ == kInvalidStreamId) { + spdy_session_->OnStreamHeadersComplete(stream_id_, fin_, frame_len_); + } else { + spdy_session_->OnPromiseHeadersComplete(stream_id_, promised_stream_id_, + frame_len_); + } // Reset state for the next frame. + promised_stream_id_ = kInvalidStreamId; stream_id_ = kInvalidStreamId; fin_ = false; frame_len_ = 0; } else { - spdy_session_->OnStreamHeaders(stream_id_, StringPiece(header_data, len)); + if (promised_stream_id_ == kInvalidStreamId) { + spdy_session_->OnStreamHeaders(stream_id_, StringPiece(header_data, len)); + } else { + spdy_session_->OnPromiseHeaders(stream_id_, + StringPiece(header_data, len)); + } } } diff --git a/src/net/quic/quic_headers_stream.h b/src/net/quic/quic_headers_stream.h index 385650e9..282a9ea5 100644 --- a/src/net/quic/quic_headers_stream.h +++ b/src/net/quic/quic_headers_stream.h @@ -18,9 +18,10 @@ namespace net { class QuicSpdySession; -// Headers in QUIC are sent as HTTP/2 HEADERS frames over a reserved reliable -// stream with the id 3. Each endpoint (client and server) will allocate an -// instance of QuicHeadersStream to send and receive headers. +// Headers in QUIC are sent as HTTP/2 HEADERS or PUSH_PROMISE frames +// over a reserved reliable stream with the id 3. 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(QuicSpdySession* session); @@ -29,23 +30,24 @@ class NET_EXPORT_PRIVATE QuicHeadersStream : public ReliableQuicStream { // Writes |headers| for |stream_id| in an HTTP/2 HEADERS frame to the peer. // If |fin| is true, the fin flag will be set on the HEADERS frame. Returns // the size, in bytes, of the resulting HEADERS frame. - size_t WriteHeaders(QuicStreamId stream_id, - const SpdyHeaderBlock& headers, - bool fin, - SpdyPriority priority, - QuicAckListenerInterface* ack_listener); + virtual size_t WriteHeaders(QuicStreamId stream_id, + const SpdyHeaderBlock& headers, + bool fin, + SpdyPriority priority, + QuicAckListenerInterface* ack_listener); // Write |headers| for |promised_stream_id| on |original_stream_id| in a // PUSH_PROMISE frame to peer. // Return the size, in bytes, of the resulting PUSH_PROMISE frame. - size_t WritePushPromise(QuicStreamId original_stream_id, - QuicStreamId promised_stream_id, - const SpdyHeaderBlock& headers, - QuicAckListenerInterface* ack_listener); + virtual size_t WritePushPromise(QuicStreamId original_stream_id, + QuicStreamId promised_stream_id, + const SpdyHeaderBlock& headers, + QuicAckListenerInterface* ack_listener); // ReliableQuicStream implementation void OnDataAvailable() override; - SpdyPriority Priority() const override; + + bool supports_push_promise() { return supports_push_promise_; } private: class SpdyFramerVisitor; @@ -58,6 +60,11 @@ class NET_EXPORT_PRIVATE QuicHeadersStream : public ReliableQuicStream { SpdyPriority priority, bool fin); + // Called when a PUSH_PROMISE frame has been received. + void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id, + bool end); + // Called when a chunk of header data is available. This is called // after OnHeaders. // |stream_id| The stream receiving the header data. @@ -78,11 +85,13 @@ class NET_EXPORT_PRIVATE QuicHeadersStream : public ReliableQuicStream { // Data about the stream whose headers are being processed. QuicStreamId stream_id_; + QuicStreamId promised_stream_id_; bool fin_; size_t frame_len_; - // Helper variable that caches the corresponding feature flag. + // Helper variables that cache the corresponding feature flag. bool measure_headers_hol_blocking_time_; + bool supports_push_promise_; // Timestamps used to measure HOL blocking, these are recorded by // the sequencer approximate to the time of arrival off the wire. diff --git a/src/net/quic/quic_one_block_arena.h b/src/net/quic/quic_one_block_arena.h new file mode 100644 index 00000000..37fc7307 --- /dev/null +++ b/src/net/quic/quic_one_block_arena.h @@ -0,0 +1,77 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// An arena that consists of a single inlined block of |ArenaSize|. Useful to +// avoid repeated calls to malloc/new and to improve memory locality. DCHECK's +// if an allocation out of the arena ever fails in debug builds; falls back to +// heap allocation in release builds. + +#ifndef NET_QUIC_QUIC_ONE_BLOCK_ARENA_H_ +#define NET_QUIC_QUIC_ONE_BLOCK_ARENA_H_ + +#include "net/quic/quic_arena_scoped_ptr.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_utils.h" + +#define PREDICT_FALSE(x) x + +namespace net { + +template +class QuicOneBlockArena { + static const uint32_t kMaxAlign = 8; + + public: + QuicOneBlockArena(); + + // Instantiates an object of type |T| with |args|. |args| are perfectly + // forwarded to |T|'s constructor. The returned pointer's lifetime is + // controlled by QuicArenaScopedPtr. + template + QuicArenaScopedPtr New(Args&&... args); + + private: + // Returns the size of |T| aligned up to |kMaxAlign|. + template + static inline uint32_t AlignedSize() { + return ((sizeof(T) + (kMaxAlign - 1)) / kMaxAlign) * kMaxAlign; + } + + // Actual storage. + // Subtle/annoying: the value '8' must be coded explicitly into the alignment + // declaration for MSVC. + QUIC_ALIGNED(8) char storage_[ArenaSize]; + // Current offset into the storage. + uint32_t offset_; + + DISALLOW_COPY_AND_ASSIGN(QuicOneBlockArena); +}; + +template +QuicOneBlockArena::QuicOneBlockArena() : offset_(0) {} + +template +template +QuicArenaScopedPtr QuicOneBlockArena::New(Args&&... args) { + DCHECK_LT(AlignedSize(), ArenaSize) + << "Object is too large for the arena."; + static_assert(QUIC_ALIGN_OF(T) > 1, + "Objects added to the arena must be at least 2B aligned."); + if (PREDICT_FALSE(offset_ > ArenaSize - AlignedSize())) { + LOG(DFATAL) << "Ran out of space in QuicOneBlockArena at " << this + << ", max size was " << ArenaSize << ", failing request was " + << AlignedSize() << ", end of arena was " << offset_; + return QuicArenaScopedPtr(new T(std::forward(args)...)); + } + + void* buf = &storage_[offset_]; + new (buf) T(std::forward(args)...); + offset_ += AlignedSize(); + return QuicArenaScopedPtr(buf, + QuicArenaScopedPtr::ConstructFrom::kArena); +} + +} // namespace net + +#endif // NET_QUIC_QUIC_ONE_BLOCK_ARENA_H_ diff --git a/src/net/quic/quic_packet_creator.cc b/src/net/quic/quic_packet_creator.cc index 233fa9db..f4e0cf74 100644 --- a/src/net/quic/quic_packet_creator.cc +++ b/src/net/quic/quic_packet_creator.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/macros.h" #include "net/quic/crypto/quic_random.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_data_writer.h" #include "net/quic/quic_fec_group.h" #include "net/quic/quic_flags.h" @@ -86,39 +87,43 @@ QuicPacketCreator::QuicPacketCreator(QuicConnectionId connection_id, DelegateInterface* delegate) : delegate_(delegate), debug_delegate_(nullptr), - connection_id_(connection_id), - encryption_level_(ENCRYPTION_NONE), - has_ack_(false), - has_stop_waiting_(false), framer_(framer), random_bool_source_(new QuicRandomBoolSource(random_generator)), - current_path_(kDefaultPathId), buffer_allocator_(buffer_allocator), - packet_number_(0), - should_fec_protect_next_packet_(false), - fec_protect_(false), send_version_in_packet_(framer->perspective() == Perspective::IS_CLIENT), + send_path_id_in_packet_(false), + next_packet_number_length_(PACKET_1BYTE_PACKET_NUMBER), max_packet_length_(0), - max_packets_per_fec_group_(kDefaultMaxPacketsPerFecGroup), connection_id_length_(PACKET_8BYTE_CONNECTION_ID), - next_packet_number_length_(PACKET_1BYTE_PACKET_NUMBER), - packet_number_length_(next_packet_number_length_), packet_size_(0), - needs_padding_(false), + connection_id_(connection_id), + packet_(kDefaultPathId, + 0, + next_packet_number_length_, + nullptr, + 0, + 0, + false, + false), + should_fec_protect_next_packet_(false), + fec_protect_(false), + max_packets_per_fec_group_(kDefaultMaxPacketsPerFecGroup), fec_send_policy_(FEC_ANY_TRIGGER), fec_timeout_(QuicTime::Delta::Zero()), rtt_multiplier_for_fec_timeout_(kRttMultiplierForFecTimeout) { SetMaxPacketLength(kDefaultMaxPacketSize); } -QuicPacketCreator::~QuicPacketCreator() {} +QuicPacketCreator::~QuicPacketCreator() { + QuicUtils::DeleteFrames(&packet_.retransmittable_frames); +} void QuicPacketCreator::OnBuiltFecProtectedPayload( const QuicPacketHeader& header, StringPiece payload) { if (fec_group_.get() != nullptr) { DCHECK_NE(0u, header.fec_group); - fec_group_->Update(encryption_level_, header, payload); + fec_group_->Update(packet_.encryption_level, header, payload); } } @@ -162,7 +167,7 @@ bool QuicPacketCreator::ShouldSendFec(bool force_close) const { void QuicPacketCreator::ResetFecGroup() { if (HasPendingFrames()) { - LOG_IF(DFATAL, packet_size_ != 0) + QUIC_BUG_IF(packet_size_ != 0) << "Cannot reset FEC group with pending frames."; return; } @@ -175,7 +180,7 @@ bool QuicPacketCreator::IsFecGroupOpen() const { void QuicPacketCreator::StartFecProtectingPackets() { if (max_packets_per_fec_group_ == 0) { - LOG(DFATAL) << "Cannot start FEC protection when FEC is not enabled."; + QUIC_BUG << "Cannot start FEC protection when FEC is not enabled."; return; } // TODO(jri): This currently requires that the generator flush out any @@ -184,7 +189,7 @@ void QuicPacketCreator::StartFecProtectingPackets() { // 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."; + QUIC_BUG << "Cannot start FEC protection with pending frames."; return; } DCHECK(!fec_protect_); @@ -193,7 +198,7 @@ void QuicPacketCreator::StartFecProtectingPackets() { void QuicPacketCreator::StopFecProtectingPackets() { if (fec_group_.get() != nullptr) { - LOG(DFATAL) << "Cannot stop FEC protection with open FEC group."; + QUIC_BUG << "Cannot stop FEC protection with open FEC group."; return; } DCHECK(fec_protect_); @@ -212,14 +217,14 @@ InFecGroup QuicPacketCreator::MaybeUpdateLengthsAndStartFec() { } // Update packet number length only on packet and FEC group boundaries. - packet_number_length_ = next_packet_number_length_; + packet_.packet_number_length = next_packet_number_length_; if (!fec_protect_) { return NOT_IN_FEC_GROUP; } // Start a new FEC group since protection is on. Set the fec group number to // the packet number of the next packet. - fec_group_.reset(new QuicFecGroup(packet_number_ + 1)); + fec_group_.reset(new QuicFecGroup(packet_.packet_number + 1)); return IN_FEC_GROUP; } @@ -238,11 +243,11 @@ void QuicPacketCreator::StopSendingVersion() { void QuicPacketCreator::UpdatePacketNumberLength( QuicPacketNumber least_packet_awaited_by_peer, QuicPacketCount max_packets_in_flight) { - DCHECK_LE(least_packet_awaited_by_peer, packet_number_ + 1); + DCHECK_LE(least_packet_awaited_by_peer, packet_.packet_number + 1); // Since the packet creator will not change packet number length mid FEC // group, include the size of an FEC group to be safe. const QuicPacketNumber current_delta = max_packets_per_fec_group_ + - packet_number_ + 1 - + packet_.packet_number + 1 - least_packet_awaited_by_peer; const uint64_t delta = max(current_delta, max_packets_in_flight); next_packet_number_length_ = @@ -271,7 +276,7 @@ bool QuicPacketCreator::ConsumeData(QuicStreamId id, return false; } if (needs_padding) { - needs_padding_ = true; + packet_.needs_padding = true; } if (fec_protection == MUST_FEC_PROTECT && iov_offset + frame->stream_frame->frame_length == iov.total_length) { @@ -283,7 +288,7 @@ bool QuicPacketCreator::ConsumeData(QuicStreamId id, } bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id, - QuicStreamOffset offset) const { + QuicStreamOffset offset) { // 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. @@ -296,11 +301,12 @@ bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id, size_t QuicPacketCreator::StreamFramePacketOverhead( QuicConnectionIdLength connection_id_length, bool include_version, + bool include_path_id, QuicPacketNumberLength packet_number_length, QuicStreamOffset offset, InFecGroup is_in_fec_group) { return GetPacketHeaderSize(connection_id_length, include_version, - /*include_path_id=*/false, packet_number_length, + include_path_id, packet_number_length, is_in_fec_group) + // Assumes this is a stream with a single lone packet. QuicFramer::GetMinStreamFrameSize(1u, offset, true, is_in_fec_group); @@ -313,9 +319,9 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, bool fin, QuicFrame* frame) { DCHECK_GT(max_packet_length_, - StreamFramePacketOverhead(connection_id_length_, kIncludeVersion, - PACKET_6BYTE_PACKET_NUMBER, offset, - IN_FEC_GROUP)); + StreamFramePacketOverhead( + connection_id_length_, kIncludeVersion, kIncludePathId, + PACKET_6BYTE_PACKET_NUMBER, offset, IN_FEC_GROUP)); InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec(); @@ -325,7 +331,7 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, << QuicFramer::GetMinStreamFrameSize(id, offset, true, is_in_fec_group); if (iov_offset == iov.total_length) { - LOG_IF(DFATAL, !fin) << "Creating a stream frame with no data or fin."; + QUIC_BUG_IF(!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, StringPiece())); return 0; @@ -395,59 +401,50 @@ void QuicPacketCreator::CopyToBuffer(QuicIOVector iov, src = static_cast(iov.iov[iovnum].iov_base); copy_len = min(length, iov.iov[iovnum].iov_len); } - LOG_IF(DFATAL, length > 0) << "Failed to copy entire length to buffer."; + QUIC_BUG_IF(length > 0) << "Failed to copy entire length to buffer."; } -SerializedPacket QuicPacketCreator::ReserializeAllFrames( - const RetransmittableFrames& frames, - EncryptionLevel original_encryption_level, - QuicPacketNumberLength original_length, +void QuicPacketCreator::ReserializeAllFrames( + const PendingRetransmission& retransmission, char* buffer, size_t buffer_len) { + DCHECK(queued_frames_.empty()); DCHECK(fec_group_.get() == nullptr); - const QuicPacketNumberLength saved_length = packet_number_length_; + DCHECK(!packet_.needs_padding); + QUIC_BUG_IF(retransmission.retransmittable_frames.empty()) + << "Attempt to serialize empty packet"; + const QuicPacketNumberLength saved_length = packet_.packet_number_length; const QuicPacketNumberLength saved_next_length = next_packet_number_length_; const bool saved_should_fec_protect = fec_protect_; - const bool needs_padding = needs_padding_; - const EncryptionLevel default_encryption_level = encryption_level_; + const EncryptionLevel default_encryption_level = packet_.encryption_level; // Temporarily set the packet number length, stop FEC protection, // and change the encryption level. - packet_number_length_ = original_length; - next_packet_number_length_ = original_length; + packet_.packet_number_length = retransmission.packet_number_length; + next_packet_number_length_ = retransmission.packet_number_length; fec_protect_ = false; - needs_padding_ = frames.needs_padding(); + packet_.needs_padding = retransmission.needs_padding; // Only preserve the original encryption level if it's a handshake packet or // if we haven't gone forward secure. - if (frames.HasCryptoHandshake() || - encryption_level_ != ENCRYPTION_FORWARD_SECURE) { - encryption_level_ = original_encryption_level; + if (retransmission.has_crypto_handshake || + packet_.encryption_level != ENCRYPTION_FORWARD_SECURE) { + packet_.encryption_level = retransmission.encryption_level; } // Serialize the packet and restore the FEC and packet number length state. - SerializedPacket serialized_packet = - SerializeAllFrames(frames.frames(), buffer, buffer_len); - packet_number_length_ = saved_length; - next_packet_number_length_ = saved_next_length; - fec_protect_ = saved_should_fec_protect; - needs_padding_ = needs_padding; - encryption_level_ = default_encryption_level; - - return serialized_packet; -} - -SerializedPacket QuicPacketCreator::SerializeAllFrames(const QuicFrames& frames, - char* buffer, - size_t buffer_len) { - LOG_IF(DFATAL, !queued_frames_.empty()) << "Frames already queued."; - LOG_IF(DFATAL, frames.empty()) << "Attempt to serialize empty packet"; - for (const QuicFrame& frame : frames) { + for (const QuicFrame& frame : retransmission.retransmittable_frames) { bool success = AddFrame(frame, false); DCHECK(success); } - SerializedPacket packet = SerializePacket(buffer, buffer_len); - DCHECK(packet.retransmittable_frames == nullptr); - return packet; + SerializePacket(buffer, buffer_len); + packet_.original_packet_number = retransmission.packet_number; + packet_.transmission_type = retransmission.transmission_type; + OnSerializedPacket(); + // Restore old values. + packet_.packet_number_length = saved_length; + next_packet_number_length_ = saved_next_length; + fec_protect_ = saved_should_fec_protect; + packet_.encryption_level = default_encryption_level; } void QuicPacketCreator::Flush() { @@ -458,24 +455,21 @@ void QuicPacketCreator::Flush() { // TODO(rtenneti): Change the default 64 alignas value (used the default // value from CACHELINE_SIZE). ALIGNAS(64) char seralized_packet_buffer[kMaxPacketSize]; - SerializedPacket serialized_packet = - SerializePacket(seralized_packet_buffer, kMaxPacketSize); - OnSerializedPacket(&serialized_packet); + SerializePacket(seralized_packet_buffer, kMaxPacketSize); + OnSerializedPacket(); } -void QuicPacketCreator::OnSerializedPacket(SerializedPacket* packet) { - if (packet->packet == nullptr) { - LOG(DFATAL) << "Failed to SerializePacket. fec_policy:" << fec_send_policy() - << " should_fec_protect_:" << should_fec_protect_next_packet_; - delegate_->CloseConnection(QUIC_FAILED_TO_SERIALIZE_PACKET, false); +void QuicPacketCreator::OnSerializedPacket() { + if (packet_.encrypted_buffer == nullptr) { + QUIC_BUG << "Failed to SerializePacket. fec_policy:" << fec_send_policy() + << " should_fec_protect_:" << should_fec_protect_next_packet_; + delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, + ConnectionCloseSource::FROM_SELF); return; } - // There may be AckListeners interested in this packet. - packet->listeners.swap(ack_listeners_); - DCHECK(ack_listeners_.empty()); - delegate_->OnSerializedPacket(packet); - has_ack_ = false; - has_stop_waiting_ = false; + + delegate_->OnSerializedPacket(&packet_); + ClearPacket(); MaybeSendFecPacketAndCloseGroup(/*force_send_fec=*/false, /*is_fec_timeout=*/false); // Maximum packet size may be only enacted while no packet is currently being @@ -485,13 +479,26 @@ void QuicPacketCreator::OnSerializedPacket(SerializedPacket* packet) { } } +void QuicPacketCreator::ClearPacket() { + packet_.has_ack = false; + packet_.has_stop_waiting = false; + packet_.has_crypto_handshake = NOT_HANDSHAKE; + packet_.needs_padding = false; + packet_.is_fec_packet = false; + packet_.original_packet_number = 0; + packet_.transmission_type = NOT_RETRANSMISSION; + packet_.encrypted_buffer = nullptr; + packet_.encrypted_length = 0; + DCHECK(packet_.retransmittable_frames.empty()); + packet_.listeners.clear(); +} + bool QuicPacketCreator::HasPendingFrames() const { return !queued_frames_.empty(); } bool QuicPacketCreator::HasPendingRetransmittableFrames() const { - return queued_retransmittable_frames_.get() != nullptr && - !queued_retransmittable_frames_->frames().empty(); + return !packet_.retransmittable_frames.empty(); } size_t QuicPacketCreator::ExpansionOnNewFrame() const { @@ -506,23 +513,24 @@ size_t QuicPacketCreator::ExpansionOnNewFrame() const { return has_trailing_stream_frame ? kQuicStreamPayloadLengthSize : 0; } -size_t QuicPacketCreator::BytesFree() const { +size_t QuicPacketCreator::BytesFree() { DCHECK_GE(max_plaintext_size_, PacketSize()); return max_plaintext_size_ - min(max_plaintext_size_, PacketSize() + ExpansionOnNewFrame()); } -size_t QuicPacketCreator::PacketSize() const { +size_t QuicPacketCreator::PacketSize() { if (!queued_frames_.empty()) { return packet_size_; } if (fec_group_.get() == nullptr) { // Update packet number length on packet and FEC boundary. - packet_number_length_ = next_packet_number_length_; + packet_.packet_number_length = next_packet_number_length_; } - packet_size_ = GetPacketHeaderSize( - connection_id_length_, send_version_in_packet_, /*include_path_id=*/false, - packet_number_length_, fec_protect_ ? IN_FEC_GROUP : NOT_IN_FEC_GROUP); + packet_size_ = + GetPacketHeaderSize(connection_id_length_, send_version_in_packet_, + send_path_id_in_packet_, packet_.packet_number_length, + fec_protect_ ? IN_FEC_GROUP : NOT_IN_FEC_GROUP); return packet_size_; } @@ -532,7 +540,7 @@ bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame) { bool QuicPacketCreator::AddPaddedSavedFrame(const QuicFrame& frame) { if (AddFrame(frame, /*save_retransmittable_frames=*/true)) { - needs_padding_ = true; + packet_.needs_padding = true; return true; } return false; @@ -541,16 +549,15 @@ bool QuicPacketCreator::AddPaddedSavedFrame(const QuicFrame& frame) { void QuicPacketCreator::AddAckListener(QuicAckListenerInterface* listener, QuicPacketLength length) { DCHECK(!queued_frames_.empty()); - ack_listeners_.push_back(AckListenerWrapper(listener, length)); + packet_.listeners.emplace_back(listener, length); } -SerializedPacket QuicPacketCreator::SerializePacket( - char* encrypted_buffer, - size_t encrypted_buffer_len) { +void QuicPacketCreator::SerializePacket(char* encrypted_buffer, + size_t encrypted_buffer_len) { DCHECK_LT(0u, encrypted_buffer_len); - LOG_IF(DFATAL, queued_frames_.empty()) << "Attempt to serialize empty packet"; + QUIC_BUG_IF(queued_frames_.empty()) << "Attempt to serialize empty packet"; if (fec_group_.get() != nullptr) { - DCHECK_GE(packet_number_ + 1, fec_group_->FecGroupNumber()); + DCHECK_GE(packet_.packet_number + 1, fec_group_->FecGroupNumber()); } QuicPacketHeader header; // FillPacketHeader increments packet_number_. @@ -569,101 +576,56 @@ SerializedPacket QuicPacketCreator::SerializePacket( queued_frames_.back().type == ACK_FRAME; // Use the packet_size_ instead of the buffer size to ensure smaller // packet sizes are properly used. - size_t encrypted_length = 0u; - if (FLAGS_quic_inplace_encryption) { - size_t length = framer_->BuildDataPacket(header, queued_frames_, - encrypted_buffer, packet_size_); - if (length == 0) { - LOG(DFATAL) << "Failed to serialize " << queued_frames_.size() - << " frames."; - return NoPacket(); - } + size_t length = framer_->BuildDataPacket(header, queued_frames_, + encrypted_buffer, packet_size_); + if (length == 0) { + QUIC_BUG << "Failed to serialize " << queued_frames_.size() << " frames."; + return; + } - // TODO(ianswett) Consider replacing QuicPacket with something else, since - // it's only used to provide convenience methods to FEC and encryption. - QuicPacket packet(encrypted_buffer, length, - /* owns_buffer */ false, - header.public_header.connection_id_length, - header.public_header.version_flag, - header.public_header.packet_number_length); - OnBuiltFecProtectedPayload(header, packet.FecProtectedData()); - - // Because of possible truncation, we can't be confident that our - // packet size calculation worked correctly. - if (!possibly_truncated_by_length) { - DCHECK_EQ(packet_size_, length); - } - // Immediately encrypt the packet, to ensure we don't encrypt the same - // packet number multiple times. - encrypted_length = - framer_->EncryptPayload(encryption_level_, packet_number_, packet, - encrypted_buffer, encrypted_buffer_len); - } else { - // The optimized encryption algorithm implementations run faster when - // operating on aligned memory. - // TODO(rtenneti): Change the default 64 alignas value (used the default - // value from CACHELINE_SIZE). - ALIGNAS(64) char buffer[kMaxPacketSize]; - size_t length = - framer_->BuildDataPacket(header, queued_frames_, buffer, packet_size_); - if (length == 0) { - LOG(DFATAL) << "Failed to serialize " << queued_frames_.size() - << " frames."; - return NoPacket(); - } + // TODO(ianswett) Consider replacing QuicPacket with something else, since + // it's only used to provide convenience methods to FEC and encryption. + QuicPacket packet( + encrypted_buffer, length, + /* owns_buffer */ false, header.public_header.connection_id_length, + header.public_header.version_flag, header.public_header.multipath_flag, + header.public_header.packet_number_length); + OnBuiltFecProtectedPayload(header, packet.FecProtectedData()); - // TODO(ianswett) Consider replacing QuicPacket with something else, since - // it's only used to provide convenience methods to FEC and encryption. - QuicPacket packet(buffer, length, - /* owns_buffer */ false, - header.public_header.connection_id_length, - header.public_header.version_flag, - header.public_header.packet_number_length); - OnBuiltFecProtectedPayload(header, packet.FecProtectedData()); - - // Because of possible truncation, we can't be confident that our - // packet size calculation worked correctly. - if (!possibly_truncated_by_length) { - DCHECK_EQ(packet_size_, length); - } - // Immediately encrypt the packet, to ensure we don't encrypt the same - // packet number multiple times. - encrypted_length = - framer_->EncryptPayload(encryption_level_, packet_number_, packet, - encrypted_buffer, encrypted_buffer_len); + // Because of possible truncation, we can't be confident that our + // packet size calculation worked correctly. + if (!possibly_truncated_by_length) { + DCHECK_EQ(packet_size_, length); } + // Immediately encrypt the packet, to ensure we don't encrypt the same + // packet number multiple times. + size_t encrypted_length = framer_->EncryptPayload( + packet_.encryption_level, packet_.path_id, packet_.packet_number, packet, + encrypted_buffer, encrypted_buffer_len); if (encrypted_length == 0) { - LOG(DFATAL) << "Failed to encrypt packet number " << packet_number_; - return NoPacket(); - } - - // Update |needs_padding_| flag of |queued_retransmittable_frames_| here, and - // not in AddFrame, because when the first padded frame is added to the queue, - // it might not be retransmittable, and hence the flag would end up being not - // set. - if (queued_retransmittable_frames_.get() != nullptr) { - queued_retransmittable_frames_->set_needs_padding(needs_padding_); + QUIC_BUG << "Failed to encrypt packet number " << packet_.packet_number; + return; } packet_size_ = 0; queued_frames_.clear(); - needs_padding_ = false; - return SerializedPacket(current_path_, header.packet_number, - header.public_header.packet_number_length, - encrypted_buffer, encrypted_length, - /* owns_buffer*/ false, - QuicFramer::GetPacketEntropyHash(header), - queued_retransmittable_frames_.release(), has_ack_, - has_stop_waiting_, encryption_level_); -} - -SerializedPacket QuicPacketCreator::SerializeFec(char* buffer, - size_t buffer_len) { + packet_.entropy_hash = QuicFramer::GetPacketEntropyHash(header); + packet_.encrypted_buffer = encrypted_buffer; + packet_.encrypted_length = encrypted_length; +} + +void QuicPacketCreator::SerializeFec(char* buffer, size_t buffer_len) { DCHECK_LT(0u, buffer_len); if (fec_group_.get() == nullptr || 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? - return NoPacket(); + QUIC_BUG << "SerializeFEC called but no group or zero packets in group."; + return; + } + if (FLAGS_quic_no_unencrypted_fec && + packet_.encryption_level == ENCRYPTION_NONE) { + QUIC_BUG << "SerializeFEC must be called with encryption."; + delegate_->OnUnrecoverableError(QUIC_UNENCRYPTED_FEC_DATA, + ConnectionCloseSource::FROM_SELF); + return; } DCHECK_EQ(0u, queued_frames_.size()); QuicPacketHeader header; @@ -672,25 +634,22 @@ SerializedPacket QuicPacketCreator::SerializeFec(char* buffer, framer_->BuildFecPacket(header, fec_group_->PayloadParity())); fec_group_.reset(nullptr); packet_size_ = 0; - LOG_IF(DFATAL, packet == nullptr) - << "Failed to serialize fec packet for group:" - << fec_group_->FecGroupNumber(); + QUIC_BUG_IF(packet == nullptr) << "Failed to serialize fec packet for group:" + << fec_group_->FecGroupNumber(); DCHECK_GE(max_packet_length_, packet->length()); // Immediately encrypt the packet, to ensure we don't encrypt the same packet // packet number multiple times. size_t encrypted_length = framer_->EncryptPayload( - encryption_level_, packet_number_, *packet, buffer, buffer_len); + packet_.encryption_level, packet_.path_id, packet_.packet_number, *packet, + buffer, buffer_len); if (encrypted_length == 0) { - LOG(DFATAL) << "Failed to encrypt packet number " << packet_number_; - return NoPacket(); + QUIC_BUG << "Failed to encrypt packet number " << packet_.packet_number; + return; } - SerializedPacket serialized(current_path_, header.packet_number, - header.public_header.packet_number_length, buffer, - encrypted_length, /* owns_buffer */ false, - QuicFramer::GetPacketEntropyHash(header), nullptr, - false, false, encryption_level_); - serialized.is_fec_packet = true; - return serialized; + packet_.entropy_hash = QuicFramer::GetPacketEntropyHash(header); + packet_.encrypted_buffer = buffer; + packet_.encrypted_length = encrypted_length; + packet_.is_fec_packet = true; } QuicEncryptedPacket* QuicPacketCreator::SerializeVersionNegotiationPacket( @@ -703,9 +662,10 @@ QuicEncryptedPacket* QuicPacketCreator::SerializeVersionNegotiationPacket( return encrypted; } +// TODO(jri): Make this a public method of framer? SerializedPacket QuicPacketCreator::NoPacket() { return SerializedPacket(kInvalidPathId, 0, PACKET_1BYTE_PACKET_NUMBER, - nullptr, 0, nullptr, false, false); + nullptr, 0, 0, false, false); } void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group, @@ -713,11 +673,13 @@ void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group, QuicPacketHeader* header) { header->public_header.connection_id = connection_id_; header->public_header.connection_id_length = connection_id_length_; + header->public_header.multipath_flag = send_path_id_in_packet_; header->public_header.reset_flag = false; header->public_header.version_flag = send_version_in_packet_; header->fec_flag = fec_flag; - header->packet_number = ++packet_number_; - header->public_header.packet_number_length = packet_number_length_; + header->path_id = packet_.path_id; + header->packet_number = ++packet_.packet_number; + header->public_header.packet_number_length = packet_.packet_number_length; 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; @@ -740,16 +702,17 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame, DVLOG(1) << "Adding frame: " << frame; if (FLAGS_quic_never_write_unencrypted_data && frame.type == STREAM_FRAME && frame.stream_frame->stream_id != kCryptoStreamId && - encryption_level_ == ENCRYPTION_NONE) { - LOG(DFATAL) << "Cannot send stream data without encryption."; - delegate_->CloseConnection(QUIC_UNENCRYPTED_STREAM_DATA, false); + packet_.encryption_level == ENCRYPTION_NONE) { + QUIC_BUG << "Cannot send stream data without encryption."; + delegate_->OnUnrecoverableError(QUIC_UNENCRYPTED_STREAM_DATA, + ConnectionCloseSource::FROM_SELF); return false; } InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec(); size_t frame_len = framer_->GetSerializedFrameLength( frame, BytesFree(), queued_frames_.empty(), true, is_in_fec_group, - packet_number_length_); + packet_.packet_number_length); if (frame_len == 0) { // Current open packet is full. Flush(); @@ -759,19 +722,24 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame, packet_size_ += ExpansionOnNewFrame() + frame_len; if (save_retransmittable_frames && ShouldRetransmit(frame)) { - if (queued_retransmittable_frames_.get() == nullptr) { - queued_retransmittable_frames_.reset(new RetransmittableFrames()); + if (packet_.retransmittable_frames.empty()) { + packet_.retransmittable_frames.reserve(2); + } + packet_.retransmittable_frames.push_back(frame); + queued_frames_.push_back(frame); + if (frame.type == STREAM_FRAME && + frame.stream_frame->stream_id == kCryptoStreamId) { + packet_.has_crypto_handshake = IS_HANDSHAKE; } - queued_frames_.push_back(queued_retransmittable_frames_->AddFrame(frame)); } else { queued_frames_.push_back(frame); } if (frame.type == ACK_FRAME) { - has_ack_ = true; + packet_.has_ack = true; } if (frame.type == STOP_WAITING_FRAME) { - has_stop_waiting_ = true; + packet_.has_stop_waiting = true; } if (debug_delegate_ != nullptr) { debug_delegate_->OnFrameAddedToPacket(frame); @@ -781,7 +749,7 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame, } void QuicPacketCreator::MaybeAddPadding() { - if (!needs_padding_) { + if (!packet_.needs_padding) { return; } @@ -811,16 +779,17 @@ void QuicPacketCreator::MaybeStartFecProtection() { void QuicPacketCreator::MaybeSendFecPacketAndCloseGroup(bool force_send_fec, bool is_fec_timeout) { if (ShouldSendFec(force_send_fec)) { - if (fec_send_policy_ == FEC_ALARM_TRIGGER && !is_fec_timeout) { + if ((FLAGS_quic_no_unencrypted_fec && + packet_.encryption_level == ENCRYPTION_NONE) || + (fec_send_policy_ == FEC_ALARM_TRIGGER && !is_fec_timeout)) { ResetFecGroup(); delegate_->OnResetFecGroup(); } else { // TODO(zhongyi): Change the default 64 alignas value (used the default // value from CACHELINE_SIZE). ALIGNAS(64) char seralized_fec_buffer[kMaxPacketSize]; - SerializedPacket serialized_fec = - SerializeFec(seralized_fec_buffer, kMaxPacketSize); - OnSerializedPacket(&serialized_fec); + SerializeFec(seralized_fec_buffer, kMaxPacketSize); + OnSerializedPacket(); } } @@ -855,13 +824,12 @@ void QuicPacketCreator::SetCurrentPath( QuicPathId path_id, QuicPacketNumber least_packet_awaited_by_peer, QuicPacketCount max_packets_in_flight) { - if (current_path_ == path_id) { + if (packet_.path_id == path_id) { return; } if (HasPendingFrames()) { - LOG(DFATAL) - << "Unable to change paths when a packet is under construction."; + QUIC_BUG << "Unable to change paths when a packet is under construction."; return; } @@ -869,12 +837,15 @@ void QuicPacketCreator::SetCurrentPath( MaybeSendFecPacketAndCloseGroup(/*force_send_fec=*/true, /*is_fec_timeout=*/false); // Save current packet number and load switching path's packet number. - multipath_packet_number_[current_path_] = packet_number_; - hash_map::iterator it = + multipath_packet_number_[packet_.path_id] = packet_.packet_number; + std::unordered_map::iterator it = multipath_packet_number_.find(path_id); // If path_id is not in the map, it's a new path. Set packet_number to 0. - packet_number_ = it == multipath_packet_number_.end() ? 0 : it->second; - current_path_ = path_id; + packet_.packet_number = it == multipath_packet_number_.end() ? 0 : it->second; + packet_.path_id = path_id; + DCHECK(packet_.path_id != kInvalidPathId); + // Send path in packet if current path is not the default path. + send_path_id_in_packet_ = packet_.path_id != kDefaultPathId ? true : false; // Switching path needs to update packet number length. UpdatePacketNumberLength(least_packet_awaited_by_peer, max_packets_in_flight); } diff --git a/src/net/quic/quic_packet_creator.h b/src/net/quic/quic_packet_creator.h index ded7678a..fcb4b508 100644 --- a/src/net/quic/quic_packet_creator.h +++ b/src/net/quic/quic_packet_creator.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -45,7 +46,11 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // and |packet.retransmittable_frames|. If it does so, they must be set // to nullptr. virtual void OnSerializedPacket(SerializedPacket* serialized_packet) = 0; - virtual void CloseConnection(QuicErrorCode error, bool from_peer) = 0; + + // Called when an unrecoverable error is encountered. + virtual void OnUnrecoverableError(QuicErrorCode error, + ConnectionCloseSource source) = 0; + // Called when current FEC group is reset (closed). virtual void OnResetFecGroup() = 0; }; @@ -105,6 +110,7 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { static size_t StreamFramePacketOverhead( QuicConnectionIdLength connection_id_length, bool include_version, + bool include_path_id, QuicPacketNumberLength packet_number_length, QuicStreamOffset offset, InFecGroup is_in_fec_group); @@ -126,26 +132,15 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // Returns true if current open packet can accommodate more stream frames of // stream |id| at |offset|, false otherwise. - bool HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset) const; - - // Serializes all frames into a single packet. All frames must fit into a - // single packet. Also, sets the entropy hash of the serialized packet to a - // random bool and returns that value as a member of SerializedPacket. - // Never returns a RetransmittableFrames in SerializedPacket. - SerializedPacket SerializeAllFrames(const QuicFrames& frames, - char* buffer, - size_t buffer_len); + bool HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset); // Re-serializes frames with the original packet's packet 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 RetransmittableFrames& frames, - EncryptionLevel original_encryption_level, - QuicPacketNumberLength original_length, - char* buffer, - size_t buffer_len); + void ReserializeAllFrames(const PendingRetransmission& retransmission, + char* buffer, + size_t buffer_len); // Serializes all added frames into a single packet and invokes the delegate_ // to further process the SerializedPacket. @@ -161,7 +156,7 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // frames in the packet. Since stream frames are slightly smaller when they // are the last frame in a packet, this method will return a different // value than max_packet_size - PacketSize(), in this case. - size_t BytesFree() const; + size_t BytesFree(); // 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 @@ -174,7 +169,7 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // if serialized with the current frames. Adding a frame to the packet // may change the serialized length of existing frames, as per the comment // in BytesFree. - size_t PacketSize() const; + size_t PacketSize(); // Tries to add |frame| to the packet creator's list of frames to be // serialized. If the frame does not fit into the current packet, flushes the @@ -185,8 +180,7 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { bool AddPaddedSavedFrame(const QuicFrame& frame); // Adds |listener| to the next serialized packet and notifies the - // std::listener - // with |length| as the number of acked bytes. + // std::listener with |length| as the number of acked bytes. void AddAckListener(QuicAckListenerInterface* listener, QuicPacketLength length); @@ -208,12 +202,12 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // Sets the encryption level that will be applied to new packets. void set_encryption_level(EncryptionLevel level) { - encryption_level_ = level; + packet_.encryption_level = level; } // packet number of the last created packet, or 0 if no packets have been // created. - QuicPacketNumber packet_number() const { return packet_number_; } + QuicPacketNumber packet_number() const { return packet_.packet_number; } QuicConnectionIdLength connection_id_length() const { return connection_id_length_; @@ -225,9 +219,9 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { QuicByteCount max_packet_length() const { return max_packet_length_; } - bool has_ack() const { return has_ack_; } + bool has_ack() const { return packet_.has_ack; } - bool has_stop_waiting() const { return has_stop_waiting_; } + bool has_stop_waiting() const { return packet_.has_stop_waiting; } // Sets the encrypter to use for the encryption level and updates the max // plaintext size. @@ -325,18 +319,18 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { void MaybeAddPadding(); // Serializes all frames which have been added and adds any which should be - // retransmitted to queued_retransmittable_frames_ if it's not nullptr. All - // frames must fit into a single packet. Sets the entropy hash of the - // serialized packet to a random bool and returns that value as a member of - // SerializedPacket. Also, sets |serialized_frames| in the SerializedPacket to - // the corresponding RetransmittableFrames if any frames are to be - // retransmitted. + // retransmitted to packet_.retransmittable_frames. All frames must fit into + // a single packet. Sets the entropy hash of the serialized packet to a + // random bool. // Fails if |buffer_len| isn't long enough for the encrypted packet. - SerializedPacket SerializePacket(char* encrypted_buffer, size_t buffer_len); + void SerializePacket(char* encrypted_buffer, size_t buffer_len); // Called after a new SerialiedPacket is created to call the delegate's // OnSerializedPacket, reset state, and potentially flush FEC groups. - void OnSerializedPacket(SerializedPacket* packet); + void OnSerializedPacket(); + + // Clears all fields of packet_ that should be cleared between serializations. + void ClearPacket(); // Turn on FEC protection for subsequent packets. If no FEC group is currently // open, this method flushes current open packet and then turns FEC on. @@ -357,32 +351,52 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // packet boundary. void ResetFecGroup(); - // Packetize FEC data. All frames must fit into a single packet. Also, sets - // the entropy hash of the serialized packet to a random bool and returns - // that value as a member of SerializedPacket. + // Packetize FEC data. Sets the entropy hash of the serialized packet to a + // random bool. // Fails if |buffer_len| isn't long enough for the encrypted packet. - SerializedPacket SerializeFec(char* buffer, size_t buffer_len); + void SerializeFec(char* buffer, size_t buffer_len); - // Does not own these delegates. + // Does not own these delegates or the framer. DelegateInterface* delegate_; DebugDelegate* debug_delegate_; - - QuicConnectionId connection_id_; - EncryptionLevel encryption_level_; - // True if an ack is queued in queued_frames_. - bool has_ack_; - // True if a stop waiting frame is queued in queued_frames_. - bool has_stop_waiting_; QuicFramer* framer_; + scoped_ptr random_bool_source_; - // Map mapping path_id to last sent packet number on the path. - hash_map multipath_packet_number_; - // The path which current constructed packet will be sent on. - QuicPathId current_path_; QuicBufferAllocator* const buffer_allocator_; - QuicPacketNumber packet_number_; + + // Controls whether version should be included while serializing the packet. + bool send_version_in_packet_; + // Controls whether path id should be included while serializing the packet. + bool send_path_id_in_packet_; + // Staging variable to hold next packet 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 packet_number_length_ + // can be changed to this new value. + QuicPacketNumberLength next_packet_number_length_; + // Maximum length including headers and encryption (UDP payload length.) + QuicByteCount max_packet_length_; + size_t max_plaintext_size_; + // Length of connection_id to send over the wire. + QuicConnectionIdLength connection_id_length_; + + // Frames to be added to the next SerializedPacket + QuicFrames queued_frames_; + + // packet_size should never be read directly, use PacketSize() instead. + // TODO(ianswett): Move packet_size_ into SerializedPacket once + // QuicEncryptedPacket has been flattened into SerializedPacket. + size_t packet_size_; + QuicConnectionId connection_id_; + + // Packet used to invoke OnSerializedPacket. + SerializedPacket packet_; + + // Map mapping path_id to last sent packet number on the path. + std::unordered_map multipath_packet_number_; + + // FEC related fields. // True when creator is requested to turn on FEC protection. False otherwise. - // There is a time difference between should_fec_protect_next_packet is + // There is a time difference between should_fec_protect_next_packet_ is // true/false and FEC is actually turned on/off (e.g., The creator may have an // open FEC group even if this variable is false). bool should_fec_protect_next_packet_; @@ -391,35 +405,8 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // one variable. bool fec_protect_; scoped_ptr fec_group_; - // Controls whether protocol version should be included while serializing the - // packet. - bool send_version_in_packet_; - // Maximum length including headers and encryption (UDP payload length.) - QuicByteCount 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 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 packet_number_length_ - // can be changed to this new value. - QuicPacketNumberLength next_packet_number_length_; - // packet 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 QuicPacketNumberLength packet_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. - mutable size_t packet_size_; - mutable size_t max_plaintext_size_; - QuicFrames queued_frames_; - scoped_ptr queued_retransmittable_frames_; - // If true, the packet will be padded up to |max_packet_length_|. - bool needs_padding_; - // Stores ack std::listeners that should be attached to the next packet. - std::list ack_listeners_; - // FEC policy that specifies when to send FEC packet. FecSendPolicy fec_send_policy_; // Timeout used for FEC alarm. Can be set to zero initially or if the SRTT has diff --git a/src/net/quic/quic_packet_generator.cc b/src/net/quic/quic_packet_generator.cc index 00293a18..f9d6ed1b 100644 --- a/src/net/quic/quic_packet_generator.cc +++ b/src/net/quic/quic_packet_generator.cc @@ -5,6 +5,7 @@ #include "net/quic/quic_packet_generator.h" #include "base/logging.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_fec_group.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" @@ -32,40 +33,7 @@ QuicPacketGenerator::QuicPacketGenerator(QuicConnectionId connection_id, max_packet_length_(kDefaultMaxPacketSize) {} QuicPacketGenerator::~QuicPacketGenerator() { - for (QuicFrame& frame : queued_control_frames_) { - switch (frame.type) { - case PADDING_FRAME: - case MTU_DISCOVERY_FRAME: - case PING_FRAME: - break; - case STREAM_FRAME: - delete frame.stream_frame; - break; - case ACK_FRAME: - delete frame.ack_frame; - break; - case RST_STREAM_FRAME: - delete frame.rst_stream_frame; - break; - case CONNECTION_CLOSE_FRAME: - delete frame.connection_close_frame; - break; - case GOAWAY_FRAME: - delete frame.goaway_frame; - break; - case WINDOW_UPDATE_FRAME: - delete frame.window_update_frame; - break; - case BLOCKED_FRAME: - delete frame.blocked_frame; - break; - case STOP_WAITING_FRAME: - delete frame.stop_waiting_frame; - break; - case NUM_FRAME_TYPES: - DCHECK(false) << "Cannot delete type: " << frame.type; - } - } + QuicUtils::DeleteFrames(&queued_control_frames_); } void QuicPacketGenerator::OnCongestionWindowChange( @@ -84,7 +52,7 @@ void QuicPacketGenerator::SetShouldSendAck(bool also_send_stop_waiting) { } if (also_send_stop_waiting && packet_creator_.has_stop_waiting()) { - LOG(DFATAL) << "Should only ever be one pending stop waiting frame."; + QUIC_BUG << "Should only ever be one pending stop waiting frame."; return; } @@ -120,7 +88,7 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( } if (!fin && (iov.total_length == 0)) { - LOG(DFATAL) << "Attempt to consume empty data without FIN."; + QUIC_BUG << "Attempt to consume empty data without FIN."; return QuicConsumedData(0, false); } @@ -132,7 +100,7 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( has_handshake, &frame, fec_protection)) { // The creator is always flushed if there's not enough room for a new // stream frame before ConsumeData, so ConsumeData should always succeed. - LOG(DFATAL) << "Failed to ConsumeData, stream:" << id; + QUIC_BUG << "Failed to ConsumeData, stream:" << id; return QuicConsumedData(0, false); } @@ -231,7 +199,7 @@ void QuicPacketGenerator::SendQueuedFrames(bool flush, bool is_fec_timeout) { void QuicPacketGenerator::OnFecTimeout() { DCHECK(!InBatchMode()); if (!packet_creator_.ShouldSendFec(true)) { - LOG(DFATAL) << "No FEC packet to send on FEC timeout."; + QUIC_BUG << "No FEC packet to send on FEC timeout."; return; } // Flush out any pending frames in the generator and the creator, and then @@ -291,7 +259,7 @@ bool QuicPacketGenerator::AddNextPendingFrame() { return !should_send_stop_waiting_; } - LOG_IF(DFATAL, queued_control_frames_.empty()) + QUIC_BUG_IF(queued_control_frames_.empty()) << "AddNextPendingFrame called with no queued control frames."; if (!packet_creator_.AddSavedFrame(queued_control_frames_.back())) { // Packet was full. @@ -339,14 +307,11 @@ QuicEncryptedPacket* QuicPacketGenerator::SerializeVersionNegotiationPacket( return packet_creator_.SerializeVersionNegotiationPacket(supported_versions); } -SerializedPacket QuicPacketGenerator::ReserializeAllFrames( - const RetransmittableFrames& frames, - EncryptionLevel original_encryption_level, - QuicPacketNumberLength original_length, +void QuicPacketGenerator::ReserializeAllFrames( + const PendingRetransmission& retransmission, char* buffer, size_t buffer_len) { - return packet_creator_.ReserializeAllFrames( - frames, original_encryption_level, original_length, buffer, buffer_len); + packet_creator_.ReserializeAllFrames(retransmission, buffer, buffer_len); } void QuicPacketGenerator::UpdateSequenceNumberLength( diff --git a/src/net/quic/quic_packet_generator.h b/src/net/quic/quic_packet_generator.h index 8f3182d8..6d956e36 100644 --- a/src/net/quic/quic_packet_generator.h +++ b/src/net/quic/quic_packet_generator.h @@ -148,12 +148,9 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { // 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 RetransmittableFrames& frames, - EncryptionLevel original_encryption_level, - QuicPacketNumberLength original_length, - char* buffer, - size_t buffer_len); + void ReserializeAllFrames(const PendingRetransmission& retransmission, + char* buffer, + size_t buffer_len); // Update the packet number length to use in future packets as soon as it // can be safely changed. diff --git a/src/net/quic/quic_packet_writer.h b/src/net/quic/quic_packet_writer.h index 4c9963ad..de6804b9 100644 --- a/src/net/quic/quic_packet_writer.h +++ b/src/net/quic/quic_packet_writer.h @@ -12,8 +12,24 @@ namespace net { +class IPAddress; struct WriteResult; +class NET_EXPORT_PRIVATE PerPacketOptions { + public: + PerPacketOptions() = default; + virtual ~PerPacketOptions() {} + + // Returns a heap-allocated copy of |this|. + virtual PerPacketOptions* Clone() const = 0; + + private: + PerPacketOptions(PerPacketOptions&& other) = delete; + PerPacketOptions& operator=(PerPacketOptions&& other) = delete; + + DISALLOW_COPY_AND_ASSIGN(PerPacketOptions); +}; + // An interface between writers and the entity managing the // socket (in our case the QuicDispatcher). This allows the Dispatcher to // control writes, and manage any writers who end up write blocked. @@ -21,14 +37,17 @@ class NET_EXPORT_PRIVATE QuicPacketWriter { public: virtual ~QuicPacketWriter() {} - // Sends the packet out to the peer. If the write succeeded, the result's - // status is WRITE_STATUS_OK and bytes_written is populated. If the write - // failed, the result's status is WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR - // and error_code is populated. + // Sends the packet out to the peer, with some optional per-packet options. + // If the write succeeded, the result's status is WRITE_STATUS_OK and + // bytes_written is populated. If the write failed, the result's status is + // WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR and error_code is populated. + // Options must be either null, or created for the particular QuicPacketWriter + // implementation. Options may be ignored, depending on the implementation. virtual WriteResult WritePacket(const char* buffer, size_t buf_len, - const IPAddressNumber& self_address, - const IPEndPoint& peer_address) = 0; + const IPAddress& self_address, + const IPEndPoint& peer_address, + PerPacketOptions* options) = 0; // Returns true if the writer buffers and subsequently rewrites data // when an attempt to write results in the underlying socket becoming diff --git a/src/net/quic/quic_protocol.cc b/src/net/quic/quic_protocol.cc index 7626f0b3..7de88d77 100644 --- a/src/net/quic/quic_protocol.cc +++ b/src/net/quic/quic_protocol.cc @@ -39,18 +39,20 @@ size_t GetPacketHeaderSize(QuicConnectionIdLength connection_id_length, size_t GetStartOfFecProtectedData(QuicConnectionIdLength connection_id_length, bool include_version, + bool include_path_id, QuicPacketNumberLength packet_number_length) { return GetPacketHeaderSize(connection_id_length, include_version, - /*include_path_id=*/false, packet_number_length, + include_path_id, packet_number_length, IN_FEC_GROUP); } size_t GetStartOfEncryptedData(QuicConnectionIdLength connection_id_length, bool include_version, + bool include_path_id, QuicPacketNumberLength packet_number_length) { // Don't include the fec size, since encryption starts before private flags. return GetPacketHeaderSize(connection_id_length, include_version, - /*include_path_id=*/false, packet_number_length, + include_path_id, packet_number_length, NOT_IN_FEC_GROUP) - kPrivateFlagsSize; } @@ -76,24 +78,26 @@ QuicPacketPublicHeader::QuicPacketPublicHeader( QuicPacketPublicHeader::~QuicPacketPublicHeader() {} QuicPacketHeader::QuicPacketHeader() - : path_id(kDefaultPathId), - packet_number(0), - fec_flag(false), + : packet_number(0), + path_id(kDefaultPathId), entropy_flag(false), entropy_hash(0), + fec_flag(false), is_in_fec_group(NOT_IN_FEC_GROUP), fec_group(0) {} QuicPacketHeader::QuicPacketHeader(const QuicPacketPublicHeader& header) : public_header(header), - path_id(kDefaultPathId), packet_number(0), - fec_flag(false), + path_id(kDefaultPathId), entropy_flag(false), entropy_hash(0), + fec_flag(false), is_in_fec_group(NOT_IN_FEC_GROUP), fec_group(0) {} +QuicPacketHeader::QuicPacketHeader(const QuicPacketHeader& other) = default; + QuicPublicResetPacket::QuicPublicResetPacket() : nonce_proof(0), rejected_packet_number(0) {} @@ -292,9 +296,11 @@ QuicAckFrame::QuicAckFrame() entropy_hash(0), is_truncated(false), largest_observed(0), - delta_time_largest_observed(QuicTime::Delta::Infinite()), + ack_delay_time(QuicTime::Delta::Infinite()), latest_revived_packet(0) {} +QuicAckFrame::QuicAckFrame(const QuicAckFrame& other) = default; + QuicAckFrame::~QuicAckFrame() {} QuicRstStreamErrorCode AdjustErrorForVersion(QuicRstStreamErrorCode error_code, @@ -349,6 +355,9 @@ QuicFrame::QuicFrame(QuicWindowUpdateFrame* frame) QuicFrame::QuicFrame(QuicBlockedFrame* frame) : type(BLOCKED_FRAME), blocked_frame(frame) {} +QuicFrame::QuicFrame(QuicPathCloseFrame* frame) + : type(PATH_CLOSE_FRAME), path_close_frame(frame) {} + ostream& operator<<(ostream& os, const QuicStopWaitingFrame& sent_info) { os << "entropy_hash: " << static_cast(sent_info.entropy_hash) << " least_unacked: " << sent_info.least_unacked; @@ -359,7 +368,9 @@ PacketNumberQueue::const_iterator::const_iterator( IntervalSet::const_iterator interval_set_iter, QuicPacketNumber first, QuicPacketNumber last) - : interval_set_iter_(interval_set_iter), current_(first), last_(last) {} + : interval_set_iter_(std::move(interval_set_iter)), + current_(first), + last_(last) {} PacketNumberQueue::const_iterator::const_iterator(const const_iterator& other) = default; @@ -386,8 +397,7 @@ bool PacketNumberQueue::const_iterator::operator==( } PacketNumberQueue::const_iterator::value_type - PacketNumberQueue::const_iterator:: - operator*() const { + PacketNumberQueue::const_iterator::operator*() const { return current_; } @@ -520,8 +530,7 @@ ostream& operator<<(ostream& os, const PacketNumberQueue& q) { ostream& operator<<(ostream& os, const QuicAckFrame& ack_frame) { os << "entropy_hash: " << static_cast(ack_frame.entropy_hash) << " largest_observed: " << ack_frame.largest_observed - << " delta_time_largest_observed: " - << ack_frame.delta_time_largest_observed.ToMicroseconds() + << " ack_delay_time: " << ack_frame.ack_delay_time.ToMicroseconds() << " missing_packets: [ " << ack_frame.missing_packets << " ] is_truncated: " << ack_frame.is_truncated << " revived_packet: " << ack_frame.latest_revived_packet @@ -581,6 +590,10 @@ ostream& operator<<(ostream& os, const QuicFrame& frame) { os << "type { MTU_DISCOVERY_FRAME } "; break; } + case PATH_CLOSE_FRAME: { + os << "type { PATH_CLOSE_FRAME } " << *(frame.path_close_frame); + break; + } default: { LOG(ERROR) << "Unknown frame type: " << frame.type; break; @@ -621,6 +634,11 @@ ostream& operator<<(ostream& os, const QuicBlockedFrame& blocked_frame) { return os; } +ostream& operator<<(ostream& os, const QuicPathCloseFrame& path_close_frame) { + os << "path_id { " << path_close_frame.path_id << " }\n"; + return os; +} + ostream& operator<<(ostream& os, const QuicStreamFrame& stream_frame) { os << "stream_id { " << stream_frame.stream_id << " } " << "fin { " << stream_frame.fin << " } " @@ -660,16 +678,20 @@ QuicWindowUpdateFrame::QuicWindowUpdateFrame(QuicStreamId stream_id, QuicBlockedFrame::QuicBlockedFrame(QuicStreamId stream_id) : stream_id(stream_id) {} +QuicPathCloseFrame::QuicPathCloseFrame(QuicPathId path_id) : path_id(path_id) {} + QuicPacket::QuicPacket(char* buffer, size_t length, bool owns_buffer, QuicConnectionIdLength connection_id_length, bool includes_version, + bool includes_path_id, QuicPacketNumberLength packet_number_length) : QuicData(buffer, length, owns_buffer), buffer_(buffer), connection_id_length_(connection_id_length), includes_version_(includes_version), + includes_path_id_(includes_path_id), packet_number_length_(packet_number_length) {} QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer, size_t length) @@ -681,109 +703,49 @@ QuicEncryptedPacket::QuicEncryptedPacket(char* buffer, : QuicData(buffer, length, owns_buffer) {} StringPiece QuicPacket::FecProtectedData() const { - const size_t start_of_fec = GetStartOfFecProtectedData( - connection_id_length_, includes_version_, packet_number_length_); + const size_t start_of_fec = + GetStartOfFecProtectedData(connection_id_length_, includes_version_, + includes_path_id_, packet_number_length_); return StringPiece(data() + start_of_fec, length() - start_of_fec); } StringPiece QuicPacket::AssociatedData() const { - return StringPiece( - data(), GetStartOfEncryptedData(connection_id_length_, includes_version_, - packet_number_length_)); + return StringPiece(data(), GetStartOfEncryptedData( + connection_id_length_, includes_version_, + includes_path_id_, packet_number_length_)); } StringPiece QuicPacket::Plaintext() const { - const size_t start_of_encrypted_data = GetStartOfEncryptedData( - connection_id_length_, includes_version_, packet_number_length_); + const size_t start_of_encrypted_data = + GetStartOfEncryptedData(connection_id_length_, includes_version_, + includes_path_id_, packet_number_length_); return StringPiece(data() + start_of_encrypted_data, length() - start_of_encrypted_data); } -RetransmittableFrames::RetransmittableFrames() - : has_crypto_handshake_(NOT_HANDSHAKE), needs_padding_(false) { - // TODO(ianswett): Consider using an inlined vector instead, since this - // is very frequently a single frame. - frames_.reserve(2); -} - -RetransmittableFrames::~RetransmittableFrames() { - for (QuicFrame& frame : frames_) { - switch (frame.type) { - // Frames smaller than a pointer are inlined, so don't need to be deleted. - case PADDING_FRAME: - case MTU_DISCOVERY_FRAME: - case PING_FRAME: - break; - case STREAM_FRAME: - delete frame.stream_frame; - break; - case ACK_FRAME: - delete frame.ack_frame; - break; - case STOP_WAITING_FRAME: - delete frame.stop_waiting_frame; - break; - case RST_STREAM_FRAME: - delete frame.rst_stream_frame; - break; - case CONNECTION_CLOSE_FRAME: - delete frame.connection_close_frame; - break; - case GOAWAY_FRAME: - delete frame.goaway_frame; - break; - case WINDOW_UPDATE_FRAME: - delete frame.window_update_frame; - break; - case BLOCKED_FRAME: - delete frame.blocked_frame; - break; - case NUM_FRAME_TYPES: - DCHECK(false) << "Cannot delete type: " << frame.type; - } - } -} - -const QuicFrame& RetransmittableFrames::AddFrame(const QuicFrame& frame) { - if (frame.type == STREAM_FRAME && - frame.stream_frame->stream_id == kCryptoStreamId) { - has_crypto_handshake_ = IS_HANDSHAKE; - } - frames_.push_back(frame); - return frame; -} - -void RetransmittableFrames::RemoveFramesForStream(QuicStreamId stream_id) { - QuicFrames::iterator it = frames_.begin(); - while (it != frames_.end()) { - if (it->type != STREAM_FRAME || it->stream_frame->stream_id != stream_id) { - ++it; - continue; - } - delete it->stream_frame; - it = frames_.erase(it); - } -} - AckListenerWrapper::AckListenerWrapper(QuicAckListenerInterface* listener, QuicPacketLength data_length) : ack_listener(listener), length(data_length) { DCHECK(listener != nullptr); } +AckListenerWrapper::AckListenerWrapper(const AckListenerWrapper& other) = + default; + AckListenerWrapper::~AckListenerWrapper() {} -SerializedPacket::SerializedPacket( - QuicPathId path_id, - QuicPacketNumber packet_number, - QuicPacketNumberLength packet_number_length, - QuicEncryptedPacket* packet, - QuicPacketEntropyHash entropy_hash, - RetransmittableFrames* retransmittable_frames, - bool has_ack, - bool has_stop_waiting) - : packet(packet), - retransmittable_frames(retransmittable_frames), +SerializedPacket::SerializedPacket(QuicPathId path_id, + QuicPacketNumber packet_number, + QuicPacketNumberLength packet_number_length, + const char* encrypted_buffer, + QuicPacketLength encrypted_length, + QuicPacketEntropyHash entropy_hash, + bool has_ack, + bool has_stop_waiting) + : encrypted_buffer(encrypted_buffer), + encrypted_length(encrypted_length), + has_crypto_handshake(NOT_HANDSHAKE), + needs_padding(false), path_id(path_id), packet_number(packet_number), packet_number_length(packet_number_length), @@ -791,34 +753,11 @@ SerializedPacket::SerializedPacket( entropy_hash(entropy_hash), is_fec_packet(false), has_ack(has_ack), - has_stop_waiting(has_stop_waiting) {} - -SerializedPacket::SerializedPacket( - QuicPathId path_id, - QuicPacketNumber packet_number, - QuicPacketNumberLength packet_number_length, - char* encrypted_buffer, - size_t encrypted_length, - bool owns_buffer, - QuicPacketEntropyHash entropy_hash, - RetransmittableFrames* retransmittable_frames, - bool has_ack, - bool has_stop_waiting, - EncryptionLevel level) - : SerializedPacket(path_id, - packet_number, - packet_number_length, - new QuicEncryptedPacket(encrypted_buffer, - encrypted_length, - owns_buffer), - entropy_hash, - retransmittable_frames, - has_ack, - has_stop_waiting) { - // TODO(ianswett): Move into the initializer list once SerializedPacket - // no longer contains an encrypted packet. - encryption_level = level; -} + has_stop_waiting(has_stop_waiting), + original_packet_number(0), + transmission_type(NOT_RETRANSMISSION) {} + +SerializedPacket::SerializedPacket(const SerializedPacket& other) = default; SerializedPacket::~SerializedPacket() {} @@ -834,8 +773,7 @@ ostream& operator<<(ostream& os, const QuicEncryptedPacket& s) { } TransmissionInfo::TransmissionInfo() - : retransmittable_frames(nullptr), - encryption_level(ENCRYPTION_NONE), + : encryption_level(ENCRYPTION_NONE), packet_number_length(PACKET_1BYTE_PACKET_NUMBER), bytes_sent(0), nack_count(0), @@ -844,19 +782,19 @@ TransmissionInfo::TransmissionInfo() in_flight(false), is_unackable(false), is_fec_packet(false), - all_transmissions(nullptr), + has_crypto_handshake(false), + needs_padding(false), retransmission(0) {} -TransmissionInfo::TransmissionInfo( - RetransmittableFrames* retransmittable_frames, - EncryptionLevel level, - QuicPacketNumberLength packet_number_length, - TransmissionType transmission_type, - QuicTime sent_time, - QuicPacketLength bytes_sent, - bool is_fec_packet) - : retransmittable_frames(retransmittable_frames), - encryption_level(level), +TransmissionInfo::TransmissionInfo(EncryptionLevel level, + QuicPacketNumberLength packet_number_length, + TransmissionType transmission_type, + QuicTime sent_time, + QuicPacketLength bytes_sent, + bool is_fec_packet, + bool has_crypto_handshake, + bool needs_padding) + : encryption_level(level), packet_number_length(packet_number_length), bytes_sent(bytes_sent), nack_count(0), @@ -865,9 +803,12 @@ TransmissionInfo::TransmissionInfo( in_flight(false), is_unackable(false), is_fec_packet(is_fec_packet), - all_transmissions(nullptr), + has_crypto_handshake(has_crypto_handshake), + needs_padding(needs_padding), retransmission(0) {} +TransmissionInfo::TransmissionInfo(const TransmissionInfo& other) = default; + TransmissionInfo::~TransmissionInfo() {} } // namespace net diff --git a/src/net/quic/quic_protocol.h b/src/net/quic/quic_protocol.h index a8166075..6c5de3f0 100644 --- a/src/net/quic/quic_protocol.h +++ b/src/net/quic/quic_protocol.h @@ -28,7 +28,6 @@ #include "net/base/ip_endpoint.h" #include "net/base/net_export.h" #include "net/quic/interval_set.h" -#include "net/quic/quic_ack_listener_interface.h" #include "net/quic/quic_bandwidth.h" #include "net/quic/quic_time.h" #include "net/quic/quic_types.h" @@ -125,6 +124,9 @@ const bool kIncludeVersion = true; // Signifies that the QuicPacket will contain path id. const bool kIncludePathId = true; +// Stream ID is reserved to denote an invalid ID. +const QuicStreamId kInvalidStreamId = 0; + // Reserved ID for the crypto stream. const QuicStreamId kCryptoStreamId = 1; @@ -177,6 +179,12 @@ const int kMaxStreamsMinimumIncrement = 10; // of available streams is 10 times the limit on the number of open streams. const int kMaxAvailableStreamsMultiplier = 10; +// Track the number of promises that are not yet claimed by a +// corresponding get. This must be smaller than +// kMaxAvailableStreamsMultiplier, because RST on a promised stream my +// create available streams entries. +const int kMaxPromisedStreamsMultiplier = kMaxAvailableStreamsMultiplier - 1; + // 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 @@ -199,7 +207,7 @@ const QuicPathId kDefaultPathId = 0; // Invalid path ID. const QuicPathId kInvalidPathId = 0xff; -enum TransmissionType { +enum TransmissionType : int8_t { NOT_RETRANSMISSION, FIRST_TRANSMISSION_TYPE = NOT_RETRANSMISSION, HANDSHAKE_RETRANSMISSION, // Retransmits due to handshake timeouts. @@ -211,15 +219,18 @@ enum TransmissionType { LAST_TRANSMISSION_TYPE = TLP_RETRANSMISSION, }; -enum HasRetransmittableData { +enum HasRetransmittableData : int8_t { NO_RETRANSMITTABLE_DATA, HAS_RETRANSMITTABLE_DATA, }; -enum IsHandshake { NOT_HANDSHAKE, IS_HANDSHAKE }; +enum IsHandshake : int8_t { NOT_HANDSHAKE, IS_HANDSHAKE }; enum class Perspective { IS_SERVER, IS_CLIENT }; +// Describes whether a ConnectionClose was originated by the peer. +enum class ConnectionCloseSource { FROM_PEER, FROM_SELF }; + NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, const Perspective& s); @@ -254,6 +265,7 @@ enum QuicFrameType { BLOCKED_FRAME = 5, STOP_WAITING_FRAME = 6, PING_FRAME = 7, + PATH_CLOSE_FRAME = 8, // STREAM and ACK frames are special frames. They are encoded differently on // the wire and their values do not need to be stable. @@ -276,7 +288,7 @@ enum InFecGroup { IN_FEC_GROUP, }; -enum QuicPacketNumberLength { +enum QuicPacketNumberLength : int8_t { PACKET_1BYTE_PACKET_NUMBER = 1, PACKET_2BYTE_PACKET_NUMBER = 2, PACKET_4BYTE_PACKET_NUMBER = 4, @@ -324,7 +336,7 @@ enum QuicPacketPublicFlags { // Bit 6: Does the packet header contain a path id? PACKET_PUBLIC_FLAGS_MULTIPATH = 1 << 6, - // All bits set (bit7 are not currently used): 01111111 + // All bits set (bit 7 is not currently used): 01111111 PACKET_PUBLIC_FLAGS_MAX = (1 << 7) - 1, }; @@ -428,11 +440,14 @@ GetPacketHeaderSize(QuicConnectionIdLength connection_id_length, NET_EXPORT_PRIVATE size_t GetStartOfFecProtectedData(QuicConnectionIdLength connection_id_length, bool include_version, + bool include_path_id, QuicPacketNumberLength packet_number_length); + // Index of the first byte in a QUIC packet of encrypted data. NET_EXPORT_PRIVATE size_t GetStartOfEncryptedData(QuicConnectionIdLength connection_id_length, bool include_version, + bool include_path_id, QuicPacketNumberLength packet_number_length); enum QuicRstStreamErrorCode { @@ -460,7 +475,16 @@ enum QuicRstStreamErrorCode { // has been reached). The sender should retry the request later (using // another stream). QUIC_REFUSED_STREAM, - + // Invalid URL in PUSH_PROMISE request header. + QUIC_INVALID_PROMISE_URL, + // Server is not authoritative for this URL. + QUIC_UNAUTHORIZED_PROMISE_URL, + // Can't have more than one active PUSH_PROMISE per URL. + QUIC_DUPLICATE_PROMISE_URL, + // Vary check failed. + QUIC_PROMISE_VARY_MISMATCH, + // Only GET and HEAD methods allowed. + QUIC_INVALID_PROMISE_METHOD, // No error. Used as bound while iterating. QUIC_STREAM_LAST_ERROR, }; @@ -474,7 +498,6 @@ 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. -// last value = 77 enum QuicErrorCode { QUIC_NO_ERROR = 0, @@ -494,6 +517,8 @@ enum QuicErrorCode { QUIC_INVALID_STREAM_DATA = 46, // STREAM frame data is not encrypted. QUIC_UNENCRYPTED_STREAM_DATA = 61, + // FEC frame data is not encrypted. + QUIC_UNENCRYPTED_FEC_DATA = 77, // RST_STREAM frame data is malformed. QUIC_INVALID_RST_STREAM_DATA = 6, // CONNECTION_CLOSE frame data is malformed. @@ -506,6 +531,8 @@ enum QuicErrorCode { QUIC_INVALID_BLOCKED_DATA = 58, // STOP_WAITING frame data is malformed. QUIC_INVALID_STOP_WAITING_DATA = 60, + // PATH_CLOSE frame data is malformed. + QUIC_INVALID_PATH_CLOSE_DATA = 78, // ACK frame data is malformed. QUIC_INVALID_ACK_DATA = 9, @@ -540,10 +567,10 @@ enum QuicErrorCode { QUIC_INVALID_NEGOTIATED_VALUE = 23, // There was an error decompressing data. QUIC_DECOMPRESSION_FAILURE = 24, - // We hit our prenegotiated (or default) timeout - QUIC_CONNECTION_TIMED_OUT = 25, - // We hit our overall connection timeout - QUIC_CONNECTION_OVERALL_TIMED_OUT = 67, + // The connection timed out due to no network activity. + QUIC_NETWORK_IDLE_TIMEOUT = 25, + // The connection timed out waiting for the handshake to complete. + QUIC_HANDSHAKE_TIMEOUT = 67, // There was an error encountered migrating addresses QUIC_ERROR_MIGRATING_ADDRESS = 26, // There was an error while writing to the socket. @@ -566,7 +593,7 @@ enum QuicErrorCode { QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS = 68, // The connection has too many outstanding received packets. QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS = 69, - // The quic connection job to load server config is cancelled. + // The quic connection has been cancelled. QUIC_CONNECTION_CANCELLED = 70, // Disabled QUIC because of high packet loss rate. QUIC_BAD_PACKET_LOSS_RATE = 71, @@ -634,8 +661,23 @@ enum QuicErrorCode { // tampered with. QUIC_VERSION_NEGOTIATION_MISMATCH = 55, + // Multipath is not enabled, but a packet with multipath flag on is received. + QUIC_BAD_MULTIPATH_FLAG = 79, + + // IP address changed causing connection close. + QUIC_IP_ADDRESS_CHANGED = 80, + + // Connection migration errors. + // Network changed, but connection had no migratable streams. + QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS = 81, + // Connection changed networks too many times. + QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES = 82, + // Connection migration was attempted, but there was no new network to + // migrate to. + QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK = 83, + // No error. Used as bound while iterating. - QUIC_LAST_ERROR = 77, + QUIC_LAST_ERROR = 84, }; // Must be updated any time a QuicErrorCode is deprecated. @@ -665,16 +707,17 @@ const QuicPacketNumber kInvalidPacketNumber = 0; struct NET_EXPORT_PRIVATE QuicPacketHeader { QuicPacketHeader(); explicit QuicPacketHeader(const QuicPacketPublicHeader& header); + QuicPacketHeader(const QuicPacketHeader& other); NET_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream& os, const QuicPacketHeader& s); QuicPacketPublicHeader public_header; - QuicPathId path_id; QuicPacketNumber packet_number; - bool fec_flag; + QuicPathId path_id; bool entropy_flag; QuicPacketEntropyHash entropy_hash; + bool fec_flag; InFecGroup is_in_fec_group; QuicFecGroupNumber fec_group; }; @@ -723,8 +766,17 @@ class NET_EXPORT_PRIVATE QuicBufferAllocator { // Returns or allocates a new buffer of |size|. Never returns null. virtual char* New(size_t size) = 0; + // Returns or allocates a new buffer of |size| if |flag_enable| is true. + // Otherwise, returns a buffer that is compatible with this class directly + // with operator new. Never returns null. + virtual char* New(size_t size, bool flag_enable) = 0; + // Releases a buffer. virtual void Delete(char* buffer) = 0; + + // Marks the allocator as being idle. Serves as a hint to notify the allocator + // that it should release any resources it's still holding on to. + virtual void MarkAllocatorIdle() {} }; // Deleter for stream buffers. Copyable to support platforms where the deleter @@ -815,8 +867,8 @@ struct NET_EXPORT_PRIVATE QuicStopWaitingFrame { // larger new packet numbers are added, with the occasional random access. class NET_EXPORT_PRIVATE PacketNumberQueue { public: - // TODO(jdorfman): remove const_iterator and change the callers to - // iterate over the intervals. + // TODO(jdorfman): remove const_iterator and change the callers to iterate + // over the intervals. class NET_EXPORT_PRIVATE const_iterator : public std::iterator for when packets arrived. PacketTimeVector received_packet_times; @@ -1051,11 +1104,28 @@ struct NET_EXPORT_PRIVATE QuicBlockedFrame { QuicStreamId stream_id; }; +// The PATH_CLOSE frame is used to explicitly close a path. Both endpoints can +// send a PATH_CLOSE frame to initiate a path termination. A path is considered +// to be closed either a PATH_CLOSE frame is sent or received. An endpoint drops +// receive side of a closed path, and packets with retransmittable frames on a +// closed path are marked as retransmissions which will be transmitted on other +// paths. +struct NET_EXPORT_PRIVATE QuicPathCloseFrame { + QuicPathCloseFrame() {} + explicit QuicPathCloseFrame(QuicPathId path_id); + + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, + const QuicPathCloseFrame& p); + + QuicPathId path_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 // understand. -enum EncryptionLevel { +enum EncryptionLevel : int8_t { ENCRYPTION_NONE = 0, ENCRYPTION_INITIAL = 1, ENCRYPTION_FORWARD_SECURE = 2, @@ -1064,20 +1134,20 @@ enum EncryptionLevel { }; enum PeerAddressChangeType { + // IP address and port remain unchanged. NO_CHANGE, - // Peer address changes which are considered to be cause by NATs. Currently, - // IPv4 address change with /24 does not change is considered to be cause by - // NATs. - NAT_PORT_REBINDING, - IPV4_SUBNET_REBINDING, - // IPv6 related address changes. - IPV4_TO_IPV6, - IPV6_TO_IPV4, - IPV6_TO_IPV6, - // This type is used when we always allow peer address changes. - UNKNOWN, - // All other peer address change types. - UNSPECIFIED, + // Port changed, but IP address remains unchanged. + PORT_CHANGE, + // IPv4 address changed, but within the /24 subnet (port may have changed.) + IPV4_SUBNET_CHANGE, + // IP address change from an IPv4 to an IPv6 address (port may have changed.) + IPV4_TO_IPV6_CHANGE, + // IP address change from an IPv6 to an IPv4 address (port may have changed.) + IPV6_TO_IPV4_CHANGE, + // IP address change from an IPv6 to an IPv6 address (port may have changed.) + IPV6_TO_IPV6_CHANGE, + // All other peer address changes. + UNSPECIFIED_CHANGE, }; struct NET_EXPORT_PRIVATE QuicFrame { @@ -1094,6 +1164,7 @@ struct NET_EXPORT_PRIVATE QuicFrame { explicit QuicFrame(QuicGoAwayFrame* frame); explicit QuicFrame(QuicWindowUpdateFrame* frame); explicit QuicFrame(QuicBlockedFrame* frame); + explicit QuicFrame(QuicPathCloseFrame* frame); NET_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream& os, const QuicFrame& frame); @@ -1114,6 +1185,7 @@ struct NET_EXPORT_PRIVATE QuicFrame { QuicGoAwayFrame* goaway_frame; QuicWindowUpdateFrame* window_update_frame; QuicBlockedFrame* blocked_frame; + QuicPathCloseFrame* path_close_frame; }; }; // QuicFrameType consumes 8 bytes with padding. @@ -1146,11 +1218,15 @@ class NET_EXPORT_PRIVATE QuicData { class NET_EXPORT_PRIVATE QuicPacket : public QuicData { public: + // TODO(fayang): 4 fields from public header are passed in as arguments. + // Consider to add a convenience method which directly accepts the entire + // public header. QuicPacket(char* buffer, size_t length, bool owns_buffer, QuicConnectionIdLength connection_id_length, bool includes_version, + bool includes_path_id, QuicPacketNumberLength packet_number_length); base::StringPiece FecProtectedData() const; @@ -1163,6 +1239,7 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData { char* buffer_; const QuicConnectionIdLength connection_id_length_; const bool includes_version_; + const bool includes_path_id_; const QuicPacketNumberLength packet_number_length_; DISALLOW_COPY_AND_ASSIGN(QuicPacket); @@ -1188,35 +1265,32 @@ class NET_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData { DISALLOW_COPY_AND_ASSIGN(QuicEncryptedPacket); }; -class NET_EXPORT_PRIVATE RetransmittableFrames { +// Pure virtual class to listen for packet acknowledgements. +class NET_EXPORT_PRIVATE QuicAckListenerInterface + : public base::RefCounted { public: - RetransmittableFrames(); - ~RetransmittableFrames(); - - // Takes ownership of the frame inside |frame|. - const QuicFrame& AddFrame(const QuicFrame& frame); - // Removes all stream frames associated with |stream_id|. - void RemoveFramesForStream(QuicStreamId stream_id); - - const QuicFrames& frames() const { return frames_; } + QuicAckListenerInterface() {} - IsHandshake HasCryptoHandshake() const { return has_crypto_handshake_; } + // Called when a packet is acked. Called once per packet. + // |acked_bytes| is the number of data bytes acked. + virtual void OnPacketAcked(int acked_bytes, + QuicTime::Delta ack_delay_time) = 0; - bool needs_padding() const { return needs_padding_; } + // Called when a packet is retransmitted. Called once per packet. + // |retransmitted_bytes| is the number of data bytes retransmitted. + virtual void OnPacketRetransmitted(int retransmitted_bytes) = 0; - void set_needs_padding(bool needs_padding) { needs_padding_ = needs_padding; } - - private: - QuicFrames frames_; - IsHandshake has_crypto_handshake_; - bool needs_padding_; + protected: + friend class base::RefCounted; - DISALLOW_COPY_AND_ASSIGN(RetransmittableFrames); + // Delegates are ref counted. + virtual ~QuicAckListenerInterface() {} }; struct NET_EXPORT_PRIVATE AckListenerWrapper { AckListenerWrapper(QuicAckListenerInterface* listener, QuicPacketLength data_length); + AckListenerWrapper(const AckListenerWrapper& other); ~AckListenerWrapper(); scoped_refptr ack_listener; @@ -1227,26 +1301,20 @@ struct NET_EXPORT_PRIVATE SerializedPacket { SerializedPacket(QuicPathId path_id, QuicPacketNumber packet_number, QuicPacketNumberLength packet_number_length, - QuicEncryptedPacket* packet, + const char* encrypted_buffer, + QuicPacketLength encrypted_length, QuicPacketEntropyHash entropy_hash, - RetransmittableFrames* retransmittable_frames, bool has_ack, bool has_stop_waiting); - SerializedPacket(QuicPathId path_id, - QuicPacketNumber packet_number, - QuicPacketNumberLength packet_number_length, - char* encrypted_buffer, - size_t encrypted_length, - bool owns_buffer, - QuicPacketEntropyHash entropy_hash, - RetransmittableFrames* retransmittable_frames, - bool has_ack, - bool has_stop_waiting, - EncryptionLevel level); + SerializedPacket(const SerializedPacket& other); ~SerializedPacket(); - QuicEncryptedPacket* packet; - RetransmittableFrames* retransmittable_frames; + // Not owned. + const char* encrypted_buffer; + QuicPacketLength encrypted_length; + QuicFrames retransmittable_frames; + IsHandshake has_crypto_handshake; + bool needs_padding; QuicPathId path_id; QuicPacketNumber packet_number; QuicPacketNumberLength packet_number_length; @@ -1255,6 +1323,8 @@ struct NET_EXPORT_PRIVATE SerializedPacket { bool is_fec_packet; bool has_ack; bool has_stop_waiting; + QuicPacketNumber original_packet_number; + TransmissionType transmission_type; // Optional notifiers which will be informed when this packet has been ACKed. std::list listeners; @@ -1264,19 +1334,22 @@ struct NET_EXPORT_PRIVATE TransmissionInfo { // Used by STL when assigning into a map. TransmissionInfo(); - // Constructs a Transmission with a new all_tranmissions set + // Constructs a Transmission with a new all_transmissions set // containing |packet_number|. - TransmissionInfo(RetransmittableFrames* retransmittable_frames, - EncryptionLevel level, + TransmissionInfo(EncryptionLevel level, QuicPacketNumberLength packet_number_length, TransmissionType transmission_type, QuicTime sent_time, QuicPacketLength bytes_sent, - bool is_fec_packet); + bool is_fec_packet, + bool has_crypto_handshake, + bool needs_padding); + + TransmissionInfo(const TransmissionInfo& other); ~TransmissionInfo(); - RetransmittableFrames* retransmittable_frames; + QuicFrames retransmittable_frames; EncryptionLevel encryption_level; QuicPacketNumberLength packet_number_length; QuicPacketLength bytes_sent; @@ -1290,18 +1363,47 @@ struct NET_EXPORT_PRIVATE TransmissionInfo { bool is_unackable; // True if the packet is an FEC packet. bool is_fec_packet; - // Stores the packet numbers of all transmissions of this packet. - // Must always be nullptr or have multiple elements. - // TODO(ianswett): Deprecate with quic_track_single_retransmission. - PacketNumberList* all_transmissions; + // True if the packet contains stream data from the crypto stream. + bool has_crypto_handshake; + // True if the packet needs padding if it's retransmitted. + bool needs_padding; // Stores the packet number of the next retransmission of this packet. // Zero if the packet has not been retransmitted. QuicPacketNumber retransmission; // Non-empty if there is a listener for this packet. std::list ack_listeners; }; -static_assert(sizeof(QuicFrame) <= 64, - "Keep the TransmissionInfo size to a cacheline."); +static_assert(sizeof(TransmissionInfo) <= 128, + "TODO(ianswett): Keep the TransmissionInfo size to a cacheline."); + +// Struct to store the pending retransmission information. +struct PendingRetransmission { + PendingRetransmission(QuicPathId path_id, + QuicPacketNumber packet_number, + TransmissionType transmission_type, + const QuicFrames& retransmittable_frames, + bool has_crypto_handshake, + bool needs_padding, + EncryptionLevel encryption_level, + QuicPacketNumberLength packet_number_length) + : packet_number(packet_number), + retransmittable_frames(retransmittable_frames), + transmission_type(transmission_type), + path_id(path_id), + has_crypto_handshake(has_crypto_handshake), + needs_padding(needs_padding), + encryption_level(encryption_level), + packet_number_length(packet_number_length) {} + + QuicPacketNumber packet_number; + const QuicFrames& retransmittable_frames; + TransmissionType transmission_type; + QuicPathId path_id; + bool has_crypto_handshake; + bool needs_padding; + EncryptionLevel encryption_level; + QuicPacketNumberLength packet_number_length; +}; // Convenience wrapper to wrap an iovec array and the total length, which must // be less than or equal to the actual total length of the iovecs. diff --git a/src/net/quic/quic_received_packet_manager.cc b/src/net/quic/quic_received_packet_manager.cc index 81e94de3..984aa6fe 100644 --- a/src/net/quic/quic_received_packet_manager.cc +++ b/src/net/quic/quic_received_packet_manager.cc @@ -9,8 +9,10 @@ #include "base/logging.h" #include "base/stl_util.h" +#include "base/strings/stringprintf.h" #include "net/base/linked_hash_map.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_connection_stats.h" using std::max; @@ -184,7 +186,8 @@ void QuicReceivedPacketManager::RecordPacketReceived( void QuicReceivedPacketManager::RecordPacketRevived( QuicPacketNumber packet_number) { - LOG_IF(DFATAL, !IsAwaitingPacket(packet_number)); + QUIC_BUG_IF(!IsAwaitingPacket(packet_number)) << base::StringPrintf( + "Not waiting for %llu", static_cast(packet_number)); ack_frame_updated_ = true; ack_frame_.latest_revived_packet = packet_number; } @@ -220,12 +223,12 @@ void QuicReceivedPacketManager::UpdateReceivedPacketInfo( if (time_largest_observed_ == QuicTime::Zero()) { // We have received no packets. - ack_frame->delta_time_largest_observed = QuicTime::Delta::Infinite(); + ack_frame->ack_delay_time = QuicTime::Delta::Infinite(); return; } // Ensure the delta is zero if approximate now is "in the past". - ack_frame->delta_time_largest_observed = + ack_frame->ack_delay_time = approximate_now < time_largest_observed_ ? QuicTime::Delta::Zero() : approximate_now.Subtract(time_largest_observed_); diff --git a/src/net/quic/quic_received_packet_manager.h b/src/net/quic/quic_received_packet_manager.h index c037aed3..404f24fd 100644 --- a/src/net/quic/quic_received_packet_manager.h +++ b/src/net/quic/quic_received_packet_manager.h @@ -144,6 +144,9 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager virtual bool ack_frame_updated() const; + // For logging purposes. + const QuicAckFrame& ack_frame() const { return ack_frame_; } + private: friend class test::QuicConnectionPeer; friend class test::QuicReceivedPacketManagerPeer; @@ -170,7 +173,7 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager // The time we received the largest_observed packet number, or zero if // no packet numbers have been received since UpdateReceivedPacketInfo. - // Needed for calculating delta_time_largest_observed. + // Needed for calculating ack_delay_time. QuicTime time_largest_observed_; QuicConnectionStats* stats_; diff --git a/src/net/quic/quic_sent_packet_manager.cc b/src/net/quic/quic_sent_packet_manager.cc index 3a39adaa..fbe446b2 100644 --- a/src/net/quic/quic_sent_packet_manager.cc +++ b/src/net/quic/quic_sent_packet_manager.cc @@ -11,12 +11,14 @@ #include "net/quic/congestion_control/pacing_sender.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/proto/cached_network_parameters.pb.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_connection_stats.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_utils_chromium.h" using std::max; using std::min; +using std::pair; namespace net { @@ -35,6 +37,8 @@ static const int64_t kMaxRetransmissionTimeMs = 60000; static const size_t kMaxRetransmissions = 10; // Maximum number of packets retransmitted upon an RTO. static const size_t kMaxRetransmissionsOnTimeout = 2; +// Minimum number of consecutive RTOs before path is considered to be degrading. +const size_t kMinTimeoutsBeforePathDegrading = 2; // Ensure the handshake timer isnt't faster than 10ms. // This limits the tenth retransmitted packet to 10s after the initial CHLO. @@ -49,11 +53,9 @@ static const int64_t kMinTailLossProbeTimeoutMs = 10; static const size_t kInitialUnpacedBurst = 10; bool HasCryptoHandshake(const TransmissionInfo& transmission_info) { - if (transmission_info.retransmittable_frames == nullptr) { - return false; - } - return transmission_info.retransmittable_frames->HasCryptoHandshake() == - IS_HANDSHAKE; + DCHECK(!transmission_info.has_crypto_handshake || + !transmission_info.retransmittable_frames.empty()); + return transmission_info.has_crypto_handshake; } } // namespace @@ -67,12 +69,14 @@ QuicSentPacketManager::QuicSentPacketManager( const QuicClock* clock, QuicConnectionStats* stats, CongestionControlType congestion_control_type, - LossDetectionType loss_type) + LossDetectionType loss_type, + MultipathDelegateInterface* delegate) : unacked_packets_(), perspective_(perspective), path_id_(path_id), clock_(clock), stats_(stats), + delegate_(delegate), debug_delegate_(nullptr), network_change_visitor_(nullptr), initial_congestion_window_(kInitialCongestionWindow), @@ -95,8 +99,7 @@ QuicSentPacketManager::QuicSentPacketManager( enable_half_rtt_tail_loss_probe_(false), using_pacing_(false), use_new_rto_(false), - handshake_confirmed_(false), - use_general_loss_algorithm_(FLAGS_quic_general_loss_algorithm) {} + handshake_confirmed_(false) {} QuicSentPacketManager::~QuicSentPacketManager() {} @@ -171,10 +174,7 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { static_cast(config.ReceivedSocketReceiveBuffer())); QuicByteCount max_cwnd_bytes = static_cast( receive_buffer_bytes_ * kConservativeReceiveBufferFraction); - if (FLAGS_quic_limit_max_cwnd) { - max_cwnd_bytes = - min(max_cwnd_bytes, kMaxCongestionWindow * kDefaultTCPMSS); - } + max_cwnd_bytes = min(max_cwnd_bytes, kMaxCongestionWindow * kDefaultTCPMSS); send_algorithm_->SetMaxCongestionWindow(max_cwnd_bytes); } send_algorithm_->SetFromConfig(config, perspective_); @@ -286,8 +286,7 @@ void QuicSentPacketManager::HandleAckForSentPackets( const QuicAckFrame& ack_frame) { // 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. - QuicTime::Delta delta_largest_observed = - ack_frame.delta_time_largest_observed; + QuicTime::Delta ack_delay_time = ack_frame.ack_delay_time; QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); for (QuicUnackedPacketMap::iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it, ++packet_number) { @@ -321,12 +320,13 @@ void QuicSentPacketManager::HandleAckForSentPackets( if (it->in_flight) { packets_acked_.push_back(std::make_pair(packet_number, it->bytes_sent)); } - MarkPacketHandled(packet_number, &(*it), delta_largest_observed); + MarkPacketHandled(packet_number, &(*it), ack_delay_time); } // Discard any retransmittable frames associated with revived packets. if (ack_frame.latest_revived_packet != 0) { - MarkPacketRevived(ack_frame.latest_revived_packet, delta_largest_observed); + MarkPacketNotRetransmittable(ack_frame.latest_revived_packet, + ack_delay_time); } } @@ -342,8 +342,7 @@ void QuicSentPacketManager::RetransmitUnackedPackets( QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it, ++packet_number) { - const RetransmittableFrames* frames = it->retransmittable_frames; - if (frames != nullptr && + if (!it->retransmittable_frames.empty() && (retransmission_type == ALL_UNACKED_RETRANSMISSION || it->encryption_level == ENCRYPTION_INITIAL)) { MarkForRetransmission(packet_number, retransmission_type); @@ -358,13 +357,17 @@ void QuicSentPacketManager::NeuterUnencryptedPackets() { QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it, ++packet_number) { - if (it->retransmittable_frames != nullptr && + if (!it->retransmittable_frames.empty() && it->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(packet_number); + if (delegate_ != nullptr) { + delegate_->OnUnencryptedPacketsNeutered(path_id_, packet_number); + } else { + pending_retransmissions_.erase(packet_number); + } unacked_packets_.RemoveFromInFlight(packet_number); unacked_packets_.RemoveRetransmittability(packet_number); } @@ -376,61 +379,46 @@ void QuicSentPacketManager::MarkForRetransmission( TransmissionType transmission_type) { const TransmissionInfo& transmission_info = unacked_packets_.GetTransmissionInfo(packet_number); - LOG_IF(DFATAL, transmission_info.retransmittable_frames == nullptr); + QUIC_BUG_IF(transmission_info.retransmittable_frames.empty()); // Both TLP and the new RTO leave the packets in flight and let the loss // detection decide if packets are lost. if (transmission_type != TLP_RETRANSMISSION && transmission_type != RTO_RETRANSMISSION) { unacked_packets_.RemoveFromInFlight(packet_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_, packet_number)) { - return; + if (delegate_ != nullptr) { + delegate_->OnRetransmissionMarked(path_id_, packet_number, + transmission_type); + } else { + // 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_, packet_number)) { + return; + } + + pending_retransmissions_[packet_number] = transmission_type; } +} - pending_retransmissions_[packet_number] = transmission_type; +void QuicSentPacketManager::RecordOneSpuriousRetransmission( + const TransmissionInfo& info) { + stats_->bytes_spuriously_retransmitted += info.bytes_sent; + ++stats_->packets_spuriously_retransmitted; + if (debug_delegate_ != nullptr) { + debug_delegate_->OnSpuriousPacketRetransmission(info.transmission_type, + info.bytes_sent); + } } void QuicSentPacketManager::RecordSpuriousRetransmissions( const TransmissionInfo& info, QuicPacketNumber acked_packet_number) { - if (unacked_packets_.track_single_retransmission()) { - QuicPacketNumber retransmission = info.retransmission; - while (retransmission != 0) { - const TransmissionInfo& retransmit_info = - unacked_packets_.GetTransmissionInfo(retransmission); - retransmission = retransmit_info.retransmission; - stats_->bytes_spuriously_retransmitted += retransmit_info.bytes_sent; - ++stats_->packets_spuriously_retransmitted; - if (debug_delegate_ != nullptr) { - debug_delegate_->OnSpuriousPacketRetransmission( - retransmit_info.transmission_type, retransmit_info.bytes_sent); - } - } - return; - } - const PacketNumberList* all_transmissions = info.all_transmissions; - for (PacketNumberList::const_reverse_iterator it = - all_transmissions->rbegin(); - it != all_transmissions->rend() && *it > acked_packet_number; ++it) { - // ianswett: Prevents crash in b/20552846. - if (*it < unacked_packets_.GetLeastUnacked() || - *it > unacked_packets_.largest_sent_packet()) { - LOG(DFATAL) << "Retransmission out of range:" << *it - << " least unacked:" << unacked_packets_.GetLeastUnacked() - << " largest sent:" << unacked_packets_.largest_sent_packet(); - return; - } + QuicPacketNumber retransmission = info.retransmission; + while (retransmission != 0) { const TransmissionInfo& retransmit_info = - unacked_packets_.GetTransmissionInfo(*it); - - stats_->bytes_spuriously_retransmitted += retransmit_info.bytes_sent; - ++stats_->packets_spuriously_retransmitted; - if (debug_delegate_ != nullptr) { - debug_delegate_->OnSpuriousPacketRetransmission( - retransmit_info.transmission_type, retransmit_info.bytes_sent); - } + unacked_packets_.GetTransmissionInfo(retransmission); + retransmission = retransmit_info.retransmission; + RecordOneSpuriousRetransmission(retransmit_info); } } @@ -438,9 +426,8 @@ bool QuicSentPacketManager::HasPendingRetransmissions() const { return !pending_retransmissions_.empty(); } -QuicSentPacketManager::PendingRetransmission -QuicSentPacketManager::NextPendingRetransmission() { - LOG_IF(DFATAL, pending_retransmissions_.empty()) +PendingRetransmission QuicSentPacketManager::NextPendingRetransmission() { + QUIC_BUG_IF(pending_retransmissions_.empty()) << "Unexpected call to PendingRetransmissions() with empty pending " << "retransmission list. Corrupted memory usage imminent."; QuicPacketNumber packet_number = pending_retransmissions_.begin()->first; @@ -459,10 +446,12 @@ QuicSentPacketManager::NextPendingRetransmission() { DCHECK(unacked_packets_.IsUnacked(packet_number)) << packet_number; const TransmissionInfo& transmission_info = unacked_packets_.GetTransmissionInfo(packet_number); - DCHECK(transmission_info.retransmittable_frames); + DCHECK(!transmission_info.retransmittable_frames.empty()); return PendingRetransmission(path_id_, packet_number, transmission_type, - *transmission_info.retransmittable_frames, + transmission_info.retransmittable_frames, + transmission_info.has_crypto_handshake, + transmission_info.needs_padding, transmission_info.encryption_level, transmission_info.packet_number_length); } @@ -470,24 +459,18 @@ QuicSentPacketManager::NextPendingRetransmission() { QuicPacketNumber QuicSentPacketManager::GetNewestRetransmission( QuicPacketNumber packet_number, const TransmissionInfo& transmission_info) const { - if (unacked_packets_.track_single_retransmission()) { - QuicPacketNumber retransmission = transmission_info.retransmission; - while (retransmission != 0) { - packet_number = retransmission; - retransmission = - unacked_packets_.GetTransmissionInfo(retransmission).retransmission; - } - return packet_number; - } else { - return transmission_info.all_transmissions == nullptr - ? packet_number - : *transmission_info.all_transmissions->rbegin(); + QuicPacketNumber retransmission = transmission_info.retransmission; + while (retransmission != 0) { + packet_number = retransmission; + retransmission = + unacked_packets_.GetTransmissionInfo(retransmission).retransmission; } + return packet_number; } -void QuicSentPacketManager::MarkPacketRevived( +void QuicSentPacketManager::MarkPacketNotRetransmittable( QuicPacketNumber packet_number, - QuicTime::Delta delta_largest_observed) { + QuicTime::Delta ack_delay_time) { if (!unacked_packets_.IsUnacked(packet_number)) { return; } @@ -496,35 +479,41 @@ void QuicSentPacketManager::MarkPacketRevived( unacked_packets_.GetTransmissionInfo(packet_number); QuicPacketNumber newest_transmission = GetNewestRetransmission(packet_number, transmission_info); - // 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); + // We do not need to retransmit this packet anymore. + if (delegate_ != nullptr) { + delegate_->OnPacketMarkedNotRetransmittable(path_id_, newest_transmission, + ack_delay_time); + } else { + pending_retransmissions_.erase(newest_transmission); + } // The AckListener needs to be notified for revived packets, // since it indicates the packet arrived from the appliction's perspective. - unacked_packets_.NotifyAndClearListeners(newest_transmission, - delta_largest_observed); - + unacked_packets_.NotifyAndClearListeners(newest_transmission, ack_delay_time); unacked_packets_.RemoveRetransmittability(packet_number); } -void QuicSentPacketManager::MarkPacketHandled( - QuicPacketNumber packet_number, - TransmissionInfo* info, - QuicTime::Delta delta_largest_observed) { +void QuicSentPacketManager::MarkPacketHandled(QuicPacketNumber packet_number, + TransmissionInfo* info, + QuicTime::Delta ack_delay_time) { QuicPacketNumber newest_transmission = GetNewestRetransmission(packet_number, *info); // Remove the most recent packet, if it is pending retransmission. - pending_retransmissions_.erase(newest_transmission); + if (delegate_ != nullptr) { + delegate_->OnPacketMarkedHandled(path_id_, newest_transmission, + ack_delay_time); + } else { + pending_retransmissions_.erase(newest_transmission); + } // The AckListener needs to be notified about the most recent // transmission, since that's the one only one it tracks. if (newest_transmission == packet_number) { unacked_packets_.NotifyAndClearListeners(&info->ack_listeners, - delta_largest_observed); + ack_delay_time); } else { unacked_packets_.NotifyAndClearListeners(newest_transmission, - delta_largest_observed); + ack_delay_time); RecordSpuriousRetransmissions(*info, packet_number); // Remove the most recent packet from flight if it's a crypto handshake // packet, since they won't be acked now that one has been processed. @@ -559,19 +548,19 @@ bool QuicSentPacketManager::OnPacketSent( SerializedPacket* serialized_packet, QuicPacketNumber original_packet_number, QuicTime sent_time, - QuicByteCount bytes, TransmissionType transmission_type, HasRetransmittableData has_retransmittable_data) { QuicPacketNumber packet_number = serialized_packet->packet_number; DCHECK_LT(0u, packet_number); DCHECK(!unacked_packets_.IsUnacked(packet_number)); - LOG_IF(DFATAL, bytes == 0) << "Cannot send empty packets."; + QUIC_BUG_IF(serialized_packet->encrypted_length == 0) + << "Cannot send empty packets."; - if (original_packet_number != 0) { + if (delegate_ == nullptr && original_packet_number != 0) { if (!pending_retransmissions_.erase(original_packet_number)) { - DLOG(DFATAL) << "Expected packet number to be in " - << "pending_retransmissions_. packet_number: " - << original_packet_number; + QUIC_BUG << "Expected packet number to be in " + << "pending_retransmissions_. packet_number: " + << original_packet_number; } } @@ -587,15 +576,11 @@ bool QuicSentPacketManager::OnPacketSent( : has_retransmittable_data; // TODO(ianswett): Remove sent_time, because it's unused. const bool in_flight = send_algorithm_->OnPacketSent( - sent_time, unacked_packets_.bytes_in_flight(), packet_number, bytes, - has_congestion_controlled_data); + sent_time, unacked_packets_.bytes_in_flight(), packet_number, + serialized_packet->encrypted_length, has_congestion_controlled_data); unacked_packets_.AddSentPacket(serialized_packet, original_packet_number, - transmission_type, sent_time, bytes, - in_flight); - - // Take ownership of the retransmittable frames before exiting. - serialized_packet->retransmittable_frames = nullptr; + transmission_type, sent_time, in_flight); // Reset the retransmission timer anytime a pending packet is sent. return in_flight; } @@ -632,6 +617,10 @@ void QuicSentPacketManager::OnRetransmissionTimeout() { case RTO_MODE: ++stats_->rto_count; RetransmitRtoPackets(); + if (network_change_visitor_ != nullptr && + consecutive_rto_count_ == kMinTimeoutsBeforePathDegrading) { + network_change_visitor_->OnPathDegrading(); + } return; } } @@ -644,8 +633,8 @@ void QuicSentPacketManager::RetransmitCryptoPackets() { for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it, ++packet_number) { // Only retransmit frames which are in flight, and therefore have been sent. - if (!it->in_flight || it->retransmittable_frames == nullptr || - it->retransmittable_frames->HasCryptoHandshake() != IS_HANDSHAKE) { + if (!it->in_flight || it->retransmittable_frames.empty() || + !it->has_crypto_handshake) { continue; } packet_retransmitted = true; @@ -663,11 +652,11 @@ bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() { for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it, ++packet_number) { // Only retransmit frames which are in flight, and therefore have been sent. - if (!it->in_flight || it->retransmittable_frames == nullptr) { + if (!it->in_flight || it->retransmittable_frames.empty()) { continue; } if (!handshake_confirmed_) { - DCHECK_NE(IS_HANDSHAKE, it->retransmittable_frames->HasCryptoHandshake()); + DCHECK(!it->has_crypto_handshake); } MarkForRetransmission(packet_number, TLP_RETRANSMISSION); return true; @@ -678,26 +667,30 @@ bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() { } void QuicSentPacketManager::RetransmitRtoPackets() { - LOG_IF(DFATAL, pending_timer_transmission_count_ > 0) + QUIC_BUG_IF(pending_timer_transmission_count_ > 0) << "Retransmissions already queued:" << pending_timer_transmission_count_; // Mark two packets for retransmission. QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it, ++packet_number) { - if (it->retransmittable_frames != nullptr && + if (!it->retransmittable_frames.empty() && pending_timer_transmission_count_ < kMaxRetransmissionsOnTimeout) { MarkForRetransmission(packet_number, RTO_RETRANSMISSION); ++pending_timer_transmission_count_; } // Abandon non-retransmittable data that's in flight to ensure it doesn't // fill up the congestion window. - const bool has_retransmissions = - unacked_packets_.track_single_retransmission() - ? it->retransmission != 0 - : it->all_transmissions != nullptr; - if (it->retransmittable_frames == nullptr && it->in_flight && + const bool has_retransmissions = it->retransmission != 0; + if (it->retransmittable_frames.empty() && it->in_flight && !has_retransmissions) { + // Log only for non-retransmittable data. + // Retransmittable data is marked as lost during loss detection, and will + // be logged later. unacked_packets_.RemoveFromInFlight(packet_number); + if (FLAGS_quic_log_loss_event && debug_delegate_ != nullptr) { + debug_delegate_->OnPacketLoss(packet_number, RTO_RETRANSMISSION, + clock_->Now()); + } } } if (pending_timer_transmission_count_ > 0) { @@ -726,55 +719,30 @@ QuicSentPacketManager::GetRetransmissionMode() const { } void QuicSentPacketManager::InvokeLossDetection(QuicTime time) { - if (use_general_loss_algorithm_) { - loss_algorithm_->DetectLosses(unacked_packets_, time, rtt_stats_, - &packets_lost_); - for (const std::pair& pair : - packets_lost_) { - ++stats_->packets_lost; - // TODO(ianswett): This could be optimized. - if (unacked_packets_.HasRetransmittableFrames(pair.first)) { - MarkForRetransmission(pair.first, 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(pair.first); - } - } - return; - } - PacketNumberSet lost_packets = loss_algorithm_->DetectLostPackets( - unacked_packets_, time, unacked_packets_.largest_observed(), rtt_stats_); - for (PacketNumberSet::const_iterator it = lost_packets.begin(); - it != lost_packets.end(); ++it) { - QuicPacketNumber packet_number = *it; - const TransmissionInfo& transmission_info = - unacked_packets_.GetTransmissionInfo(packet_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. + loss_algorithm_->DetectLosses(unacked_packets_, time, rtt_stats_, + &packets_lost_); + for (const pair& pair : packets_lost_) { ++stats_->packets_lost; - packets_lost_.push_back( - std::make_pair(packet_number, transmission_info.bytes_sent)); - DVLOG(1) << ENDPOINT << "Lost packet " << packet_number; + if (FLAGS_quic_log_loss_event && debug_delegate_ != nullptr) { + debug_delegate_->OnPacketLoss(pair.first, LOSS_RETRANSMISSION, time); + } - if (transmission_info.retransmittable_frames != nullptr) { - MarkForRetransmission(packet_number, LOSS_RETRANSMISSION); + // TODO(ianswett): This could be optimized. + if (unacked_packets_.HasRetransmittableFrames(pair.first)) { + MarkForRetransmission(pair.first, 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(packet_number); + // a packet whose previous transmission has been acked, a packet that + // has been TLP retransmitted, or an FEC packet. + unacked_packets_.RemoveFromInFlight(pair.first); } } } bool QuicSentPacketManager::MaybeUpdateRTT(const QuicAckFrame& ack_frame, const QuicTime& ack_receive_time) { - // We rely on delta_time_largest_observed to compute an RTT estimate, so we + // We rely on ack_delay_time to compute an RTT estimate, so we // only update rtt when the largest observed gets acked. // NOTE: If ack is a truncated ack, then the largest observed is in fact // unacked, and may cause an RTT sample to be taken. @@ -787,15 +755,14 @@ bool QuicSentPacketManager::MaybeUpdateRTT(const QuicAckFrame& ack_frame, unacked_packets_.GetTransmissionInfo(ack_frame.largest_observed); // Ensure the packet has a valid sent time. if (transmission_info.sent_time == QuicTime::Zero()) { - LOG(DFATAL) << "Acked packet has zero sent time, largest_observed:" - << ack_frame.largest_observed; + QUIC_BUG << "Acked packet has zero sent time, largest_observed:" + << ack_frame.largest_observed; return false; } QuicTime::Delta send_delta = ack_receive_time.Subtract(transmission_info.sent_time); - rtt_stats_.UpdateRtt(send_delta, ack_frame.delta_time_largest_observed, - ack_receive_time); + rtt_stats_.UpdateRtt(send_delta, ack_frame.ack_delay_time, ack_receive_time); if (network_change_visitor_ != nullptr) { network_change_visitor_->OnRttChange(); @@ -961,6 +928,9 @@ QuicPacketCount QuicSentPacketManager::GetSlowStartThresholdInTcpMss() const { void QuicSentPacketManager::CancelRetransmissionsForStream( QuicStreamId stream_id) { unacked_packets_.CancelRetransmissionsForStream(stream_id); + if (delegate_ != nullptr) { + return; + } PendingRetransmissionMap::iterator it = pending_retransmissions_.begin(); while (it != pending_retransmissions_.end()) { if (HasRetransmittableFrames(it->first)) { @@ -987,18 +957,28 @@ void QuicSentPacketManager::EnablePacing() { } void QuicSentPacketManager::OnConnectionMigration(PeerAddressChangeType type) { - if (type == UNKNOWN) { - return; - } - - if (type == NAT_PORT_REBINDING || type == IPV4_SUBNET_REBINDING) { + if (type == PORT_CHANGE || type == IPV4_SUBNET_CHANGE) { // Rtt and cwnd do not need to be reset when the peer address change is // considered to be caused by NATs. return; } - + consecutive_rto_count_ = 0; + consecutive_tlp_count_ = 0; rtt_stats_.OnConnectionMigration(); send_algorithm_->OnConnectionMigration(); } +bool QuicSentPacketManager::InSlowStart() const { + return send_algorithm_->InSlowStart(); +} + +TransmissionInfo* QuicSentPacketManager::GetMutableTransmissionInfo( + QuicPacketNumber packet_number) { + return unacked_packets_.GetMutableTransmissionInfo(packet_number); +} + +void QuicSentPacketManager::RemoveObsoletePackets() { + unacked_packets_.RemoveObsoletePackets(); +} + } // namespace net diff --git a/src/net/quic/quic_sent_packet_manager.h b/src/net/quic/quic_sent_packet_manager.h index 24027a61..15b12d62 100644 --- a/src/net/quic/quic_sent_packet_manager.h +++ b/src/net/quic/quic_sent_packet_manager.h @@ -41,6 +41,31 @@ struct QuicConnectionStats; // previous transmission is acked, the data will not be retransmitted. class NET_EXPORT_PRIVATE QuicSentPacketManager { public: + // A delegate interface which manages pending retransmissions. + class MultipathDelegateInterface { + public: + virtual ~MultipathDelegateInterface() {} + + // Called when unencrypted |packet_number| is requested to be neutered. + virtual void OnUnencryptedPacketsNeutered( + QuicPathId path_id, + QuicPacketNumber packet_number) = 0; + // Called when |packet_number| is requested to be retransmitted. + virtual void OnRetransmissionMarked(QuicPathId path_id, + QuicPacketNumber packet_number, + TransmissionType transmission_type) = 0; + // Called when |packet_number| is marked as not retransmittable. + virtual void OnPacketMarkedNotRetransmittable( + QuicPathId path_id, + QuicPacketNumber packet_number, + QuicTime::Delta delta_largest_observed) = 0; + // Called when any transmission of |packet_number| is handled. + virtual void OnPacketMarkedHandled( + QuicPathId path_id, + QuicPacketNumber packet_number, + QuicTime::Delta delta_largest_observed) = 0; + }; + // 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. @@ -58,6 +83,10 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { QuicPacketNumber largest_observed, bool rtt_updated, QuicPacketNumber least_unacked_sent_packet) {} + + virtual void OnPacketLoss(QuicPacketNumber lost_packet_number, + TransmissionType transmission_type, + QuicTime detection_time) {} }; // Interface which gets callbacks from the QuicSentPacketManager when @@ -73,29 +102,12 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { // Called when RTT may have changed, including when an RTT is read from // the config. virtual void OnRttChange() = 0; - }; - // Struct to store the pending retransmission information. - struct PendingRetransmission { - PendingRetransmission(QuicPathId path_id, - QuicPacketNumber packet_number, - TransmissionType transmission_type, - const RetransmittableFrames& retransmittable_frames, - EncryptionLevel encryption_level, - QuicPacketNumberLength packet_number_length) - : path_id(path_id), - packet_number(packet_number), - transmission_type(transmission_type), - retransmittable_frames(retransmittable_frames), - encryption_level(encryption_level), - packet_number_length(packet_number_length) {} - - QuicPathId path_id; - QuicPacketNumber packet_number; - TransmissionType transmission_type; - const RetransmittableFrames& retransmittable_frames; - EncryptionLevel encryption_level; - QuicPacketNumberLength packet_number_length; + // Called with the path may be degrading. Note that the path may only be + // temporarily degrading. + // TODO(jri): With multipath, this method should probably have a path_id + // parameter, and should maybe result in the path being marked as inactive. + virtual void OnPathDegrading() = 0; }; QuicSentPacketManager(Perspective perspective, @@ -103,7 +115,8 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { const QuicClock* clock, QuicConnectionStats* stats, CongestionControlType congestion_control_type, - LossDetectionType loss_type); + LossDetectionType loss_type, + MultipathDelegateInterface* delegate); virtual ~QuicSentPacketManager(); virtual void SetFromConfig(const QuicConfig& config); @@ -166,7 +179,6 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { virtual bool OnPacketSent(SerializedPacket* serialized_packet, QuicPacketNumber original_packet_number, QuicTime sent_time, - QuicByteCount bytes, TransmissionType transmission_type, HasRetransmittableData has_retransmittable_data); @@ -249,6 +261,8 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { network_change_visitor_ = visitor; } + bool InSlowStart() const; + // Used in Chromium, but not in the server. size_t consecutive_rto_count() const { return consecutive_rto_count_; } @@ -322,17 +336,19 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { void MaybeInvokeCongestionEvent(bool rtt_updated, QuicByteCount bytes_in_flight); - // Marks |packet_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(QuicPacketNumber packet_number, - QuicTime::Delta delta_largest_observed); + // Called when frames of |packet_number| has been received but the packet + // itself has not been received by the peer (e.g., packet is revived by FEC). + // The packet needs no longer to be retransmitted, but the packet remains + // pending if it is and the congestion control does not consider the packet + // acked. + void MarkPacketNotRetransmittable(QuicPacketNumber packet_number, + QuicTime::Delta ack_delay_time); // Removes the retransmittability and in flight properties from the packet at // |info| due to receipt by the peer. void MarkPacketHandled(QuicPacketNumber packet_number, TransmissionInfo* info, - QuicTime::Delta delta_largest_observed); + QuicTime::Delta ack_delay_time); // Request that |packet_number| be retransmitted after the other pending // retransmissions. Does not add it to the retransmissions if it's already @@ -340,10 +356,25 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { void MarkForRetransmission(QuicPacketNumber packet_number, TransmissionType transmission_type); - // Notify observers about spurious retransmits. + // Notify observers that packet with TransmissionInfo |info| is a spurious + // retransmission. It is caller's responsibility to guarantee the packet with + // TransmissionInfo |info| is a spurious retransmission before calling this + // function. + void RecordOneSpuriousRetransmission(const TransmissionInfo& info); + + // Notify observers about spurious retransmits of packet with TransmissionInfo + // |info|. void RecordSpuriousRetransmissions(const TransmissionInfo& info, QuicPacketNumber acked_packet_number); + // Returns mutable TransmissionInfo associated with |packet_number|, which + // must be unacked. + TransmissionInfo* GetMutableTransmissionInfo(QuicPacketNumber packet_number); + + // Remove any packets no longer needed for retransmission, congestion, or + // RTT measurement purposes. + void RemoveObsoletePackets(); + // 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 @@ -364,6 +395,10 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { const QuicClock* clock_; QuicConnectionStats* stats_; + + // Pending retransmissions are managed by delegate_ if it is not null. + MultipathDelegateInterface* delegate_; // Not owned. + DebugDelegate* debug_delegate_; NetworkChangeVisitor* network_change_visitor_; const QuicPacketCount initial_congestion_window_; @@ -408,9 +443,6 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { // retransmittable frames. bool handshake_confirmed_; - // Latched value of FLAGS_gfe2_reloadable_flag_quic_general_loss_algorithm. - const bool use_general_loss_algorithm_; - // Records bandwidth from server to client in normal operation, over periods // of time with no loss events. QuicSustainedBandwidthRecorder sustained_bandwidth_recorder_; diff --git a/src/net/quic/quic_server_id.cc b/src/net/quic/quic_server_id.cc index 77fe3a3b..b810bb37 100644 --- a/src/net/quic/quic_server_id.cc +++ b/src/net/quic/quic_server_id.cc @@ -41,7 +41,6 @@ bool QuicServerId::operator==(const QuicServerId& other) const { host_port_pair_.Equals(other.host_port_pair_); } -#if 0 // static QuicServerId QuicServerId::FromString(const std::string& str) { GURL url(str); @@ -51,7 +50,6 @@ QuicServerId QuicServerId::FromString(const std::string& str) { ? PRIVACY_MODE_ENABLED : PRIVACY_MODE_DISABLED); } -#endif string QuicServerId::ToString() const { return "https://" + host_port_pair_.ToString() + diff --git a/src/net/quic/quic_server_id.h b/src/net/quic/quic_server_id.h index fe219cf4..97dbebbb 100644 --- a/src/net/quic/quic_server_id.h +++ b/src/net/quic/quic_server_id.h @@ -31,11 +31,9 @@ class NET_EXPORT_PRIVATE QuicServerId { bool operator<(const QuicServerId& other) const; bool operator==(const QuicServerId& other) const; -#if 0 // Creates a QuicServerId from a string formatted in same manner as // ToString(). static QuicServerId FromString(const std::string& str); -#endif // ToString() will convert the QuicServerId to "scheme:hostname:port" or // "scheme:hostname:port/private". "scheme" will be "https". diff --git a/src/net/quic/quic_session.cc b/src/net/quic/quic_session.cc index 814a3f39..98873584 100644 --- a/src/net/quic/quic_session.cc +++ b/src/net/quic/quic_session.cc @@ -5,8 +5,10 @@ #include "net/quic/quic_session.h" #include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "net/quic/crypto/proof_verifier.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_flow_controller.h" @@ -14,6 +16,7 @@ #include "net/ssl/ssl_info.h" #endif +using base::IntToString; using base::StringPiece; using base::hash_map; using base::hash_set; @@ -29,84 +32,11 @@ namespace net { #define ENDPOINT \ (perspective() == Perspective::IS_SERVER ? "Server: " : " Client: ") -// We want to make sure we delete any closed streams in a safe manner. -// To avoid deleting a stream in mid-operation, we have a simple shim between -// us and the stream, so we can delete any streams when we return from -// processing. -// -// We could just override the base methods, but this makes it easier to make -// sure we don't miss any. -class VisitorShim : public QuicConnectionVisitorInterface { - public: - explicit VisitorShim(QuicSession* session) : session_(session) {} - - void OnStreamFrame(const QuicStreamFrame& frame) override { - session_->OnStreamFrame(frame); - session_->PostProcessAfterData(); - } - void OnRstStream(const QuicRstStreamFrame& frame) override { - session_->OnRstStream(frame); - session_->PostProcessAfterData(); - } - - void OnGoAway(const QuicGoAwayFrame& frame) override { - session_->OnGoAway(frame); - session_->PostProcessAfterData(); - } - - void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override { - session_->OnWindowUpdateFrame(frame); - session_->PostProcessAfterData(); - } - - void OnBlockedFrame(const QuicBlockedFrame& frame) override { - session_->OnBlockedFrame(frame); - session_->PostProcessAfterData(); - } - - void OnCanWrite() override { - session_->OnCanWrite(); - session_->PostProcessAfterData(); - } - - void OnCongestionWindowChange(QuicTime now) override { - session_->OnCongestionWindowChange(now); - } - - void OnSuccessfulVersionNegotiation(const QuicVersion& version) override { - session_->OnSuccessfulVersionNegotiation(version); - } - - void OnConnectionClosed(QuicErrorCode error, bool from_peer) override { - session_->OnConnectionClosed(error, from_peer); - // The session will go away, so don't bother with cleanup. - } - - void OnWriteBlocked() override { session_->OnWriteBlocked(); } - - void OnConnectionMigration() override { session_->OnConnectionMigration(); } - - bool WillingAndAbleToWrite() const override { - return session_->WillingAndAbleToWrite(); - } - - bool HasPendingHandshake() const override { - return session_->HasPendingHandshake(); - } - - bool HasOpenDynamicStreams() const override { - return session_->HasOpenDynamicStreams(); - } - - private: - QuicSession* session_; -}; - QuicSession::QuicSession(QuicConnection* connection, const QuicConfig& config) : connection_(connection), - visitor_shim_(new VisitorShim(this)), config_(config), - max_open_streams_(config_.MaxStreamsPerConnection()), + max_open_outgoing_streams_(config_.MaxStreamsPerConnection()), + max_open_incoming_streams_(config_.MaxStreamsPerConnection()), next_outgoing_stream_id_(perspective() == Perspective::IS_SERVER ? 2 : 3), largest_peer_created_stream_id_( perspective() == Perspective::IS_SERVER ? 1 : 0), @@ -119,10 +49,11 @@ QuicSession::QuicSession(QuicConnection* connection, const QuicConfig& config) perspective(), kMinimumFlowControlSendWindow, config_.GetInitialSessionFlowControlWindowToSend(), - false) {} + false), + currently_writing_stream_id_(0) {} void QuicSession::Initialize() { - connection_->set_visitor(visitor_shim_.get()); + connection_->set_visitor(this); connection_->SetFromConfig(config_); DCHECK_EQ(kCryptoStreamId, GetCryptoStream()->id()); @@ -134,12 +65,12 @@ QuicSession::~QuicSession() { STLDeleteValues(&dynamic_stream_map_); DLOG_IF(WARNING, num_locally_closed_incoming_streams_highest_offset() > - max_open_streams_) + max_open_incoming_streams_) << "Surprisingly high number of locally closed peer initiated streams" "still waiting for final byte offset: " << num_locally_closed_incoming_streams_highest_offset(); - DLOG_IF(WARNING, - GetNumLocallyClosedOutgoingStreamsHighestOffset() > max_open_streams_) + DLOG_IF(WARNING, GetNumLocallyClosedOutgoingStreamsHighestOffset() > + max_open_outgoing_streams_) << "Surprisingly high number of locally closed self initiated streams" "still waiting for final byte offset: " << GetNumLocallyClosedOutgoingStreamsHighestOffset(); @@ -171,10 +102,7 @@ void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) { ReliableQuicStream* stream = GetOrCreateDynamicStream(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); + HandleRstOnValidNonexistentStream(frame); return; // Errors are handled by GetStream. } @@ -185,7 +113,8 @@ void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) { DCHECK(frame.last_good_stream_id < next_outgoing_stream_id_); } -void QuicSession::OnConnectionClosed(QuicErrorCode error, bool from_peer) { +void QuicSession::OnConnectionClosed(QuicErrorCode error, + ConnectionCloseSource source) { DCHECK(!connection_->connected()); if (error_ == QUIC_NO_ERROR) { error_ = error; @@ -194,11 +123,10 @@ void QuicSession::OnConnectionClosed(QuicErrorCode error, bool from_peer) { while (!dynamic_stream_map_.empty()) { StreamMap::iterator it = dynamic_stream_map_.begin(); QuicStreamId id = it->first; - it->second->OnConnectionClosed(error, from_peer); + it->second->OnConnectionClosed(error, source); // The stream should call CloseStream as part of OnConnectionClosed. if (dynamic_stream_map_.find(id) != dynamic_stream_map_.end()) { - LOG(DFATAL) << ENDPOINT - << "Stream failed to close under OnConnectionClosed"; + QUIC_BUG << ENDPOINT << "Stream failed to close under OnConnectionClosed"; CloseStream(id); } } @@ -207,6 +135,8 @@ void QuicSession::OnConnectionClosed(QuicErrorCode error, bool from_peer) { void QuicSession::OnSuccessfulVersionNegotiation( const QuicVersion& /*version*/) {} +void QuicSession::OnPathDegrading() {} + void QuicSession::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { // Stream may be closed by the time we receive a WINDOW_UPDATE, so we can't // assume that it still exists. @@ -262,20 +192,22 @@ void QuicSession::OnCanWrite() { 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); + QUIC_BUG << "WriteBlockedStream is missing"; + connection_->CloseConnection(QUIC_INTERNAL_ERROR, + ConnectionCloseSource::FROM_SELF); return; } if (!connection_->CanWriteStreamData()) { return; } - QuicStreamId stream_id = write_blocked_streams_.PopFront(); - ReliableQuicStream* stream = GetStream(stream_id); + currently_writing_stream_id_ = write_blocked_streams_.PopFront(); + ReliableQuicStream* stream = GetStream(currently_writing_stream_id_); if (stream != nullptr && !stream->flow_controller()->IsBlocked()) { // If the stream can't write all bytes it'll re-add itself to the blocked // list. stream->OnCanWrite(); } + currently_writing_stream_id_ = 0; } } @@ -298,6 +230,12 @@ bool QuicSession::HasOpenDynamicStreams() const { locally_closed_streams_highest_offset_.size()) > 0; } +void QuicSession::ProcessUdpPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicEncryptedPacket& packet) { + connection_->ProcessUdpPacket(self_address, peer_address, packet); +} + QuicConsumedData QuicSession::WritevData( QuicStreamId id, QuicIOVector iov, @@ -321,7 +259,7 @@ void QuicSession::SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error, QuicStreamOffset bytes_written) { if (ContainsKey(static_stream_map_, id)) { - LOG(DFATAL) << "Cannot send RST for a static stream with ID " << id; + QUIC_BUG << "Cannot send RST for a static stream with ID " << id; return; } @@ -344,6 +282,15 @@ void QuicSession::CloseStream(QuicStreamId stream_id) { CloseStreamInner(stream_id, false); } +void QuicSession::InsertLocallyClosedStreamsHighestOffset( + const QuicStreamId id, + QuicStreamOffset offset) { + locally_closed_streams_highest_offset_[id] = offset; + if (IsIncomingStream(id)) { + ++num_locally_closed_incoming_streams_highest_offset_; + } +} + void QuicSession::CloseStreamInner(QuicStreamId stream_id, bool locally_reset) { DVLOG(1) << ENDPOINT << "Closing stream " << stream_id; @@ -368,11 +315,8 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id, bool locally_reset) { // of the how many bytes the stream's flow controller believes it has // received, for accurate connection level flow control accounting. if (!stream->HasFinalReceivedByteOffset()) { - locally_closed_streams_highest_offset_[stream_id] = - stream->flow_controller()->highest_received_byte_offset(); - if (IsIncomingStream(stream_id)) { - ++num_locally_closed_incoming_streams_highest_offset_; - } + InsertLocallyClosedStreamsHighestOffset( + stream_id, stream->flow_controller()->highest_received_byte_offset()); } dynamic_stream_map_.erase(it); @@ -434,14 +378,11 @@ void QuicSession::OnConfigNegotiated() { uint32_t max_streams = config_.MaxStreamsPerConnection(); if (perspective() == Perspective::IS_SERVER) { - // A server should accept a small number of additional streams beyond the - // limit sent to the client. This helps avoid early connection termination - // when FIN/RSTs for old streams are lost or arrive out of order. - // Use a minimum number of additional streams, or a percentage increase, - // whichever is larger. - max_streams = - max(max_streams + kMaxStreamsMinimumIncrement, - static_cast(max_streams * kMaxStreamsMultiplier)); + if (!FLAGS_quic_different_max_num_open_streams) { + max_streams = + max(max_streams + kMaxStreamsMinimumIncrement, + static_cast(max_streams * kMaxStreamsMultiplier)); + } if (config_.HasReceivedConnectionOptions()) { if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kAFCW)) { @@ -460,7 +401,21 @@ void QuicSession::OnConfigNegotiated() { } } } - set_max_open_streams(max_streams); + + set_max_open_outgoing_streams(max_streams); + + uint32_t max_incoming_streams = max_streams; + if (FLAGS_quic_different_max_num_open_streams) { + // A small number of additional incoming streams beyond the limit should be + // allowed. This helps avoid early connection termination when FIN/RSTs for + // old streams are lost or arrive out of order. + // Use a minimum number of additional streams, or a percentage increase, + // whichever is larger. + max_incoming_streams = + max(max_streams + kMaxStreamsMinimumIncrement, + static_cast(max_streams * kMaxStreamsMultiplier)); + } + set_max_open_incoming_streams(max_incoming_streams); if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) { // Streams which were created before the SHLO was received (0-RTT @@ -513,6 +468,28 @@ void QuicSession::AdjustInitialFlowControlWindows(size_t stream_window) { } } +void QuicSession::HandleFrameOnNonexistentOutgoingStream( + QuicStreamId stream_id) { + DCHECK(!IsClosedStream(stream_id)); + // Received a frame for a locally-created stream that is not currently + // active. This is an error. + connection()->SendConnectionCloseWithDetails(QUIC_INVALID_STREAM_ID, + "Data for nonexistent stream"); +} + +void QuicSession::HandleRstOnValidNonexistentStream( + const QuicRstStreamFrame& frame) { + // If the stream is neither originally in active streams nor created in + // GetOrCreateDynamicStream(), it could be a closed stream in which case its + // final received byte offset need to be updated. + if (IsClosedStream(frame.stream_id)) { + // 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); + } +} + void QuicSession::OnNewStreamFlowControlWindow(QuicStreamOffset new_window) { if (new_window < kMinimumFlowControlSendWindow) { LOG(ERROR) << "Peer sent us an invalid stream flow control send window: " @@ -571,7 +548,7 @@ void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { break; case HANDSHAKE_CONFIRMED: - LOG_IF(DFATAL, !config_.negotiated()) + QUIC_BUG_IF(!config_.negotiated()) << ENDPOINT << "Handshake confirmed without parameter negotiation."; // Discard originally encrypted packets, since they can't be decrypted by // the peer. @@ -630,11 +607,45 @@ void QuicSession::StreamDraining(QuicStreamId stream_id) { } } -void QuicSession::CloseConnectionWithDetails(QuicErrorCode error, - const char* details) { - if (connection()->connected()) { - connection()->SendConnectionCloseWithDetails(error, details); +bool QuicSession::MaybeIncreaseLargestPeerStreamId( + const QuicStreamId stream_id) { + if (stream_id <= largest_peer_created_stream_id_) { + return true; + } + + // Check if the new number of available streams would cause the number of + // available streams to exceed the limit. Note that the peer can create + // only alternately-numbered streams. + size_t additional_available_streams = + (stream_id - largest_peer_created_stream_id_) / 2 - 1; + size_t new_num_available_streams = + GetNumAvailableStreams() + additional_available_streams; + if (new_num_available_streams > MaxAvailableStreams()) { + DVLOG(1) << "Failed to create a new incoming stream with id:" << stream_id + << ". There are already " << GetNumAvailableStreams() + << " streams available, which would become " + << new_num_available_streams << ", which exceeds the limit " + << MaxAvailableStreams() << "."; + string details = IntToString(new_num_available_streams) + " above " + + IntToString(MaxAvailableStreams()); + connection()->SendConnectionCloseWithDetails( + QUIC_TOO_MANY_AVAILABLE_STREAMS, details.c_str()); + return false; + } + for (QuicStreamId id = largest_peer_created_stream_id_ + 2; id < stream_id; + id += 2) { + available_streams_.insert(id); } + largest_peer_created_stream_id_ = stream_id; + + return true; +} + +bool QuicSession::ShouldYield(QuicStreamId stream_id) { + if (stream_id == currently_writing_stream_id_) { + return false; + } + return write_blocked_streams()->ShouldYield(stream_id); } ReliableQuicStream* QuicSession::GetOrCreateDynamicStream( @@ -655,61 +666,33 @@ ReliableQuicStream* QuicSession::GetOrCreateDynamicStream( } if (!IsIncomingStream(stream_id)) { - // Received a frame for a locally-created stream that is not currently - // active. This is an error. - CloseConnectionWithDetails(QUIC_INVALID_STREAM_ID, - "Data for nonexistent stream"); + HandleFrameOnNonexistentOutgoingStream(stream_id); return nullptr; } available_streams_.erase(stream_id); - if (stream_id > largest_peer_created_stream_id_) { - // Check if the new number of available streams would cause the number of - // available streams to exceed the limit. Note that the peer can create - // only alternately-numbered streams. - size_t additional_available_streams = - (stream_id - largest_peer_created_stream_id_) / 2 - 1; - size_t new_num_available_streams = - GetNumAvailableStreams() + additional_available_streams; - if (new_num_available_streams > get_max_available_streams()) { - DVLOG(1) << "Failed to create a new incoming stream with id:" << stream_id - << ". There are already " << GetNumAvailableStreams() - << " streams available, which would become " - << new_num_available_streams << ", which exceeds the limit " - << get_max_available_streams() << "."; - CloseConnectionWithDetails( - QUIC_TOO_MANY_AVAILABLE_STREAMS, - base::StringPrintf( - "%lu above %lu", - static_cast(new_num_available_streams), - static_cast(get_max_available_streams())) - .c_str()); - return nullptr; - } - for (QuicStreamId id = largest_peer_created_stream_id_ + 2; id < stream_id; - id += 2) { - available_streams_.insert(id); - } - largest_peer_created_stream_id_ = stream_id; + if (!MaybeIncreaseLargestPeerStreamId(stream_id)) { + return nullptr; } // Check if the new number of open streams would cause the number of // open streams to exceed the limit. - size_t num_current_open_streams = + size_t num_open_incoming_streams = FLAGS_quic_distinguish_incoming_outgoing_streams ? GetNumOpenIncomingStreams() : dynamic_stream_map_.size() - draining_streams_.size() + locally_closed_streams_highest_offset_.size(); - if (num_current_open_streams >= get_max_open_streams()) { + if (num_open_incoming_streams >= max_open_incoming_streams()) { if (connection()->version() <= QUIC_VERSION_27) { - CloseConnectionWithDetails(QUIC_TOO_MANY_OPEN_STREAMS, - "Old style stream rejection"); + connection()->SendConnectionCloseWithDetails( + QUIC_TOO_MANY_OPEN_STREAMS, "Old style stream rejection"); } else { // Refuse to open the stream. SendRstStream(stream_id, QUIC_REFUSED_STREAM, 0); } return nullptr; } + ReliableQuicStream* stream = CreateIncomingDynamicStream(stream_id); if (stream == nullptr) { return nullptr; @@ -718,11 +701,19 @@ ReliableQuicStream* QuicSession::GetOrCreateDynamicStream( return stream; } -void QuicSession::set_max_open_streams(size_t max_open_streams) { - DVLOG(1) << "Setting max_open_streams_ to " << max_open_streams; - DVLOG(1) << "Setting get_max_available_streams() to " - << get_max_available_streams(); - max_open_streams_ = max_open_streams; +void QuicSession::set_max_open_incoming_streams( + size_t max_open_incoming_streams) { + DVLOG(1) << "Setting max_open_incoming_streams_ to " + << max_open_incoming_streams; + max_open_incoming_streams_ = max_open_incoming_streams; + DVLOG(1) << "MaxAvailableStreams() became " << MaxAvailableStreams(); +} + +void QuicSession::set_max_open_outgoing_streams( + size_t max_open_outgoing_streams) { + DVLOG(1) << "Setting max_open_outgoing_streams_ to " + << max_open_outgoing_streams; + max_open_outgoing_streams_ = max_open_outgoing_streams; } bool QuicSession::goaway_sent() const { @@ -735,8 +726,7 @@ bool QuicSession::goaway_received() const { bool QuicSession::IsClosedStream(QuicStreamId id) { DCHECK_NE(0u, id); - if (ContainsKey(static_stream_map_, id) || - ContainsKey(dynamic_stream_map_, id)) { + if (IsOpenStream(id)) { // Stream is active return false; } @@ -750,6 +740,16 @@ bool QuicSession::IsClosedStream(QuicStreamId id) { !ContainsKey(available_streams_, id); } +bool QuicSession::IsOpenStream(QuicStreamId id) { + DCHECK_NE(0u, id); + if (ContainsKey(static_stream_map_, id) || + ContainsKey(dynamic_stream_map_, id)) { + // Stream is active + return true; + } + return false; +} + size_t QuicSession::GetNumOpenIncomingStreams() const { return num_dynamic_incoming_streams_ - num_draining_incoming_streams_ + num_locally_closed_incoming_streams_highest_offset_; @@ -768,21 +768,11 @@ size_t QuicSession::GetNumAvailableStreams() const { return available_streams_.size(); } -void QuicSession::MarkConnectionLevelWriteBlocked(QuicStreamId id, - SpdyPriority priority) { -#ifndef NDEBUG - ReliableQuicStream* stream = GetStream(id); - if (stream != nullptr) { - LOG_IF(DFATAL, priority != stream->Priority()) - << ENDPOINT << "Stream " << id - << "Priorities do not match. Got: " << static_cast(priority) - << " Expected: " << static_cast(stream->Priority()); - } else { - LOG(DFATAL) << "Marking unknown stream " << id << " blocked."; - } -#endif +void QuicSession::MarkConnectionLevelWriteBlocked(QuicStreamId id) { + QUIC_BUG_IF(GetStream(id) == nullptr) << "Marking unknown stream " << id + << " blocked."; - write_blocked_streams_.AddStream(id, priority); + write_blocked_streams_.AddStream(id); } bool QuicSession::HasDataToWrite() const { @@ -827,6 +817,10 @@ bool QuicSession::IsStreamFlowControlBlocked() { return false; } +size_t QuicSession::MaxAvailableStreams() const { + return max_open_incoming_streams_ * kMaxAvailableStreamsMultiplier; +} + bool QuicSession::IsIncomingStream(QuicStreamId id) const { return id % 2 != next_outgoing_stream_id_ % 2; } diff --git a/src/net/quic/quic_session.h b/src/net/quic/quic_session.h index 0ea60bb9..b4d71316 100644 --- a/src/net/quic/quic_session.h +++ b/src/net/quic/quic_session.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include "base/compiler_specific.h" @@ -31,7 +33,6 @@ namespace net { class QuicCryptoStream; class QuicFlowController; class ReliableQuicStream; -class VisitorShim; namespace test { class QuicSessionPeer; @@ -69,15 +70,25 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { void OnGoAway(const QuicGoAwayFrame& frame) override; void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override; void OnBlockedFrame(const QuicBlockedFrame& frame) override; - void OnConnectionClosed(QuicErrorCode error, bool from_peer) override; + void OnConnectionClosed(QuicErrorCode error, + ConnectionCloseSource source) override; void OnWriteBlocked() override {} void OnSuccessfulVersionNegotiation(const QuicVersion& version) override; void OnCanWrite() override; void OnCongestionWindowChange(QuicTime /*now*/) override {} void OnConnectionMigration() override {} + // Deletes streams that are safe to be deleted now that it's safe to do so (no + // other operations are being done on the streams at this time). + void PostProcessAfterData() override; bool WillingAndAbleToWrite() const override; bool HasPendingHandshake() const override; bool HasOpenDynamicStreams() const override; + void OnPathDegrading() override; + + // Called on every incoming packet. Passes |packet| through to |connection_|. + virtual void ProcessUdpPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicEncryptedPacket& packet); // 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 @@ -174,7 +185,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // connection-level flow control but not by its own stream-level flow control. // The stream will be given a chance to write when a connection-level // WINDOW_UPDATE arrives. - void MarkConnectionLevelWriteBlocked(QuicStreamId id, SpdyPriority priority); + void MarkConnectionLevelWriteBlocked(QuicStreamId id); // Returns true if the session has data to be sent, either queued in the // connection, or in a write-blocked stream. @@ -196,22 +207,26 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // Returns true if any stream is flow controller blocked. bool IsStreamFlowControlBlocked(); - size_t get_max_open_streams() const { return max_open_streams_; } + size_t max_open_incoming_streams() const { + return max_open_incoming_streams_; + } - size_t get_max_available_streams() const { - return max_open_streams_ * kMaxAvailableStreamsMultiplier; + size_t max_open_outgoing_streams() const { + return max_open_outgoing_streams_; } + size_t MaxAvailableStreams() const; + ReliableQuicStream* GetStream(const QuicStreamId stream_id); // Mark a stream as draining. - void StreamDraining(QuicStreamId id); + virtual void StreamDraining(QuicStreamId id); - // Close the connection, if it is not already closed. - void CloseConnectionWithDetails(QuicErrorCode error, const char* details); + // Returns true if this stream should yield writes to another blocked stream. + bool ShouldYield(QuicStreamId stream_id); protected: - typedef base::hash_map StreamMap; + typedef std::unordered_map StreamMap; // Creates a new stream, owned by the caller, to handle a peer-initiated // stream. Returns nullptr and does error handling if the stream can not be @@ -240,11 +255,20 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // exists, the connection is closed. ReliableQuicStream* GetOrCreateDynamicStream(QuicStreamId stream_id); - // This is called after every call other than OnConnectionClose from the - // QuicConnectionVisitor to allow post-processing once the work has been done. - // In this case, it deletes streams given that it's safe to do so (no other - // operations are being done on the streams at this time) - virtual void PostProcessAfterData(); + // Performs the work required to close |stream_id|. If |locally_reset| + // then the stream has been reset by this endpoint, not by the peer. + virtual void CloseStreamInner(QuicStreamId stream_id, bool locally_reset); + + // 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); + + // Return true if given stream is peer initiated. + bool IsIncomingStream(QuicStreamId id) const; StreamMap& static_streams() { return static_stream_map_; } const StreamMap& static_streams() const { return static_stream_map_; } @@ -256,7 +280,8 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { return &closed_streams_; } - void set_max_open_streams(size_t max_open_streams); + void set_max_open_incoming_streams(size_t max_open_incoming_streams); + void set_max_open_outgoing_streams(size_t max_open_outgoing_streams); void set_largest_peer_created_stream_id( QuicStreamId largest_peer_created_stream_id) { @@ -277,21 +302,32 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { size_t GetNumLocallyClosedOutgoingStreamsHighestOffset() const; - private: - friend class test::QuicSessionPeer; - friend class VisitorShim; + // Returns true if the stream is still active. + bool IsOpenStream(QuicStreamId id); - // Performs the work required to close |stream_id|. If |locally_reset| - // then the stream has been reset by this endpoint, not by the peer. - void CloseStreamInner(QuicStreamId stream_id, bool locally_reset); + QuicStreamId next_outgoing_stream_id() const { + return next_outgoing_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); + // Close connection when receive a frame for a locally-created nonexistant + // stream. + // Prerequisite: IsClosedStream(stream_id) == false + // Server session might need to override this method to allow server push + // stream to be promised before creating an active stream. + virtual void HandleFrameOnNonexistentOutgoingStream(QuicStreamId stream_id); + + bool MaybeIncreaseLargestPeerStreamId(const QuicStreamId stream_id); + + void InsertLocallyClosedStreamsHighestOffset(const QuicStreamId id, + QuicStreamOffset offset); + // If stream is a locally closed stream, this RST will update FIN offset. + // Otherwise stream is a preserved stream and the behavior of it depends on + // derived class's own implementation. + virtual void HandleRstOnValidNonexistentStream( + const QuicRstStreamFrame& frame); + + private: + friend class test::QuicSessionPeer; // Called in OnConfigNegotiated when we receive a new stream level flow // control window in a negotiated config. Closes the connection if invalid. @@ -309,9 +345,6 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // starting with smaller flow control receive windows and auto-tuning. void AdjustInitialFlowControlWindows(size_t stream_window); - // Return true if given stream is peer initiated. - bool IsIncomingStream(QuicStreamId id) const; - // Keep track of highest received byte offset of locally closed streams, while // waiting for a definitive final highest offset from the peer. std::map @@ -319,16 +352,15 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { scoped_ptr connection_; - // A shim to stand between the connection and the session, to handle stream - // deletions. - scoped_ptr visitor_shim_; - std::vector closed_streams_; QuicConfig config_; - // Returns the maximum number of streams this connection can open. - size_t max_open_streams_; + // The maximum number of outgoing streams this connection can open. + size_t max_open_outgoing_streams_; + + // The maximum number of incoming streams this connection will allow. + size_t max_open_incoming_streams_; // Static streams, such as crypto and header streams. Owned by child classes // that create these streams. @@ -342,12 +374,12 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // Set of stream ids that are less than the largest stream id that has been // received, but are nonetheless available to be created. - base::hash_set available_streams_; + std::unordered_set available_streams_; // Set of stream ids that are "draining" -- a FIN has been sent and received, // but the stream object still exists because not all the received data has // been consumed. - base::hash_set draining_streams_; + std::unordered_set draining_streams_; // A list of streams which need to write more data. QuicWriteBlockedList write_blocked_streams_; @@ -370,6 +402,10 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // Used for connection-level flow control. QuicFlowController flow_controller_; + // The stream id which was last popped in OnCanWrite, or 0, if not under the + // call stack of OnCanWrite. + QuicStreamId currently_writing_stream_id_; + DISALLOW_COPY_AND_ASSIGN(QuicSession); }; diff --git a/src/net/quic/quic_simple_buffer_allocator.cc b/src/net/quic/quic_simple_buffer_allocator.cc index 5dae280d..efee2ccc 100644 --- a/src/net/quic/quic_simple_buffer_allocator.cc +++ b/src/net/quic/quic_simple_buffer_allocator.cc @@ -10,6 +10,10 @@ char* SimpleBufferAllocator::New(size_t size) { return new char[size]; } +char* SimpleBufferAllocator::New(size_t size, bool /* flag_enable */) { + return New(size); +} + void SimpleBufferAllocator::Delete(char* buffer) { delete[] buffer; } diff --git a/src/net/quic/quic_simple_buffer_allocator.h b/src/net/quic/quic_simple_buffer_allocator.h index 171f6b0d..dfb0fffa 100644 --- a/src/net/quic/quic_simple_buffer_allocator.h +++ b/src/net/quic/quic_simple_buffer_allocator.h @@ -12,6 +12,7 @@ namespace net { class NET_EXPORT_PRIVATE SimpleBufferAllocator : public QuicBufferAllocator { public: char* New(size_t size) override; + char* New(size_t size, bool flag_enable) override; void Delete(char* buffer) override; }; diff --git a/src/net/quic/quic_socket_address_coder.cc b/src/net/quic/quic_socket_address_coder.cc index bd28353e..c0f528d5 100644 --- a/src/net/quic/quic_socket_address_coder.cc +++ b/src/net/quic/quic_socket_address_coder.cc @@ -4,6 +4,7 @@ #include "net/quic/quic_socket_address_coder.h" +#include "net/base/ip_address.h" #include "net/base/sys_addrinfo.h" using std::string; @@ -70,7 +71,7 @@ bool QuicSocketAddressCoder::Decode(const char* data, size_t length) { if (length < ip_length) { return false; } - IPAddressNumber ip(ip_length); + std::vector ip(ip_length); memcpy(&ip[0], data, ip_length); data += ip_length; length -= ip_length; @@ -81,7 +82,7 @@ bool QuicSocketAddressCoder::Decode(const char* data, size_t length) { } memcpy(&port, data, length); - address_ = IPEndPoint(ip, port); + address_ = IPEndPoint(IPAddress(ip), port); return true; } diff --git a/src/net/quic/quic_socket_address_coder.h b/src/net/quic/quic_socket_address_coder.h index 0b620ba9..e6f8bf6a 100644 --- a/src/net/quic/quic_socket_address_coder.h +++ b/src/net/quic/quic_socket_address_coder.h @@ -16,6 +16,8 @@ namespace net { +class IPAddress; + // 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. @@ -29,7 +31,7 @@ class NET_EXPORT_PRIVATE QuicSocketAddressCoder { bool Decode(const char* data, size_t length); - IPAddressNumber ip() const { return address_.address(); } + const IPAddress& ip() const { return address_.address(); } uint16_t port() const { return address_.port(); } diff --git a/src/net/quic/quic_spdy_session.cc b/src/net/quic/quic_spdy_session.cc index cdf27492..6038299a 100644 --- a/src/net/quic/quic_spdy_session.cc +++ b/src/net/quic/quic_spdy_session.cc @@ -4,6 +4,7 @@ #include "net/quic/quic_spdy_session.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_headers_stream.h" namespace net { @@ -93,4 +94,19 @@ QuicSpdyStream* QuicSpdySession::GetSpdyDataStream( return static_cast(GetOrCreateDynamicStream(stream_id)); } +void QuicSpdySession::OnPromiseHeaders(QuicStreamId stream_id, + StringPiece headers_data) { + QUIC_BUG << "OnPromiseHeaders should be overriden in client code."; + connection()->CloseConnection(QUIC_INTERNAL_ERROR, + ConnectionCloseSource::FROM_SELF); +} + +void QuicSpdySession::OnPromiseHeadersComplete(QuicStreamId stream_id, + QuicStreamId promised_stream_id, + size_t frame_len) { + QUIC_BUG << "OnPromiseHeadersComplete shoule be overriden in client code."; + connection()->CloseConnection(QUIC_INTERNAL_ERROR, + ConnectionCloseSource::FROM_SELF); +} + } // namespace net diff --git a/src/net/quic/quic_spdy_session.h b/src/net/quic/quic_spdy_session.h index d660af71..246a6def 100644 --- a/src/net/quic/quic_spdy_session.h +++ b/src/net/quic/quic_spdy_session.h @@ -42,6 +42,18 @@ class NET_EXPORT_PRIVATE QuicSpdySession : public QuicSession { bool fin, size_t frame_len); + // Called by |headers_stream_| when push promise headers have been + // received for a stream. + virtual void OnPromiseHeaders(QuicStreamId stream_id, + StringPiece headers_data); + + // Called by |headers_stream_| when push promise headers have been + // completely received. |fin| will be true if the fin flag was set + // in the headers. + virtual void OnPromiseHeadersComplete(QuicStreamId stream_id, + QuicStreamId promised_stream_id, + size_t frame_len); + // 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 diff --git a/src/net/quic/quic_spdy_stream.cc b/src/net/quic/quic_spdy_stream.cc index 4f967773..3c68c130 100644 --- a/src/net/quic/quic_spdy_stream.cc +++ b/src/net/quic/quic_spdy_stream.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_spdy_session.h" #include "net/quic/quic_utils.h" #include "net/quic/quic_write_blocked_list.h" @@ -82,7 +83,7 @@ size_t QuicSpdyStream::WriteTrailers( return 0; } if (fin_sent()) { - LOG(DFATAL) << "Trailers cannot be sent after a FIN."; + QUIC_BUG << "Trailers cannot be sent after a FIN."; return 0; } @@ -155,10 +156,6 @@ void QuicSpdyStream::SetPriority(SpdyPriority priority) { priority_ = priority; } -SpdyPriority QuicSpdyStream::Priority() const { - return priority(); -} - void QuicSpdyStream::OnStreamHeaders(StringPiece headers_data) { if (!FLAGS_quic_supports_trailers || !headers_decompressed_) { headers_data.AppendToString(&decompressed_headers_); @@ -191,20 +188,36 @@ void QuicSpdyStream::OnInitialHeadersComplete(bool fin, size_t /*frame_len*/) { } } +void QuicSpdyStream::OnPromiseHeaders(StringPiece headers_data) { + headers_data.AppendToString(&decompressed_headers_); +} + +void QuicSpdyStream::OnPromiseHeadersComplete( + QuicStreamId /* promised_stream_id */, + size_t /* frame_len */) { + // To be overridden in QuicSpdyClientStream. Not supported on + // server side. + session()->connection()->SendConnectionCloseWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, "Promise headers received by server"); + return; +} + void QuicSpdyStream::OnTrailingHeadersComplete(bool fin, size_t /*frame_len*/) { DCHECK(!trailers_decompressed_); if (fin_received()) { DLOG(ERROR) << "Received Trailers after FIN, on stream: " << id(); - session()->CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, - "Trailers after fin"); + session()->connection()->SendConnectionCloseWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers after fin"); return; } if (!fin) { DLOG(ERROR) << "Trailers must have FIN set, on stream: " << id(); - session()->CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, - "Fin missing from trailers"); + session()->connection()->SendConnectionCloseWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, "Fin missing from trailers"); return; } + + OnStreamFrame(QuicStreamFrame(id(), fin, stream_bytes_read(), StringPiece())); trailers_decompressed_ = true; } @@ -247,4 +260,7 @@ bool QuicSpdyStream::FinishedReadingTrailers() const { return no_more_trailers && decompressed_trailers_.empty(); } +SpdyPriority QuicSpdyStream::priority() const { + return priority_; +} } // namespace net diff --git a/src/net/quic/quic_spdy_stream.h b/src/net/quic/quic_spdy_stream.h index d49d650e..e7f640e0 100644 --- a/src/net/quic/quic_spdy_stream.h +++ b/src/net/quic/quic_spdy_stream.h @@ -69,10 +69,6 @@ class NET_EXPORT_PRIVATE QuicSpdyStream : public ReliableQuicStream { // ReliableQuicStream implementation void OnClose() override; - // This is the same as priority() and is being deprecated - // TODO(alyssar) remove after Priority refactor. - SpdyPriority Priority() const override; - // Called by the session when decompressed headers data is received // for this stream. // May be called multiple times, with each call providing additional headers @@ -88,6 +84,17 @@ class NET_EXPORT_PRIVATE QuicSpdyStream : public ReliableQuicStream { // should be closed; no more data will be sent by the peer. virtual void OnStreamHeadersComplete(bool fin, size_t frame_len); + // Called by the session when decompressed PUSH_PROMISE headers data + // is received for this stream. + // May be called multiple times, with each call providing additional headers + // data until OnPromiseHeadersComplete is called. + virtual void OnPromiseHeaders(StringPiece headers_data); + + // Called by the session when decompressed push promise headers have + // been completely delivered to this stream. + virtual void OnPromiseHeadersComplete(QuicStreamId promised_id, + size_t frame_len); + // Override the base class to not discard response when receiving // QUIC_STREAM_NO_ERROR on QUIC_VERSION_29 and later versions. void OnStreamReset(const QuicRstStreamFrame& frame) override; @@ -134,7 +141,7 @@ class NET_EXPORT_PRIVATE QuicSpdyStream : public ReliableQuicStream { return decompressed_trailers_; } - SpdyPriority priority() const { return priority_; } + virtual SpdyPriority priority() const; // Sets priority_ to priority. This should only be called before bytes are // written to the server. @@ -145,6 +152,8 @@ class NET_EXPORT_PRIVATE QuicSpdyStream : public ReliableQuicStream { // trailing) headers are expected next. virtual void OnInitialHeadersComplete(bool fin, size_t frame_len); virtual void OnTrailingHeadersComplete(bool fin, size_t frame_len); + QuicSpdySession* spdy_session() const { return spdy_session_; } + Visitor* visitor() { return visitor_; } // Returns true if headers have been fully read and consumed. bool FinishedReadingHeaders() const; diff --git a/src/net/quic/quic_stream_sequencer.cc b/src/net/quic/quic_stream_sequencer.cc index ec332e87..18f40a39 100644 --- a/src/net/quic/quic_stream_sequencer.cc +++ b/src/net/quic/quic_stream_sequencer.cc @@ -9,6 +9,7 @@ #include #include "base/logging.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_clock.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_frame_list.h" @@ -48,6 +49,8 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { const size_t data_len = frame.frame_length; if (data_len == 0 && !frame.fin) { // Stream frames must have data or a fin flag. + LOG(WARNING) << "QUIC_INVALID_STREAM_FRAM: Empty stream frame " + "without FIN set."; stream_->CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, "Empty stream frame without FIN set."); return; @@ -65,6 +68,8 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { clock_->ApproximateNow(), &bytes_written); if (result == QUIC_INVALID_STREAM_DATA) { + LOG(WARNING) << "QUIC_INVALID_STREAM_FRAME: Stream frame " + "overlaps with buffered data."; stream_->CloseConnectionWithDetails( QUIC_INVALID_STREAM_FRAME, "Stream frame overlaps with buffered data."); return; @@ -158,9 +163,9 @@ void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) { DCHECK(!blocked_); bool result = buffered_frames_->MarkConsumed(num_bytes_consumed); if (!result) { - LOG(DFATAL) << "Invalid argument to MarkConsumed." - << " expect to consume: " << num_bytes_consumed - << ", but not enough bytes available."; + QUIC_BUG << "Invalid argument to MarkConsumed." + << " expect to consume: " << num_bytes_consumed + << ", but not enough bytes available."; stream_->Reset(QUIC_ERROR_PROCESSING_STREAM); return; } diff --git a/src/net/quic/quic_unacked_packet_map.cc b/src/net/quic/quic_unacked_packet_map.cc index 74dde005..c8a32505 100644 --- a/src/net/quic/quic_unacked_packet_map.cc +++ b/src/net/quic/quic_unacked_packet_map.cc @@ -6,8 +6,10 @@ #include "base/logging.h" #include "base/stl_util.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_connection_stats.h" #include "net/quic/quic_flags.h" +#include "net/quic/quic_utils.h" #include "net/quic/quic_utils_chromium.h" using std::max; @@ -19,19 +21,13 @@ QuicUnackedPacketMap::QuicUnackedPacketMap() largest_observed_(0), least_unacked_(1), bytes_in_flight_(0), - pending_crypto_packet_count_(0), - track_single_retransmission_(FLAGS_quic_track_single_retransmission) {} + pending_crypto_packet_count_(0) {} QuicUnackedPacketMap::~QuicUnackedPacketMap() { QuicPacketNumber index = least_unacked_; for (UnackedPacketMap::iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it, ++index) { - delete it->retransmittable_frames; - // Only delete all_transmissions once, for the newest packet. - if (it->all_transmissions != nullptr && - index == *it->all_transmissions->rbegin()) { - delete it->all_transmissions; - } + QuicUtils::DeleteFrames(&it->retransmittable_frames); } } @@ -39,20 +35,22 @@ void QuicUnackedPacketMap::AddSentPacket(SerializedPacket* packet, QuicPacketNumber old_packet_number, TransmissionType transmission_type, QuicTime sent_time, - QuicByteCount bytes_sent, bool set_in_flight) { QuicPacketNumber packet_number = packet->packet_number; - LOG_IF(DFATAL, largest_sent_packet_ >= packet_number) << packet_number; + QuicPacketLength bytes_sent = packet->encrypted_length; + QUIC_BUG_IF(largest_sent_packet_ >= packet_number) << packet_number; DCHECK_GE(packet_number, least_unacked_ + unacked_packets_.size()); while (least_unacked_ + unacked_packets_.size() < packet_number) { unacked_packets_.push_back(TransmissionInfo()); unacked_packets_.back().is_unackable = true; } - TransmissionInfo info(packet->retransmittable_frames, - packet->encryption_level, packet->packet_number_length, + const bool has_crypto_handshake = + packet->has_crypto_handshake == IS_HANDSHAKE; + TransmissionInfo info(packet->encryption_level, packet->packet_number_length, transmission_type, sent_time, bytes_sent, - packet->is_fec_packet); + packet->is_fec_packet, has_crypto_handshake, + packet->needs_padding); if (old_packet_number > 0) { TransferRetransmissionInfo(old_packet_number, packet_number, transmission_type, &info); @@ -64,13 +62,15 @@ void QuicUnackedPacketMap::AddSentPacket(SerializedPacket* packet, info.in_flight = true; } unacked_packets_.push_back(info); - // Swap the ack listeners after to avoid an extra list allocation. + // Swap the ack listeners and retransmittable frames to avoid allocations. // TODO(ianswett): Could use emplace_back when Chromium can. if (old_packet_number == 0) { - if (packet->retransmittable_frames != nullptr && - packet->retransmittable_frames->HasCryptoHandshake() == IS_HANDSHAKE) { + if (has_crypto_handshake) { ++pending_crypto_packet_count_; } + + packet->retransmittable_frames.swap( + unacked_packets_.back().retransmittable_frames); unacked_packets_.back().ack_listeners.swap(packet->listeners); } } @@ -93,9 +93,9 @@ void QuicUnackedPacketMap::TransferRetransmissionInfo( TransmissionInfo* info) { if (old_packet_number < least_unacked_ || old_packet_number > largest_sent_packet_) { - LOG(DFATAL) << "Old TransmissionInfo no longer exists for:" - << old_packet_number << " least_unacked:" << least_unacked_ - << " largest_sent:" << largest_sent_packet_; + QUIC_BUG << "Old TransmissionInfo no longer exists for:" + << old_packet_number << " least_unacked:" << least_unacked_ + << " largest_sent:" << largest_sent_packet_; return; } DCHECK_GE(new_packet_number, least_unacked_ + unacked_packets_.size()); @@ -103,56 +103,30 @@ void QuicUnackedPacketMap::TransferRetransmissionInfo( TransmissionInfo* transmission_info = &unacked_packets_.at(old_packet_number - least_unacked_); - RetransmittableFrames* frames = transmission_info->retransmittable_frames; - transmission_info->retransmittable_frames = nullptr; + QuicFrames* frames = &transmission_info->retransmittable_frames; for (AckListenerWrapper& wrapper : transmission_info->ack_listeners) { wrapper.ack_listener->OnPacketRetransmitted(wrapper.length); } + + // Swap the frames and preserve needs_padding and has_crypto_handshake. + frames->swap(info->retransmittable_frames); + info->has_crypto_handshake = transmission_info->has_crypto_handshake; + transmission_info->has_crypto_handshake = false; + info->needs_padding = transmission_info->needs_padding; + // Transfer the AckListeners if any are present. info->ack_listeners.swap(transmission_info->ack_listeners); - LOG_IF(DFATAL, frames == nullptr) + QUIC_BUG_IF(frames == nullptr) << "Attempt to retransmit packet with no " << "retransmittable frames: " << old_packet_number; - // Only keep one transmission older than largest observed, because only the - // most recent is expected to possibly be a spurious retransmission. - if (!track_single_retransmission_) { - while (transmission_info->all_transmissions != nullptr && - transmission_info->all_transmissions->size() > 1 && - *(++transmission_info->all_transmissions->begin()) < - largest_observed_) { - QuicPacketNumber old_transmission = - *transmission_info->all_transmissions->begin(); - TransmissionInfo* old_info = - &unacked_packets_[old_transmission - least_unacked_]; - // Don't remove old packets if they're still in flight. - if (old_info->in_flight) { - break; - } - old_info->all_transmissions->pop_front(); - // This will cause the packet be removed in RemoveObsoletePackets. - old_info->all_transmissions = nullptr; - } - } // Don't link old transmissions to new ones when version or // encryption changes. if (transmission_type == ALL_INITIAL_RETRANSMISSION || transmission_type == ALL_UNACKED_RETRANSMISSION) { RemoveAckability(transmission_info); } else { - if (track_single_retransmission_) { - transmission_info->retransmission = new_packet_number; - } else { - if (transmission_info->all_transmissions == nullptr) { - transmission_info->all_transmissions = new PacketNumberList(); - transmission_info->all_transmissions->push_back(old_packet_number); - } - transmission_info->all_transmissions->push_back(new_packet_number); - } - } - info->retransmittable_frames = frames; - if (!track_single_retransmission_) { - info->all_transmissions = transmission_info->all_transmissions; + transmission_info->retransmission = new_packet_number; } // Proactively remove obsolete packets so the least unacked can be raised. RemoveObsoletePackets(); @@ -162,8 +136,8 @@ bool QuicUnackedPacketMap::HasRetransmittableFrames( QuicPacketNumber packet_number) const { DCHECK_GE(packet_number, least_unacked_); DCHECK_LT(packet_number, least_unacked_ + unacked_packets_.size()); - return unacked_packets_[packet_number - least_unacked_] - .retransmittable_frames != nullptr; + return !unacked_packets_[packet_number - least_unacked_] + .retransmittable_frames.empty(); } void QuicUnackedPacketMap::NackPacket(QuicPacketNumber packet_number, @@ -175,29 +149,12 @@ void QuicUnackedPacketMap::NackPacket(QuicPacketNumber packet_number, } void QuicUnackedPacketMap::RemoveRetransmittability(TransmissionInfo* info) { - if (track_single_retransmission_) { - while (info->retransmission != 0) { - const QuicPacketNumber retransmission = info->retransmission; - info->retransmission = 0; - info = &unacked_packets_[retransmission - least_unacked_]; - } - MaybeRemoveRetransmittableFrames(info); - return; - } - PacketNumberList* all_transmissions = info->all_transmissions; - if (all_transmissions == nullptr) { - MaybeRemoveRetransmittableFrames(info); - return; - } - // TODO(ianswett): Consider adding a check to ensure there are retransmittable - // frames associated with this packet. - for (QuicPacketNumber packet_number : *all_transmissions) { - TransmissionInfo* transmission_info = - &unacked_packets_[packet_number - least_unacked_]; - MaybeRemoveRetransmittableFrames(transmission_info); - transmission_info->all_transmissions = nullptr; + while (info->retransmission != 0) { + const QuicPacketNumber retransmission = info->retransmission; + info->retransmission = 0; + info = &unacked_packets_[retransmission - least_unacked_]; } - delete all_transmissions; + MaybeRemoveRetransmittableFrames(info); } void QuicUnackedPacketMap::RemoveRetransmittability( @@ -209,36 +166,20 @@ void QuicUnackedPacketMap::RemoveRetransmittability( } void QuicUnackedPacketMap::RemoveAckability(TransmissionInfo* info) { - DCHECK(info->retransmittable_frames == nullptr); + DCHECK(info->retransmittable_frames.empty()); + DCHECK_EQ(0u, info->retransmission); info->is_unackable = true; - if (track_single_retransmission_) { - DCHECK_EQ(0u, info->retransmission); - return; - } - PacketNumberList* all_transmissions = info->all_transmissions; - if (all_transmissions == nullptr) { - return; - } - for (QuicPacketNumber packet_number : *all_transmissions) { - TransmissionInfo* transmission_info = - &unacked_packets_[packet_number - least_unacked_]; - transmission_info->all_transmissions = nullptr; - transmission_info->is_unackable = true; - } - delete all_transmissions; } void QuicUnackedPacketMap::MaybeRemoveRetransmittableFrames( TransmissionInfo* transmission_info) { - if (transmission_info->retransmittable_frames == nullptr) { - return; - } - if (transmission_info->retransmittable_frames->HasCryptoHandshake() == - IS_HANDSHAKE) { + if (transmission_info->has_crypto_handshake) { + DCHECK(!transmission_info->retransmittable_frames.empty()); + DCHECK_LT(0u, pending_crypto_packet_count_); --pending_crypto_packet_count_; + transmission_info->has_crypto_handshake = false; } - delete transmission_info->retransmittable_frames; - transmission_info->retransmittable_frames = nullptr; + QuicUtils::DeleteFrames(&transmission_info->retransmittable_frames); } void QuicUnackedPacketMap::IncreaseLargestObserved( @@ -265,8 +206,7 @@ bool QuicUnackedPacketMap::IsPacketUsefulForRetransmittableData( const TransmissionInfo& info) const { // Packet may have retransmittable frames, or the data may have been // retransmitted with a new packet number. - return info.retransmittable_frames != nullptr || - info.all_transmissions != nullptr || + return !info.retransmittable_frames.empty() || // Allow for an extra 1 RTT before stopping to track old packets. info.retransmission > largest_observed_; } @@ -289,25 +229,25 @@ bool QuicUnackedPacketMap::IsUnacked(QuicPacketNumber packet_number) const { void QuicUnackedPacketMap::NotifyAndClearListeners( std::list* ack_listeners, - QuicTime::Delta delta_largest_observed) { + QuicTime::Delta ack_delay_time) { for (const AckListenerWrapper& wrapper : *ack_listeners) { - wrapper.ack_listener->OnPacketAcked(wrapper.length, delta_largest_observed); + wrapper.ack_listener->OnPacketAcked(wrapper.length, ack_delay_time); } ack_listeners->clear(); } void QuicUnackedPacketMap::NotifyAndClearListeners( QuicPacketNumber packet_number, - QuicTime::Delta delta_largest_observed) { + QuicTime::Delta ack_delay_time) { DCHECK_GE(packet_number, least_unacked_); DCHECK_LT(packet_number, least_unacked_ + unacked_packets_.size()); TransmissionInfo* info = &unacked_packets_[packet_number - least_unacked_]; - NotifyAndClearListeners(&info->ack_listeners, delta_largest_observed); + NotifyAndClearListeners(&info->ack_listeners, ack_delay_time); } void QuicUnackedPacketMap::RemoveFromInFlight(TransmissionInfo* info) { if (info->in_flight) { - LOG_IF(DFATAL, bytes_in_flight_ < info->bytes_sent); + QUIC_BUG_IF(bytes_in_flight_ < info->bytes_sent); bytes_in_flight_ -= info->bytes_sent; info->in_flight = false; } @@ -323,14 +263,14 @@ void QuicUnackedPacketMap::RemoveFromInFlight(QuicPacketNumber packet_number) { void QuicUnackedPacketMap::CancelRetransmissionsForStream( QuicStreamId stream_id) { QuicPacketNumber packet_number = least_unacked_; - for (UnackedPacketMap::const_iterator it = unacked_packets_.begin(); + for (UnackedPacketMap::iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it, ++packet_number) { - RetransmittableFrames* retransmittable_frames = it->retransmittable_frames; - if (retransmittable_frames == nullptr) { + QuicFrames* frames = &it->retransmittable_frames; + if (frames->empty()) { continue; } - retransmittable_frames->RemoveFramesForStream(stream_id); - if (retransmittable_frames->frames().empty()) { + QuicUtils::RemoveFramesForStream(frames, stream_id); + if (frames->empty()) { RemoveRetransmittability(packet_number); } } @@ -349,17 +289,22 @@ const TransmissionInfo& QuicUnackedPacketMap::GetTransmissionInfo( return unacked_packets_[packet_number - least_unacked_]; } +TransmissionInfo* QuicUnackedPacketMap::GetMutableTransmissionInfo( + QuicPacketNumber packet_number) { + return &unacked_packets_[packet_number - least_unacked_]; +} + QuicTime QuicUnackedPacketMap::GetLastPacketSentTime() const { UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin(); while (it != unacked_packets_.rend()) { if (it->in_flight) { - LOG_IF(DFATAL, it->sent_time == QuicTime::Zero()) + QUIC_BUG_IF(it->sent_time == QuicTime::Zero()) << "Sent time can never be zero for a packet in flight."; return it->sent_time; } ++it; } - LOG(DFATAL) << "GetLastPacketSentTime requires in flight packets."; + QUIC_BUG << "GetLastPacketSentTime requires in flight packets."; return QuicTime::Zero(); } @@ -399,7 +344,7 @@ bool QuicUnackedPacketMap::HasPendingCryptoPackets() const { bool QuicUnackedPacketMap::HasUnackedRetransmittableFrames() const { for (UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin(); it != unacked_packets_.rend(); ++it) { - if (it->in_flight && it->retransmittable_frames) { + if (it->in_flight && !it->retransmittable_frames.empty()) { return true; } } diff --git a/src/net/quic/quic_unacked_packet_map.h b/src/net/quic/quic_unacked_packet_map.h index 51cd03e6..8ed6d0a0 100644 --- a/src/net/quic/quic_unacked_packet_map.h +++ b/src/net/quic/quic_unacked_packet_map.h @@ -37,7 +37,6 @@ class NET_EXPORT_PRIVATE QuicUnackedPacketMap { QuicPacketNumber old_packet_number, TransmissionType transmission_type, QuicTime sent_time, - QuicByteCount bytes_sent, bool set_in_flight); // Returns true if the packet |packet_number| is unacked. @@ -85,10 +84,6 @@ class NET_EXPORT_PRIVATE QuicUnackedPacketMap { // Returns the largest packet number that has been acked. QuicPacketNumber largest_observed() const { return largest_observed_; } - bool track_single_retransmission() const { - return track_single_retransmission_; - } - // Returns the sum of bytes from all packets in flight. QuicByteCount bytes_in_flight() const { return bytes_in_flight_; } @@ -114,6 +109,10 @@ class NET_EXPORT_PRIVATE QuicUnackedPacketMap { const TransmissionInfo& GetTransmissionInfo( QuicPacketNumber packet_number) const; + // Returns mutable TransmissionInfo associated with |packet_number|, which + // must be unacked. + TransmissionInfo* GetMutableTransmissionInfo(QuicPacketNumber packet_number); + // Returns the time that the last unacked packet was sent. QuicTime GetLastPacketSentTime() const; @@ -192,9 +191,6 @@ class NET_EXPORT_PRIVATE QuicUnackedPacketMap { // Number of retransmittable crypto handshake packets. size_t pending_crypto_packet_count_; - // Latched copy of gfe2_reloadable_flag_quic_track_single_retransmission. - const bool track_single_retransmission_; - DISALLOW_COPY_AND_ASSIGN(QuicUnackedPacketMap); }; diff --git a/src/net/quic/quic_utils.cc b/src/net/quic/quic_utils.cc index 792aca47..c6a5cda7 100644 --- a/src/net/quic/quic_utils.cc +++ b/src/net/quic/quic_utils.cc @@ -15,6 +15,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" +#include "net/base/ip_address.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_write_blocked_list.h" @@ -55,6 +56,8 @@ uint128 IncrementalHashFast(uint128 uhash, const char* data, size_t len) { } #endif +#ifndef QUIC_UTIL_HAS_UINT128 +// Slow implementation of IncrementalHash. In practice, only used by Chromium. uint128 IncrementalHashSlow(uint128 hash, const char* data, size_t len) { // kPrime = 309485009821345068724781371 static const uint128 kPrime(16777216, 315); @@ -65,17 +68,21 @@ uint128 IncrementalHashSlow(uint128 hash, const char* data, size_t len) { } return hash; } +#endif uint128 IncrementalHash(uint128 hash, const char* data, size_t len) { #ifdef QUIC_UTIL_HAS_UINT128 - return FLAGS_quic_utils_use_fast_incremental_hash - ? IncrementalHashFast(hash, data, len) - : IncrementalHashSlow(hash, data, len); + return IncrementalHashFast(hash, data, len); #else return IncrementalHashSlow(hash, data, len); #endif } +bool IsInitializedIPEndPoint(const IPEndPoint& address) { + return net::GetAddressFamily(address.address().bytes()) != + net::ADDRESS_FAMILY_UNSPECIFIED; +} + } // namespace // static @@ -191,6 +198,11 @@ const char* QuicUtils::StreamErrorToString(QuicRstStreamErrorCode error) { RETURN_STRING_LITERAL(QUIC_RST_ACKNOWLEDGEMENT); RETURN_STRING_LITERAL(QUIC_REFUSED_STREAM); RETURN_STRING_LITERAL(QUIC_STREAM_LAST_ERROR); + RETURN_STRING_LITERAL(QUIC_INVALID_PROMISE_URL); + RETURN_STRING_LITERAL(QUIC_UNAUTHORIZED_PROMISE_URL); + RETURN_STRING_LITERAL(QUIC_DUPLICATE_PROMISE_URL); + RETURN_STRING_LITERAL(QUIC_PROMISE_VARY_MISMATCH); + RETURN_STRING_LITERAL(QUIC_INVALID_PROMISE_METHOD); } // Return a default value so that we return this when |error| doesn't match // any of the QuicRstStreamErrorCodes. This can happen when the RstStream @@ -216,6 +228,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { 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_PATH_CLOSE_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_VERSION_NEGOTIATION_PACKET); RETURN_STRING_LITERAL(QUIC_INVALID_PUBLIC_RST_PACKET); @@ -246,8 +259,8 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID); RETURN_STRING_LITERAL(QUIC_INVALID_NEGOTIATED_VALUE); RETURN_STRING_LITERAL(QUIC_DECOMPRESSION_FAILURE); - RETURN_STRING_LITERAL(QUIC_CONNECTION_TIMED_OUT); - RETURN_STRING_LITERAL(QUIC_CONNECTION_OVERALL_TIMED_OUT); + RETURN_STRING_LITERAL(QUIC_NETWORK_IDLE_TIMEOUT); + RETURN_STRING_LITERAL(QUIC_HANDSHAKE_TIMEOUT); RETURN_STRING_LITERAL(QUIC_ERROR_MIGRATING_ADDRESS); RETURN_STRING_LITERAL(QUIC_PACKET_WRITE_ERROR); RETURN_STRING_LITERAL(QUIC_PACKET_READ_ERROR); @@ -274,6 +287,12 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_TIMEOUTS_WITH_OPEN_STREAMS); RETURN_STRING_LITERAL(QUIC_FAILED_TO_SERIALIZE_PACKET); RETURN_STRING_LITERAL(QUIC_TOO_MANY_AVAILABLE_STREAMS); + RETURN_STRING_LITERAL(QUIC_UNENCRYPTED_FEC_DATA); + RETURN_STRING_LITERAL(QUIC_BAD_MULTIPATH_FLAG); + RETURN_STRING_LITERAL(QUIC_IP_ADDRESS_CHANGED); + RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS); + RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES); + RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK); RETURN_STRING_LITERAL(QUIC_LAST_ERROR); // Intentionally have no default case, so we'll break the build // if we add errors and don't put them here. @@ -386,4 +405,125 @@ string QuicUtils::StringToHexASCIIDump(StringPiece in_buffer) { return s; } +// static +void QuicUtils::DeleteFrames(QuicFrames* frames) { + for (QuicFrame& frame : *frames) { + switch (frame.type) { + // Frames smaller than a pointer are inlined, so don't need to be deleted. + case PADDING_FRAME: + case MTU_DISCOVERY_FRAME: + case PING_FRAME: + break; + case STREAM_FRAME: + delete frame.stream_frame; + break; + case ACK_FRAME: + delete frame.ack_frame; + break; + case STOP_WAITING_FRAME: + delete frame.stop_waiting_frame; + break; + case RST_STREAM_FRAME: + delete frame.rst_stream_frame; + break; + case CONNECTION_CLOSE_FRAME: + delete frame.connection_close_frame; + break; + case GOAWAY_FRAME: + delete frame.goaway_frame; + break; + case BLOCKED_FRAME: + delete frame.blocked_frame; + break; + case WINDOW_UPDATE_FRAME: + delete frame.window_update_frame; + break; + case PATH_CLOSE_FRAME: + delete frame.path_close_frame; + break; + case NUM_FRAME_TYPES: + DCHECK(false) << "Cannot delete type: " << frame.type; + } + } + frames->clear(); +} + +// static +void QuicUtils::RemoveFramesForStream(QuicFrames* frames, + QuicStreamId stream_id) { + QuicFrames::iterator it = frames->begin(); + while (it != frames->end()) { + if (it->type != STREAM_FRAME || it->stream_frame->stream_id != stream_id) { + ++it; + continue; + } + delete it->stream_frame; + it = frames->erase(it); + } +} + +// static +void QuicUtils::ClearSerializedPacket(SerializedPacket* serialized_packet) { + if (!serialized_packet->retransmittable_frames.empty()) { + DeleteFrames(&serialized_packet->retransmittable_frames); + } + serialized_packet->encrypted_buffer = nullptr; + serialized_packet->encrypted_length = 0; +} + +// static +uint64_t QuicUtils::PackPathIdAndPacketNumber(QuicPathId path_id, + QuicPacketNumber packet_number) { + // Setting the nonce below relies on QuicPathId and QuicPacketNumber being + // specific sizes. + static_assert(sizeof(path_id) == 1, "Size of QuicPathId changed."); + static_assert(sizeof(packet_number) == 8, + "Size of QuicPacketNumber changed."); + // Use path_id and lower 7 bytes of packet_number as lower 8 bytes of nonce. + uint64_t path_id_packet_number = + (static_cast(path_id) << 56) | packet_number; + DCHECK(path_id != kDefaultPathId || path_id_packet_number == packet_number); + return path_id_packet_number; +} + +// static +char* QuicUtils::CopyBuffer(const SerializedPacket& packet) { + char* dst_buffer = new char[packet.encrypted_length]; + memcpy(dst_buffer, packet.encrypted_buffer, packet.encrypted_length); + return dst_buffer; +} + +// static +PeerAddressChangeType QuicUtils::DetermineAddressChangeType( + const IPEndPoint& old_address, + const IPEndPoint& new_address) { + if (!IsInitializedIPEndPoint(old_address) || + !IsInitializedIPEndPoint(new_address) || old_address == new_address) { + return NO_CHANGE; + } + + if (old_address.address() == new_address.address()) { + return PORT_CHANGE; + } + + bool old_ip_is_ipv4 = old_address.address().IsIPv4(); + bool migrating_ip_is_ipv4 = new_address.address().IsIPv4(); + if (old_ip_is_ipv4 && !migrating_ip_is_ipv4) { + return IPV4_TO_IPV6_CHANGE; + } + + if (!old_ip_is_ipv4) { + return migrating_ip_is_ipv4 ? IPV6_TO_IPV4_CHANGE : IPV6_TO_IPV6_CHANGE; + } + + if (IPAddressMatchesPrefix(old_address.address(), new_address.address(), + 24)) { + // Subnet part does not change (here, we use /24), which is considered to be + // caused by NATs. + return IPV4_SUBNET_CHANGE; + } + + return UNSPECIFIED_CHANGE; +} + } // namespace net diff --git a/src/net/quic/quic_utils.h b/src/net/quic/quic_utils.h index 35b675c6..5af98c40 100644 --- a/src/net/quic/quic_utils.h +++ b/src/net/quic/quic_utils.h @@ -18,6 +18,16 @@ #include "net/base/net_export.h" #include "net/quic/quic_protocol.h" +#ifdef _MSC_VER +// MSVC 2013 and prior don't have alignof or aligned(); they have __alignof and +// a __declspec instead. +#define QUIC_ALIGN_OF __alignof +#define QUIC_ALIGNED(X) __declspec(align(X)) +#else +#define QUIC_ALIGN_OF alignof +#define QUIC_ALIGNED(X) __attribute__((aligned(X))) +#endif // _MSC_VER + namespace net { class NET_EXPORT_PRIVATE QuicUtils { @@ -95,6 +105,31 @@ class NET_EXPORT_PRIVATE QuicUtils { return reinterpret_cast(data); } + // Deletes all the sub-frames contained in |frames|. + static void DeleteFrames(QuicFrames* frames); + + // Deletes all the QuicStreamFrames for the specified |stream_id|. + static void RemoveFramesForStream(QuicFrames* frames, QuicStreamId stream_id); + + // Deletes and clears all the frames and the packet from serialized packet. + static void ClearSerializedPacket(SerializedPacket* serialized_packet); + + // Returns a packed representation of |path_id| and |packet_number| in which + // the highest byte is set to |path_id| and the lower 7 bytes are the lower + // 7 bytes of |packet_number|. + static uint64_t PackPathIdAndPacketNumber(QuicPathId path_id, + QuicPacketNumber packet_number); + + // Allocates a new char[] of size |packet.encrypted_length| and copies in + // |packet.encrypted_buffer|. + static char* CopyBuffer(const SerializedPacket& packet); + + // Determines and returns change type of address change from |old_address| to + // |new_address|. + static PeerAddressChangeType DetermineAddressChangeType( + const IPEndPoint& old_address, + const IPEndPoint& new_address); + private: DISALLOW_COPY_AND_ASSIGN(QuicUtils); }; diff --git a/src/net/quic/quic_write_blocked_list.h b/src/net/quic/quic_write_blocked_list.h index ad511877..d004452a 100644 --- a/src/net/quic/quic_write_blocked_list.h +++ b/src/net/quic/quic_write_blocked_list.h @@ -15,7 +15,6 @@ #include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" #include "net/spdy/priority_write_scheduler.h" -#include "net/spdy/write_blocked_list.h" namespace net { @@ -24,7 +23,6 @@ namespace net { // Crypto stream > Headers stream > Data streams by requested priority. class NET_EXPORT_PRIVATE QuicWriteBlockedList { private: - typedef WriteBlockedList QuicWriteBlockedListBase; typedef PriorityWriteScheduler QuicPriorityWriteScheduler; public: @@ -32,11 +30,7 @@ class NET_EXPORT_PRIVATE QuicWriteBlockedList { ~QuicWriteBlockedList(); bool HasWriteBlockedDataStreams() const { - if (use_new_blocked_list_) { - return priority_write_scheduler_.HasReadyStreams(); - } else { - return base_write_blocked_list_.HasWriteBlockedStreams(); - } + return priority_write_scheduler_.HasReadyStreams(); } bool HasWriteBlockedCryptoOrHeadersStream() const { @@ -44,9 +38,7 @@ class NET_EXPORT_PRIVATE QuicWriteBlockedList { } size_t NumBlockedStreams() const { - size_t num_blocked = use_new_blocked_list_ - ? priority_write_scheduler_.NumReadyStreams() - : base_write_blocked_list_.NumBlockedStreams(); + size_t num_blocked = priority_write_scheduler_.NumReadyStreams(); if (crypto_stream_blocked_) { ++num_blocked; } @@ -57,6 +49,23 @@ class NET_EXPORT_PRIVATE QuicWriteBlockedList { return num_blocked; } + bool ShouldYield(QuicStreamId id) const { + if (id == kCryptoStreamId) { + return false; // The crypto stream yields to none. + } + if (crypto_stream_blocked_) { + return true; // If the crypto stream is blocked, all other streams yield. + } + if (id == kHeadersStreamId) { + return false; // The crypto stream isn't blocked so headers won't yield. + } + if (headers_stream_blocked_) { + return true; // All data streams yield to the headers stream. + } + + return priority_write_scheduler_.ShouldYield(id); + } + // Pops the highest priorty stream, special casing crypto and headers streams. // Latches the most recently popped data stream for batch writing purposes. QuicStreamId PopFront() { @@ -70,23 +79,10 @@ class NET_EXPORT_PRIVATE QuicWriteBlockedList { return kHeadersStreamId; } - SpdyPriority priority; - QuicStreamId id; - - if (use_new_blocked_list_) { - id = priority_write_scheduler_.PopNextReadyStream(); - priority = priority_write_scheduler_.GetStreamPriority(id); - } else { - priority = base_write_blocked_list_.GetHighestPriorityWriteBlockedList(); - id = base_write_blocked_list_.PopFront(priority); - } + QuicStreamId id = priority_write_scheduler_.PopNextReadyStream(); + SpdyPriority priority = priority_write_scheduler_.GetStreamPriority(id); - size_t num_blocked_for_priority = - use_new_blocked_list_ - ? priority_write_scheduler_.NumReadyStreams(priority) - : base_write_blocked_list_.NumBlockedStreams(priority); - - if (num_blocked_for_priority == 0) { + if (!priority_write_scheduler_.HasReadyStreams()) { // If no streams are blocked, don't bother latching. This stream will be // the first popped for its priority anyway. batch_write_stream_id_[priority] = 0; @@ -102,21 +98,15 @@ class NET_EXPORT_PRIVATE QuicWriteBlockedList { } void RegisterStream(QuicStreamId stream_id, SpdyPriority priority) { - if (use_new_blocked_list_) { - priority_write_scheduler_.RegisterStream(stream_id, priority); - } + priority_write_scheduler_.RegisterStream(stream_id, priority); } void UnregisterStream(QuicStreamId stream_id) { - if (use_new_blocked_list_) { - priority_write_scheduler_.UnregisterStream(stream_id); - } + priority_write_scheduler_.UnregisterStream(stream_id); } void UpdateStreamPriority(QuicStreamId stream_id, SpdyPriority new_priority) { - if (use_new_blocked_list_) { - priority_write_scheduler_.UpdateStreamPriority(stream_id, new_priority); - } + priority_write_scheduler_.UpdateStreamPriority(stream_id, new_priority); } void UpdateBytesForStream(QuicStreamId stream_id, size_t bytes) { @@ -125,51 +115,31 @@ class NET_EXPORT_PRIVATE QuicWriteBlockedList { // bytes remaining in its batch write. bytes_left_for_batch_write_[last_priority_popped_] -= static_cast(bytes); - } else { - // If a batch write stream was set, it should only be preempted by the - // crypto or headers streams. Any higher priority data stream would - // *become* the new batch write stream. - if (FLAGS_quic_respect_send_alarm2 && FLAGS_quic_batch_writes) { - DCHECK(stream_id == kCryptoStreamId || stream_id == kHeadersStreamId || - batch_write_stream_id_[last_priority_popped_] == 0 || - bytes == 0); - } } } - // Pushes a stream to the back of the list for this priority level - // *unless* it is latched for doing batched writes in which case it goes to - // the front of the list for this priority level. + // Pushes a stream to the back of the list for its priority level *unless* + // it is latched for doing batched writes in which case it goes to the front + // of the list for its priority level. // Headers and crypto streams are special cased to always resume first. - void AddStream(QuicStreamId stream_id, SpdyPriority priority) { + void AddStream(QuicStreamId stream_id) { if (stream_id == kCryptoStreamId) { - DCHECK_EQ(kV3HighestPriority, priority); // TODO(avd) Add DCHECK(!crypto_stream_blocked_) crypto_stream_blocked_ = true; return; } if (stream_id == kHeadersStreamId) { - DCHECK_EQ(kV3HighestPriority, priority); // TODO(avd) Add DCHECK(!headers_stream_blocked_); headers_stream_blocked_ = true; return; } - if (use_new_blocked_list_) { - bool push_front = - FLAGS_quic_batch_writes && - stream_id == batch_write_stream_id_[last_priority_popped_] && - bytes_left_for_batch_write_[last_priority_popped_] > 0; - priority_write_scheduler_.MarkStreamReady(stream_id, push_front); - } else if (FLAGS_quic_batch_writes && - stream_id == batch_write_stream_id_[last_priority_popped_] && - bytes_left_for_batch_write_[last_priority_popped_] > 0) { - // If the batch write stream has more data to write, push it to the front - // for its priority level. - base_write_blocked_list_.PushFront(stream_id, priority); - } else { - base_write_blocked_list_.PushBack(stream_id, priority); - } + bool push_front = + FLAGS_quic_batch_writes && + stream_id == batch_write_stream_id_[last_priority_popped_] && + bytes_left_for_batch_write_[last_priority_popped_] > 0; + priority_write_scheduler_.MarkStreamReady(stream_id, push_front); + return; } @@ -177,9 +147,7 @@ class NET_EXPORT_PRIVATE QuicWriteBlockedList { bool headers_stream_blocked() const { return headers_stream_blocked_; } private: - QuicWriteBlockedListBase base_write_blocked_list_; QuicPriorityWriteScheduler priority_write_scheduler_; - bool use_new_blocked_list_ = FLAGS_quic_new_blocked_list; // If performing batch writes, this will be the stream ID of the stream doing // batch writes for this priority level. We will allow this stream to write diff --git a/src/net/quic/reliable_quic_stream.cc b/src/net/quic/reliable_quic_stream.cc index f42687ad..eea769d3 100644 --- a/src/net/quic/reliable_quic_stream.cc +++ b/src/net/quic/reliable_quic_stream.cc @@ -6,7 +6,7 @@ #include "base/logging.h" #include "net/quic/iovector.h" -#include "net/quic/quic_ack_listener_interface.h" +#include "net/quic/quic_bug_tracker.h" #include "net/quic/quic_flags.h" #include "net/quic/quic_flow_controller.h" #include "net/quic/quic_session.h" @@ -90,6 +90,8 @@ void ReliableQuicStream::SetFromConfig() { void ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) { DCHECK_EQ(frame.stream_id, id_); + DCHECK(!(read_side_closed_ && write_side_closed_)); + if (frame.fin) { fin_received_ = true; if (fin_sent_) { @@ -145,7 +147,7 @@ void ReliableQuicStream::OnStreamReset(const QuicRstStreamFrame& frame) { } void ReliableQuicStream::OnConnectionClosed(QuicErrorCode error, - bool /*from_peer*/) { + ConnectionCloseSource /*source*/) { if (read_side_closed_ && write_side_closed_) { return; } @@ -176,10 +178,6 @@ void ReliableQuicStream::Reset(QuicRstStreamErrorCode error) { rst_sent_ = true; } -void ReliableQuicStream::CloseConnection(QuicErrorCode error) { - session()->connection()->SendConnectionClose(error); -} - void ReliableQuicStream::CloseConnectionWithDetails(QuicErrorCode error, const string& details) { session()->connection()->SendConnectionCloseWithDetails(error, details); @@ -190,12 +188,12 @@ void ReliableQuicStream::WriteOrBufferData( bool fin, QuicAckListenerInterface* ack_listener) { if (data.empty() && !fin) { - LOG(DFATAL) << "data.empty() && !fin"; + QUIC_BUG << "data.empty() && !fin"; return; } if (fin_buffered_) { - LOG(DFATAL) << "Fin already buffered"; + QUIC_BUG << "Fin already buffered"; return; } if (write_side_closed_) { @@ -217,7 +215,7 @@ void ReliableQuicStream::WriteOrBufferData( (fin && !consumed_data.fin_consumed)) { StringPiece remainder(data.substr(consumed_data.bytes_consumed)); queued_data_bytes_ += remainder.size(); - queued_data_.push_back(PendingData(remainder.as_string(), ack_listener)); + queued_data_.emplace_back(remainder.as_string(), ack_listener); } } @@ -233,9 +231,8 @@ void ReliableQuicStream::OnCanWrite() { pending_data->offset >= pending_data->data.size()) { // This should be impossible because offset tracks the amount of // pending_data written thus far. - LOG(DFATAL) << "Pending offset is beyond available data. offset: " - << pending_data->offset - << " vs: " << pending_data->data.size(); + QUIC_BUG << "Pending offset is beyond available data. offset: " + << pending_data->offset << " vs: " << pending_data->data.size(); return; } size_t remaining_len = pending_data->data.size() - pending_data->offset; @@ -268,7 +265,7 @@ void ReliableQuicStream::MaybeSendBlocked() { // WINDOW_UPDATE arrives. if (connection_flow_controller_->IsBlocked() && !flow_controller_.IsBlocked()) { - session_->MarkConnectionLevelWriteBlocked(id(), Priority()); + session_->MarkConnectionLevelWriteBlocked(id()); } } @@ -295,6 +292,11 @@ QuicConsumedData ReliableQuicStream::WritevData( min(send_window, connection_flow_controller_->SendWindowSize()); } + if (FLAGS_quic_cede_correctly && session_->ShouldYield(id())) { + session_->MarkConnectionLevelWriteBlocked(id()); + return QuicConsumedData(0, false); + } + if (send_window == 0 && !fin_with_zero_data) { // Quick return if nothing can be sent. MaybeSendBlocked(); @@ -333,10 +335,10 @@ QuicConsumedData ReliableQuicStream::WritevData( } CloseWriteSide(); } else if (fin && !consumed_data.fin_consumed) { - session_->MarkConnectionLevelWriteBlocked(id(), Priority()); + session_->MarkConnectionLevelWriteBlocked(id()); } } else { - session_->MarkConnectionLevelWriteBlocked(id(), Priority()); + session_->MarkConnectionLevelWriteBlocked(id()); } return consumed_data; } diff --git a/src/net/quic/reliable_quic_stream.h b/src/net/quic/reliable_quic_stream.h index dfb86b31..8e660b2e 100644 --- a/src/net/quic/reliable_quic_stream.h +++ b/src/net/quic/reliable_quic_stream.h @@ -33,8 +33,6 @@ #include "net/quic/quic_protocol.h" #include "net/quic/quic_stream_sequencer.h" #include "net/quic/quic_types.h" -//#include "std::strings/std::stringpiece.h" -//#include "util/refcount/reffed_ptr.h" // TODO(alyssar) remove this after cleaning Priority logic from this class. #include "net/quic/quic_write_blocked_list.h" @@ -75,7 +73,8 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { // Called by the session when the endpoint receives or sends a connection // close, and should immediately close the stream. - virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer); + virtual void OnConnectionClosed(QuicErrorCode error, + ConnectionCloseSource source); // Called by the stream subclass after it has consumed the final incoming // data. @@ -92,13 +91,9 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { // Called by the subclass or the sequencer to close the entire connection from // this end. - virtual void CloseConnection(QuicErrorCode error); virtual void CloseConnectionWithDetails(QuicErrorCode error, const std::string& details); - // Returns the priority for the stream. - virtual SpdyPriority Priority() const = 0; - QuicStreamId id() const { return id_; } QuicRstStreamErrorCode stream_error() const { return stream_error_; } diff --git a/src/net/quic/spdy_utils.cc b/src/net/quic/spdy_utils.cc new file mode 100644 index 00000000..b5aeb1a0 --- /dev/null +++ b/src/net/quic/spdy_utils.cc @@ -0,0 +1,141 @@ +// 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/spdy_utils.h" + +#include + +#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 "net/spdy/spdy_frame_builder.h" +#include "net/spdy/spdy_framer.h" +#include "net/spdy/spdy_protocol.h" +#include "url/gurl.h" + +using std::string; +using std::vector; + +namespace net { + +// static +string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) { + SpdyMajorVersion spdy_version = HTTP2; + + size_t length = SpdyFramer::GetSerializedLength(spdy_version, &headers); + SpdyFrameBuilder builder(length, spdy_version); + SpdyFramer::WriteHeaderBlock(&builder, spdy_version, &headers); + scoped_ptr block(builder.take()); + return string(block->data(), length); +} + +// static +bool SpdyUtils::ParseHeaders(const char* data, + uint32_t data_len, + int* content_length, + SpdyHeaderBlock* headers) { + SpdyFramer framer(HTTP2); + if (!framer.ParseHeaderBlockInBuffer(data, data_len, headers) || + headers->empty()) { + return false; // Headers were invalid. + } + + if (ContainsKey(*headers, "content-length")) { + // Check whether multiple values are consistent. + base::StringPiece content_length_header = (*headers)["content-length"]; + vector values = + base::SplitString(content_length_header, base::StringPiece("\0", 1), + base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + for (const string& value : values) { + int new_value; + if (!base::StringToInt(value, &new_value) || new_value < 0) { + return false; + } + if (*content_length < 0) { + *content_length = new_value; + continue; + } + if (new_value != *content_length) { + return false; + } + } + } + + return true; +} + +// static +bool SpdyUtils::ParseTrailers(const char* data, + uint32_t data_len, + size_t* final_byte_offset, + SpdyHeaderBlock* trailers) { + SpdyFramer framer(HTTP2); + if (!framer.ParseHeaderBlockInBuffer(data, data_len, trailers) || + trailers->empty()) { + DVLOG(1) << "Request Trailers are invalid."; + return false; // Trailers were invalid. + } + + // Pull out the final offset pseudo header which indicates the number of + // response body bytes expected. + auto it = trailers->find(kFinalOffsetHeaderKey); + if (it == trailers->end() || + !base::StringToSizeT(it->second, final_byte_offset)) { + DVLOG(1) << "Required key '" << kFinalOffsetHeaderKey << "' not present"; + return false; + } + // The final offset header is no longer needed. + trailers->erase(it->first); + + // Trailers must not have empty keys, and must not contain pseudo headers. + for (const auto& trailer : *trailers) { + base::StringPiece key = trailer.first; + base::StringPiece value = trailer.second; + if (key.starts_with(":")) { + DVLOG(1) << "Trailers must not contain pseudo-header: '" << key << "','" + << value << "'."; + return false; + } + + // TODO(rjshade): Check for other forbidden keys, following the HTTP/2 spec. + } + + DVLOG(1) << "Successfully parsed Trailers."; + return true; +} + +// static +string SpdyUtils::GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers) { + SpdyHeaderBlock::const_iterator it = headers.find(":scheme"); + if (it == headers.end()) + return ""; + std::string url = it->second.as_string(); + + url.append("://"); + + it = headers.find(":authority"); + if (it == headers.end()) + return ""; + url.append(it->second.as_string()); + + it = headers.find(":path"); + if (it == headers.end()) + return ""; + url.append(it->second.as_string()); + return url; +} + +// static +string SpdyUtils::GetHostNameFromHeaderBlock(const SpdyHeaderBlock& headers) { + return GURL(GetUrlFromHeaderBlock(headers)).host(); +} + +// static +bool SpdyUtils::UrlIsValid(const SpdyHeaderBlock& headers) { + string url(GetUrlFromHeaderBlock(headers)); + return url != "" && GURL(url).is_valid(); +} + +} // namespace net diff --git a/src/net/quic/spdy_utils.h b/src/net/quic/spdy_utils.h new file mode 100644 index 00000000..cef13683 --- /dev/null +++ b/src/net/quic/spdy_utils.h @@ -0,0 +1,63 @@ +// 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_SPDY_UTILS_H_ +#define NET_QUIC_SPDY_UTILS_H_ + +#include +#include + +#include +#include + +#include "base/macros.h" +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" +#include "net/spdy/spdy_framer.h" + +namespace net { + +class NET_EXPORT_PRIVATE SpdyUtils { + public: + static std::string SerializeUncompressedHeaders( + const SpdyHeaderBlock& headers); + + // Parses |data| as a std::string containing serialized HTTP/2 HEADERS frame, + // populating |headers| with the key->value std:pairs found. + // |content_length| will be populated with the value of the content-length + // header if one or more are present. + // Returns true on success, false if parsing fails, or invalid keys are found. + static bool ParseHeaders(const char* data, + uint32_t data_len, + int* content_length, + SpdyHeaderBlock* headers); + + // Parses |data| as a std::string containing serialized HTTP/2 HEADERS frame, + // populating |trailers| with the key->value std:pairs found. + // The final offset header will be excluded from |trailers|, and instead the + // value will be copied to |final_byte_offset|. + // Returns true on success, false if parsing fails, or invalid keys are found. + static bool ParseTrailers(const char* data, + uint32_t data_len, + size_t* final_byte_offset, + SpdyHeaderBlock* trailers); + + // Returns URL composed from scheme, authority, and path header + // values, or empty string if any of those fields are missing. + static std::string GetUrlFromHeaderBlock(const net::SpdyHeaderBlock& headers); + + // Returns hostname, or empty std::string if missing. + static std::string GetHostNameFromHeaderBlock(const SpdyHeaderBlock& headers); + + // Returns true if result of |GetUrlFromHeaderBlock()| is non-empty + // and is a well-formed URL. + static bool UrlIsValid(const net::SpdyHeaderBlock& headers); + + private: + DISALLOW_COPY_AND_ASSIGN(SpdyUtils); +}; + +} // namespace net + +#endif // NET_QUIC_SPDY_UTILS_H_ diff --git a/src/net/quic/stream_sequencer_buffer.cc b/src/net/quic/stream_sequencer_buffer.cc index a54f13cf..5b98452d 100644 --- a/src/net/quic/stream_sequencer_buffer.cc +++ b/src/net/quic/stream_sequencer_buffer.cc @@ -5,6 +5,7 @@ #include "net/quic/stream_sequencer_buffer.h" #include "base/logging.h" +#include "net/quic/quic_bug_tracker.h" using std::min; @@ -64,7 +65,7 @@ QuicErrorCode StreamSequencerBuffer::OnStreamData( QuicStreamOffset offset = starting_offset; size_t size = data.size(); if (size == 0) { - LOG(DFATAL) << "Attempted to write 0 bytes of data."; + QUIC_BUG << "Attempted to write 0 bytes of data."; return QUIC_INVALID_STREAM_FRAME; } diff --git a/src/net/spdy/hpack/hpack_constants.h b/src/net/spdy/hpack/hpack_constants.h index bc9b39be..a247e405 100644 --- a/src/net/spdy/hpack/hpack_constants.h +++ b/src/net/spdy/hpack/hpack_constants.h @@ -48,12 +48,12 @@ const uint32_t kDefaultHeaderTableSizeSetting = 4096; // Largest string literal an HpackDecoder/HpackEncoder will attempt to process // before returning an error. -const uint32_t kDefaultMaxStringLiteralSize = 16 * 1024; +const uint32_t kDefaultMaxStringLiteralSize = 256 * 1024; // Maximum amount of encoded header buffer HpackDecoder will retain before // returning an error. // TODO(jgraettinger): Remove with SpdyHeadersHandlerInterface switch. -const uint32_t kMaxDecodeBufferSize = 32 * 1024; +const uint32_t kMaxDecodeBufferSize = 256 * 1024; // 6.2: Flag for a string literal that is stored unmodified (i.e., // without Huffman encoding). diff --git a/src/net/spdy/hpack/hpack_decoder.cc b/src/net/spdy/hpack/hpack_decoder.cc index ab5c7838..6d4ab989 100644 --- a/src/net/spdy/hpack/hpack_decoder.cc +++ b/src/net/spdy/hpack/hpack_decoder.cc @@ -21,10 +21,10 @@ const char kCookieKey[] = "cookie"; } // namespace -HpackDecoder::HpackDecoder(const HpackHuffmanTable& table) +HpackDecoder::HpackDecoder() : max_string_literal_size_(kDefaultMaxStringLiteralSize), - huffman_table_(table), handler_(nullptr), + total_header_bytes_(0), regular_header_seen_(false), header_block_started_(false) {} @@ -62,7 +62,7 @@ bool HpackDecoder::HandleControlFrameHeadersComplete(size_t* compressed_len) { } } if (handler_ != nullptr) { - handler_->OnHeaderBlockEnd(headers_block_buffer_.size()); + handler_->OnHeaderBlockEnd(total_header_bytes_); } headers_block_buffer_.clear(); header_block_started_ = false; @@ -72,6 +72,8 @@ bool HpackDecoder::HandleControlFrameHeadersComplete(size_t* compressed_len) { bool HpackDecoder::HandleHeaderRepresentation(StringPiece name, StringPiece value) { + total_header_bytes_ += name.size() + value.size(); + // Fail if pseudo-header follows regular header. if (name.size() > 0) { if (name[0] == kPseudoHeaderPrefix) { @@ -209,7 +211,7 @@ bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream, StringPiece* output) { if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) { string* buffer = is_key ? &key_buffer_ : &value_buffer_; - bool result = input_stream->DecodeNextHuffmanString(huffman_table_, buffer); + bool result = input_stream->DecodeNextHuffmanString(buffer); *output = StringPiece(*buffer); return result; } diff --git a/src/net/spdy/hpack/hpack_decoder.h b/src/net/spdy/hpack/hpack_decoder.h index 85c7f2b5..b430f7c5 100644 --- a/src/net/spdy/hpack/hpack_decoder.h +++ b/src/net/spdy/hpack/hpack_decoder.h @@ -25,8 +25,6 @@ namespace net { -class HpackHuffmanTable; - namespace test { class HpackDecoderPeer; } // namespace test @@ -35,9 +33,7 @@ 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(); ~HpackDecoder(); // Called upon acknowledgement of SETTINGS_HEADER_TABLE_SIZE. @@ -49,6 +45,7 @@ class NET_EXPORT_PRIVATE HpackDecoder { // headers to it rather than accumulating them in a SpdyHeaderBlock. void HandleControlFrameHeadersStart(SpdyHeadersHandlerInterface* handler) { handler_ = handler; + total_header_bytes_ = 0; } // Called as headers data arrives. Returns false if an error occurred. @@ -102,13 +99,12 @@ class NET_EXPORT_PRIVATE HpackDecoder { std::string headers_block_buffer_; SpdyHeaderBlock decoded_block_; - // Huffman table to be applied to decoded Huffman literals, - // and scratch space for storing those decoded literals. - const HpackHuffmanTable& huffman_table_; + // Scratch space for storing decoded literals. std::string key_buffer_, value_buffer_; // If non-NULL, handles decoded headers. SpdyHeadersHandlerInterface* handler_; + size_t total_header_bytes_; // Flag to keep track of having seen a regular header field. bool regular_header_seen_; diff --git a/src/net/spdy/hpack/hpack_header_table.cc b/src/net/spdy/hpack/hpack_header_table.cc index bf5d312e..01e96c09 100644 --- a/src/net/spdy/hpack/hpack_header_table.cc +++ b/src/net/spdy/hpack/hpack_header_table.cc @@ -14,29 +14,27 @@ namespace net { using base::StringPiece; -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 = lhs->IsLookup() ? 0 : 1 + lhs->InsertionIndex(); - const size_t rhs_index = rhs->IsLookup() ? 0 : 1 + rhs->InsertionIndex(); - 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; +size_t HpackHeaderTable::EntryHasher::operator()( + const HpackEntry* entry) const { + return base::StringPieceHash()(entry->name()) ^ + base::StringPieceHash()(entry->value()); +} + +bool HpackHeaderTable::EntriesEq::operator()(const HpackEntry* lhs, + const HpackEntry* rhs) const { + if (lhs == nullptr) { + return rhs == nullptr; + } + if (rhs == nullptr) { + return false; + } + return lhs->name() == rhs->name() && lhs->value() == rhs->value(); } HpackHeaderTable::HpackHeaderTable() : static_entries_(ObtainHpackStaticTable().GetStaticEntries()), static_index_(ObtainHpackStaticTable().GetStaticIndex()), + static_name_index_(ObtainHpackStaticTable().GetStaticNameIndex()), settings_size_bound_(kDefaultHeaderTableSizeSetting), size_(0), max_size_(kDefaultHeaderTableSizeSetting), @@ -60,17 +58,16 @@ const HpackEntry* HpackHeaderTable::GetByIndex(size_t index) { } const HpackEntry* HpackHeaderTable::GetByName(StringPiece name) { - HpackEntry query(name, ""); { - OrderedEntrySet::const_iterator it = static_index_.lower_bound(&query); - if (it != static_index_.end() && (*it)->name() == name) { - return *it; + NameToEntryMap::const_iterator it = static_name_index_.find(name); + if (it != static_name_index_.end()) { + return it->second; } } { - OrderedEntrySet::const_iterator it = dynamic_index_.lower_bound(&query); - if (it != dynamic_index_.end() && (*it)->name() == name) { - return *it; + NameToEntryMap::const_iterator it = dynamic_name_index_.find(name); + if (it != dynamic_name_index_.end()) { + return it->second; } } return NULL; @@ -80,16 +77,14 @@ const HpackEntry* HpackHeaderTable::GetByNameAndValue(StringPiece name, StringPiece value) { HpackEntry query(name, value); { - OrderedEntrySet::const_iterator it = static_index_.lower_bound(&query); - if (it != static_index_.end() && (*it)->name() == name && - (*it)->value() == value) { + UnorderedEntrySet::const_iterator it = static_index_.find(&query); + if (it != static_index_.end()) { return *it; } } { - OrderedEntrySet::const_iterator it = dynamic_index_.lower_bound(&query); - if (it != dynamic_index_.end() && (*it)->name() == name && - (*it)->value() == value) { + UnorderedEntrySet::const_iterator it = dynamic_index_.find(&query); + if (it != dynamic_index_.end()) { return *it; } } @@ -159,7 +154,22 @@ void HpackHeaderTable::Evict(size_t count) { HpackEntry* entry = &dynamic_entries_.back(); size_ -= entry->Size(); - CHECK_EQ(1u, dynamic_index_.erase(entry)); + UnorderedEntrySet::iterator it = dynamic_index_.find(entry); + DCHECK(it != dynamic_index_.end()); + // Only remove an entry from the index if its insertion index matches; + // otherwise, the index refers to another entry with the same name and + // value. + if ((*it)->InsertionIndex() == entry->InsertionIndex()) { + dynamic_index_.erase(it); + } + NameToEntryMap::iterator name_it = dynamic_name_index_.find(entry->name()); + DCHECK(name_it != dynamic_name_index_.end()); + // Only remove an entry from the literal index if its insertion index + /// matches; otherwise, the index refers to another entry with the same + // name. + if (name_it->second->InsertionIndex() == entry->InsertionIndex()) { + dynamic_name_index_.erase(name_it); + } dynamic_entries_.pop_back(); } } @@ -178,7 +188,35 @@ const HpackEntry* HpackHeaderTable::TryAddEntry(StringPiece name, dynamic_entries_.push_front(HpackEntry(name, value, false, // is_static total_insertions_)); - CHECK(dynamic_index_.insert(&dynamic_entries_.front()).second); + HpackEntry* new_entry = &dynamic_entries_.front(); + auto index_result = dynamic_index_.insert(new_entry); + if (!index_result.second) { + // An entry with the same name and value already exists in the dynamic + // index. We should replace it with the newly added entry. + DVLOG(1) << "Found existing entry: " + << (*index_result.first)->GetDebugString() + << " replacing with: " << new_entry->GetDebugString(); + DCHECK_GT(new_entry->InsertionIndex(), + (*index_result.first)->InsertionIndex()); + dynamic_index_.erase(index_result.first); + CHECK(dynamic_index_.insert(new_entry).second); + } + + auto name_result = + dynamic_name_index_.insert(std::make_pair(new_entry->name(), new_entry)); + if (!name_result.second) { + // An entry with the same name already exists in the dynamic index. We + // should replace it with the newly added entry. + DVLOG(1) << "Found existing entry: " + << name_result.first->second->GetDebugString() + << " replacing with: " << new_entry->GetDebugString(); + DCHECK_GT(new_entry->InsertionIndex(), + name_result.first->second->InsertionIndex()); + dynamic_name_index_.erase(name_result.first); + auto insert_result = dynamic_name_index_.insert( + std::make_pair(new_entry->name(), new_entry)); + CHECK(insert_result.second); + } size_ += entry_size; ++total_insertions_; @@ -193,14 +231,20 @@ void HpackHeaderTable::DebugLogTableState() const { DVLOG(2) << " " << it->GetDebugString(); } DVLOG(2) << "Full Static Index:"; - for (OrderedEntrySet::const_iterator it = static_index_.begin(); - it != static_index_.end(); ++it) { - DVLOG(2) << " " << (*it)->GetDebugString(); + for (const auto entry : static_index_) { + DVLOG(2) << " " << entry->GetDebugString(); + } + DVLOG(2) << "Full Static Name Index:"; + for (const auto it : static_name_index_) { + DVLOG(2) << " " << it.first << ": " << it.second->GetDebugString(); } DVLOG(2) << "Full Dynamic Index:"; - for (OrderedEntrySet::const_iterator it = dynamic_index_.begin(); - it != dynamic_index_.end(); ++it) { - DVLOG(2) << " " << (*it)->GetDebugString(); + for (const auto entry : dynamic_index_) { + DVLOG(2) << " " << entry->GetDebugString(); + } + DVLOG(2) << "Full Dynamic Name Index:"; + for (const auto it : dynamic_name_index_) { + DVLOG(2) << " " << it.first << ": " << it.second->GetDebugString(); } } diff --git a/src/net/spdy/hpack/hpack_header_table.h b/src/net/spdy/hpack/hpack_header_table.h index be0ac0b7..02b9e27d 100644 --- a/src/net/spdy/hpack/hpack_header_table.h +++ b/src/net/spdy/hpack/hpack_header_table.h @@ -7,9 +7,11 @@ #include #include -#include +#include +#include #include "base/macros.h" +#include "base/strings/string_piece.h" #include "net/base/net_export.h" #include "net/spdy/hpack/hpack_entry.h" @@ -33,15 +35,18 @@ class NET_EXPORT_PRIVATE HpackHeaderTable { // extended to map to list iterators. typedef std::deque 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 { + struct NET_EXPORT_PRIVATE EntryHasher { + size_t operator()(const HpackEntry* entry) const; + }; + struct NET_EXPORT_PRIVATE EntriesEq { bool operator()(const HpackEntry* lhs, const HpackEntry* rhs) const; }; - typedef std::set OrderedEntrySet; + + using UnorderedEntrySet = + std::unordered_set; + using NameToEntryMap = std::unordered_map; HpackHeaderTable(); @@ -109,8 +114,18 @@ class NET_EXPORT_PRIVATE HpackHeaderTable { const EntryTable& static_entries_; EntryTable dynamic_entries_; - const OrderedEntrySet& static_index_; - OrderedEntrySet dynamic_index_; + // Tracks the unique HpackEntry for a given header name and value. + const UnorderedEntrySet& static_index_; + + // Tracks the first static entry for each name in the static table. + const NameToEntryMap& static_name_index_; + + // Tracks the most recently inserted HpackEntry for a given header name and + // value. + UnorderedEntrySet dynamic_index_; + + // Tracks the most recently inserted HpackEntry for a given header name. + NameToEntryMap dynamic_name_index_; // Last acknowledged value for SETTINGS_HEADER_TABLE_SIZE. size_t settings_size_bound_; diff --git a/src/net/spdy/hpack/hpack_huffman_decoder.cc b/src/net/spdy/hpack/hpack_huffman_decoder.cc new file mode 100644 index 00000000..3573744c --- /dev/null +++ b/src/net/spdy/hpack/hpack_huffman_decoder.cc @@ -0,0 +1,410 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Decoder for strings encoded using the HPACK Huffman Code (see +// https://httpwg.github.io/specs/rfc7541.html#huffman.code). +// +// This implementation is inspired by the One-Shift algorithm described in +// "On the Implementation of Minimum Redundancy Prefix Codes", by Alistair +// Moffat and Andrew Turpin, 1997. +// See also https://en.wikipedia.org/wiki/Canonical_Huffman_code for background +// on canonical Huffman codes. +// +// This decoder differs from that in .../spdy/hpack/hpack_huffman_table.cc +// as follows: +// 1) It decodes only the code described in RFC7541, where as the older +// implementation supported any canonical Huffman code provided at run +// time. +// 2) It uses a fixed amount of memory allocated at build time; it doesn't +// construct a tree of of decoding tables based on an encoding +// table provided at run time. +// 3) In benchmarks it runs from 10% to 70% faster, based on the length +// of the strings (faster for longer strings). Some of the improvements +// could be back ported, but others are fundamental to the approach. + +#include "net/spdy/hpack/hpack_huffman_decoder.h" + +#include +#include +#include + +#include "base/logging.h" +#include "net/spdy/hpack/hpack_input_stream.h" + +namespace net { +namespace { + +typedef HpackHuffmanDecoder::HuffmanWord HuffmanWord; +typedef HpackHuffmanDecoder::HuffmanCodeLength HuffmanCodeLength; + +const HuffmanCodeLength kHuffmanWordLength = + std::numeric_limits::digits; + +const HuffmanCodeLength kMinCodeLength = 5; +const HuffmanCodeLength kMaxCodeLength = 30; + +const HuffmanWord kInvalidLJCode = ~static_cast(0); +// Length of a code in bits to the first code with that length, left-justified. +// Note that this can be computed from kLengthToFirstCanonical. +const HuffmanWord kLengthToFirstLJCode[] = { + kInvalidLJCode, // There are no codes of length 0. + kInvalidLJCode, // There are no codes of length 1. + kInvalidLJCode, // There are no codes of length 2. + kInvalidLJCode, // There are no codes of length 3. + kInvalidLJCode, // There are no codes of length 4. + 0x00000000, // Length 5. + 0x50000000, // Length 6. + 0xb8000000, // Length 7. + 0xf8000000, // Length 8. + kInvalidLJCode, // There are no codes of length 9. + 0xfe000000, // Length 10. + 0xff400000, // Length 11. + 0xffa00000, // Length 12. + 0xffc00000, // Length 13. + 0xfff00000, // Length 14. + 0xfff80000, // Length 15. + kInvalidLJCode, // There are no codes of length 16. + kInvalidLJCode, // There are no codes of length 17. + kInvalidLJCode, // There are no codes of length 18. + 0xfffe0000, // Length 19. + 0xfffe6000, // Length 20. + 0xfffee000, // Length 21. + 0xffff4800, // Length 22. + 0xffffb000, // Length 23. + 0xffffea00, // Length 24. + 0xfffff600, // Length 25. + 0xfffff800, // Length 26. + 0xfffffbc0, // Length 27. + 0xfffffe20, // Length 28. + kInvalidLJCode, // There are no codes of length 29. + 0xfffffff0, // Length 30. +}; + +// TODO(jamessynge): Determine the performance impact of different types for +// the elements of this array (i.e. a larger type uses more cache, yet might +// better on some architectures). +const uint8_t kInvalidCanonical = 255; +// Maps from length of a code to the first 'canonical symbol' with that length. +const uint8_t kLengthToFirstCanonical[] = { + kInvalidCanonical, // Length 0, 0 codes. + kInvalidCanonical, // Length 1, 0 codes. + kInvalidCanonical, // Length 2, 0 codes. + kInvalidCanonical, // Length 3, 0 codes. + kInvalidCanonical, // Length 4, 0 codes. + 0, // Length 5, 10 codes. + 10, // Length 6, 26 codes. + 36, // Length 7, 32 codes. + 68, // Length 8, 6 codes. + kInvalidCanonical, // Length 9, 0 codes. + 74, // Length 10, 5 codes. + 79, // Length 11, 3 codes. + 82, // Length 12, 2 codes. + 84, // Length 13, 6 codes. + 90, // Length 14, 2 codes. + 92, // Length 15, 3 codes. + kInvalidCanonical, // Length 16, 0 codes. + kInvalidCanonical, // Length 17, 0 codes. + kInvalidCanonical, // Length 18, 0 codes. + 95, // Length 19, 3 codes. + 98, // Length 20, 8 codes. + 106, // Length 21, 13 codes. + 119, // Length 22, 26 codes. + 145, // Length 23, 29 codes. + 174, // Length 24, 12 codes. + 186, // Length 25, 4 codes. + 190, // Length 26, 15 codes. + 205, // Length 27, 19 codes. + 224, // Length 28, 29 codes. + kInvalidCanonical, // Length 29, 0 codes. + 253, // Length 30, 4 codes. +}; + +// Mapping from canonical symbol (0 to 255) to actual symbol. +// clang-format off +const uint8_t kCanonicalToSymbol[] = { + '0', '1', '2', 'a', 'c', 'e', 'i', 'o', + 's', 't', 0x20, '%', '-', '.', '/', '3', + '4', '5', '6', '7', '8', '9', '=', 'A', + '_', 'b', 'd', 'f', 'g', 'h', 'l', 'm', + 'n', 'p', 'r', 'u', ':', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'Y', 'j', 'k', 'q', 'v', + 'w', 'x', 'y', 'z', '&', '*', ',', ';', + 'X', 'Z', '!', '\"', '(', ')', '?', '\'', + '+', '|', '#', '>', 0x00, '$', '@', '[', + ']', '~', '^', '}', '<', '`', '{', '\\', + 0xc3, 0xd0, 0x80, 0x82, 0x83, 0xa2, 0xb8, 0xc2, + 0xe0, 0xe2, 0x99, 0xa1, 0xa7, 0xac, 0xb0, 0xb1, + 0xb3, 0xd1, 0xd8, 0xd9, 0xe3, 0xe5, 0xe6, 0x81, + 0x84, 0x85, 0x86, 0x88, 0x92, 0x9a, 0x9c, 0xa0, + 0xa3, 0xa4, 0xa9, 0xaa, 0xad, 0xb2, 0xb5, 0xb9, + 0xba, 0xbb, 0xbd, 0xbe, 0xc4, 0xc6, 0xe4, 0xe8, + 0xe9, 0x01, 0x87, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, + 0x8f, 0x93, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9d, + 0x9e, 0xa5, 0xa6, 0xa8, 0xae, 0xaf, 0xb4, 0xb6, + 0xb7, 0xbc, 0xbf, 0xc5, 0xe7, 0xef, 0x09, 0x8e, + 0x90, 0x91, 0x94, 0x9f, 0xab, 0xce, 0xd7, 0xe1, + 0xec, 0xed, 0xc7, 0xcf, 0xea, 0xeb, 0xc0, 0xc1, + 0xc8, 0xc9, 0xca, 0xcd, 0xd2, 0xd5, 0xda, 0xdb, + 0xee, 0xf0, 0xf2, 0xf3, 0xff, 0xcb, 0xcc, 0xd3, + 0xd4, 0xd6, 0xdd, 0xde, 0xdf, 0xf1, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0b, + 0x0c, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x7f, 0xdc, 0xf9, 0x0a, 0x0d, 0x16, +}; +// clang-format on + +#ifndef NDEBUG + +// Only used in DLOG. +bool IsEOSPrefix(HuffmanWord bits, HuffmanCodeLength bits_available) { + if (bits_available == 0) { + return true; + } + // We expect all the bits below the high order |bits_available| bits + // to be cleared. + HuffmanWord expected = HuffmanWord(0xffffffff) << (32 - bits_available); + return bits == expected; +} + +#endif // NDEBUG + +} // namespace + +// TODO(jamessynge): Should we read these magic numbers from +// kLengthToFirstLJCode? Would that reduce cache consumption? Slow decoding? +// TODO(jamessynge): Is this being inlined by the compiler? Should we inline +// into DecodeString the tests for code lengths 5 through 8 (> 99% of codes +// according to the HPACK spec)? +HpackHuffmanDecoder::HuffmanCodeLength HpackHuffmanDecoder::CodeLengthOfPrefix( + HpackHuffmanDecoder::HuffmanWord value) { + HuffmanCodeLength length; + if (value < 0xb8000000) { + if (value < 0x50000000) { + length = 5; + } else { + length = 6; + } + } else { + if (value < 0xfe000000) { + if (value < 0xf8000000) { + length = 7; + } else { + length = 8; + } + } else { + if (value < 0xffc00000) { + if (value < 0xffa00000) { + if (value < 0xff400000) { + length = 10; + } else { + length = 11; + } + } else { + length = 12; + } + } else { + if (value < 0xfffe0000) { + if (value < 0xfff80000) { + if (value < 0xfff00000) { + length = 13; + } else { + length = 14; + } + } else { + length = 15; + } + } else { + if (value < 0xffff4800) { + if (value < 0xfffee000) { + if (value < 0xfffe6000) { + length = 19; + } else { + length = 20; + } + } else { + length = 21; + } + } else { + if (value < 0xffffea00) { + if (value < 0xffffb000) { + length = 22; + } else { + length = 23; + } + } else { + if (value < 0xfffffbc0) { + if (value < 0xfffff800) { + if (value < 0xfffff600) { + length = 24; + } else { + length = 25; + } + } else { + length = 26; + } + } else { + if (value < 0xfffffff0) { + if (value < 0xfffffe20) { + length = 27; + } else { + length = 28; + } + } else { + length = 30; + } + } + } + } + } + } + } + } + return length; +} + +HuffmanWord HpackHuffmanDecoder::DecodeToCanonical( + HuffmanCodeLength code_length, + HuffmanWord bits) { + DCHECK_LE(kMinCodeLength, code_length); + DCHECK_LE(code_length, kMaxCodeLength); + + // What is the first left-justified code of length |code_length|? + HuffmanWord first_lj_code = kLengthToFirstLJCode[code_length]; + DCHECK_NE(kInvalidLJCode, first_lj_code); + + // Which canonical symbol corresponds to the high order |code_length| + // bits of |first_lj_code|? + HuffmanWord first_canonical = kLengthToFirstCanonical[code_length]; + DCHECK_NE(kInvalidCanonical, first_canonical); + + // What is the position of the canonical symbol being decoded within + // the canonical symbols of length |code_length|? + HuffmanWord ordinal_in_length = + ((bits - first_lj_code) >> (kHuffmanWordLength - code_length)); + + // Combined these two to produce the position of the canonical symbol + // being decoded within all of the canonical symbols. + return first_canonical + ordinal_in_length; +} + +char HpackHuffmanDecoder::CanonicalToSource(HuffmanWord canonical) { + DCHECK_LT(canonical, 256u); + return static_cast(kCanonicalToSymbol[canonical]); +} + +// TODO(jamessynge): Maybe further refactorings, including just passing in a +// StringPiece instead of an HpackInputStream, thus avoiding the PeekBits calls, +// and also allowing us to separate the code into portions dealing with long +// strings, and a later portion dealing with the last few bytes of strings. +// TODO(jamessynge): Determine if that is worth it by adding some counters to +// measure the distribution of string sizes seen in practice. +bool HpackHuffmanDecoder::DecodeString(HpackInputStream* in, + size_t out_capacity, + std::string* out) { + out->clear(); + + // Load |bits| with the leading bits of the input stream, left justified + // (i.e. the bits of the first byte are the high-order bits of |bits|, + // and the bits of the fourth byte are the low-order bits of |bits|). + // |peeked_success| if there are more bits in |*in| (i.e. the encoding + // of the string to be decoded is more than 4 bytes). + + auto bits_available_and_bits = in->InitializePeekBits(); + HuffmanCodeLength bits_available = bits_available_and_bits.first; + HuffmanWord bits = bits_available_and_bits.second; + + // |peeked_success| tracks whether the previous PeekBits call was able to + // store any new bits into |bits|. For the first pass through the loop below + // the value false is appropriate: + // If we have 32 bits (i.e. the input has at least 4 bytes), then: + // |peeked_sucess| is not examined because |code_length| is + // at most 30 in the HPACK Huffman Code. + // If we have at most 24 bits (i.e. the input has at most 3 bytes), then: + // It is possible that the very first |code_length| is greater than + // |bits_available|, in which case we need to read peeked_success to + // determine whether we should try to read more input, or have already + // loaded |bits| with the final bits of the input. + // After the first loop |peeked_success| has been set by a call to PeekBits. + bool peeked_success = false; + + while (true) { + const HuffmanCodeLength code_length = CodeLengthOfPrefix(bits); + DCHECK_LE(kMinCodeLength, code_length); + DCHECK_LE(code_length, kMaxCodeLength); + DVLOG(1) << "bits: 0b" << std::bitset<32>(bits) + << " (avail=" << bits_available << ")" + << " prefix length: " << code_length + << (code_length > bits_available ? " *****" : ""); + if (code_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 EOS condition. + // Note that this does NOT check whether the available bits are all + // set to 1, which the encoder is required to set at EOS, and the + // decoder is required to check. + // TODO(jamessynge): Discuss whether we should enforce this check, + // as required by the RFC, presumably flag guarded so that we can + // disable it should it occur a lot. From my testing it appears that + // our encoder may be doing this wrong. Sigh. + // TODO(jamessynge): Add a counter for how often the remaining bits + // are non-zero. + in->ConsumeByteRemainder(); + DLOG_IF(WARNING, + (in->HasMoreData() || !IsEOSPrefix(bits, bits_available))) + << "bits: 0b" << std::bitset<32>(bits) + << " (avail=" << bits_available << ")" + << " prefix length: " << code_length + << " HasMoreData: " << in->HasMoreData(); + return !in->HasMoreData(); + } + // We're dealing with a long code. It *might* be useful to add a special + // method to HpackInputStream for getting more than "at most 8" bits + // at a time. + do { + peeked_success = in->PeekBits(&bits_available, &bits); + } while (peeked_success && bits_available < 32); + } else { + if (out->size() == out_capacity) { + // This code would cause us to overflow |out_capacity|. + // TODO(jamessynge) Avoid this case by pre-allocating out based on + // scaling up the encoded size by 8/5 (shortest codes are 5 bits). + DLOG(WARNING) << "Output size too large: " << out_capacity; + return false; + } + + // Convert from the prefix code of length |code_length| to the + // canonical symbol (i.e. where the input symbols (bytes) are ordered by + // increasing code length and then by their increasing uint8 value). + HuffmanWord canonical = DecodeToCanonical(code_length, bits); + + bits = bits << code_length; + bits_available -= code_length; + in->ConsumeBits(code_length); + + if (canonical < 256) { + out->push_back(CanonicalToSource(canonical)); + } else { + // Encoder is not supposed to explicity encode the EOS symbol (30 + // 1-bits). + // TODO(jamessynge): Discuss returning false here, as required by HPACK. + DCHECK(false) << "EOS explicitly encoded!\n" + << "bits: 0b" << std::bitset<32>(bits) + << " (avail=" << bits_available << ")" + << " prefix length: " << code_length + << " canonical: " << canonical; + } + // Get some more bits for decoding (up to 8). |peeked_success| is true + // if we got any bits. + peeked_success = in->PeekBits(&bits_available, &bits); + } + DLOG_IF(WARNING, (VLOG_IS_ON(1) && bits_available < 32 && !peeked_success)) + << "no more peeking possible"; + } +} + +} // namespace net diff --git a/src/net/spdy/hpack/hpack_huffman_decoder.h b/src/net/spdy/hpack/hpack_huffman_decoder.h new file mode 100644 index 00000000..d63aa048 --- /dev/null +++ b/src/net/spdy/hpack/hpack_huffman_decoder.h @@ -0,0 +1,70 @@ +// Copyright 2016 The Chromium 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_HPACK_HUFFMAN_DECODER_H_ +#define NET_SPDY_HPACK_HPACK_HUFFMAN_DECODER_H_ + +#include +#include + +#include + +#include "net/base/net_export.h" +#include "net/spdy/hpack/hpack_input_stream.h" + +namespace net { +namespace test { +class HpackHuffmanDecoderPeer; +} // namespace test + +// Declared as a class to simplify testing. +// No instances are actually allocated. +class NET_EXPORT_PRIVATE HpackHuffmanDecoder { + public: + typedef uint32_t HuffmanWord; + typedef size_t HuffmanCodeLength; + + HpackHuffmanDecoder() = delete; + + // Decodes a string that has been encoded using the HPACK Huffman Code (see + // https://httpwg.github.io/specs/rfc7541.html#huffman.code), reading the + // encoded bitstream from |*in|, appending each decoded char to |*out|. + // To avoid repeatedly growing the |*out| string, the caller should reserve + // sufficient space in |*out| 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. + static bool DecodeString(HpackInputStream* in, + size_t out_capacity, + std::string* out); + + private: + friend class test::HpackHuffmanDecoderPeer; + + // The following private methods are declared here rather than simply + // inlined into DecodeString so that they can be tested directly. + + // Returns the length (in bits) of the HPACK Huffman code that starts with + // the high bits of |value|. + static HuffmanCodeLength CodeLengthOfPrefix(HuffmanWord value); + + // Decodes the code in the high |code_length| bits of |bits| to the + // corresponding canonical symbol. + // Returns a value in the range [0, 256] (257 values). 256 is the EOS symbol, + // which must not be explicitly encoded; the HPACK spec says that a decoder + // must treat EOS as a decoding error. + // Note that the canonical symbol is not the final value to be output because + // the source symbols are not in descending probability order, so another + // translation is required (see CanonicalToSource below). + static HuffmanWord DecodeToCanonical(HuffmanCodeLength code_length, + HuffmanWord bits); + + // Converts a canonical symbol to the source symbol (the char in the original + // string that was encoded). + static char CanonicalToSource(HuffmanWord canonical); +}; + +} // namespace net + +#endif // NET_SPDY_HPACK_HPACK_HUFFMAN_DECODER_H_ diff --git a/src/net/spdy/hpack/hpack_huffman_table.cc b/src/net/spdy/hpack/hpack_huffman_table.cc index b56c58ec..e29e1c4d 100644 --- a/src/net/spdy/hpack/hpack_huffman_table.cc +++ b/src/net/spdy/hpack/hpack_huffman_table.cc @@ -6,6 +6,7 @@ #include #include +#include #include "base/logging.h" #include "base/numerics/safe_conversions.h" @@ -263,9 +264,9 @@ size_t HpackHuffmanTable::EncodedSize(StringPiece in) const { return bit_count / 8; } -bool HpackHuffmanTable::DecodeString(HpackInputStream* in, - size_t out_capacity, - string* out) const { +bool HpackHuffmanTable::GenericDecodeString(HpackInputStream* in, + size_t out_capacity, + string* out) const { // Number of decode iterations required for a 32-bit code. const int kDecodeIterations = static_cast( std::ceil((32.f - kDecodeTableRootBits) / kDecodeTableBranchBits)); diff --git a/src/net/spdy/hpack/hpack_huffman_table.h b/src/net/spdy/hpack/hpack_huffman_table.h index 56627150..6fd60e43 100644 --- a/src/net/spdy/hpack/hpack_huffman_table.h +++ b/src/net/spdy/hpack/hpack_huffman_table.h @@ -82,14 +82,18 @@ class NET_EXPORT_PRIVATE HpackHuffmanTable { // 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; + // Decodes symbols from |in| into |out|, using the support for generic (any) + // huffman tables, not just those defined in the HPACK spec. It is the + // caller's responsibility to ensure |out| has reserved a sufficient buffer to + // hold decoded output. GenericDecodeString() 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. + // DEPRECATED: HpackHuffmanDecoder is now used for decoding strings encoded + // according to the Huffman Table in the HPACK spec. + bool GenericDecodeString(HpackInputStream* in, + size_t out_capacity, + std::string* out) const; private: // Expects symbols ordered on length & ID ascending. diff --git a/src/net/spdy/hpack/hpack_input_stream.cc b/src/net/spdy/hpack/hpack_input_stream.cc index f1869285..e9e9540e 100644 --- a/src/net/spdy/hpack/hpack_input_stream.cc +++ b/src/net/spdy/hpack/hpack_input_stream.cc @@ -7,6 +7,7 @@ #include #include "base/logging.h" +#include "net/spdy/hpack/hpack_huffman_decoder.h" namespace net { @@ -45,6 +46,7 @@ bool HpackInputStream::MatchPrefixAndConsume(HpackPrefix prefix) { bool HpackInputStream::PeekNextOctet(uint8_t* next_octet) { if ((bit_offset_ > 0) || buffer_.empty()) { + DVLOG(1) << "HpackInputStream::PeekNextOctet bit_offset_=" << bit_offset_; return false; } @@ -73,6 +75,7 @@ bool HpackInputStream::DecodeNextUint32(uint32_t* I) { uint8_t next_marker = (1 << N) - 1; uint8_t next_octet = 0; if (!DecodeNextOctet(&next_octet)) { + DVLOG(1) << "HpackInputStream::DecodeNextUint32 initial octet error"; return false; } *I = next_octet & next_marker; @@ -82,6 +85,7 @@ bool HpackInputStream::DecodeNextUint32(uint32_t* I) { while (has_more && (shift < 32)) { uint8_t next_octet = 0; if (!DecodeNextOctet(&next_octet)) { + DVLOG(1) << "HpackInputStream::DecodeNextUint32 shift=" << shift; return false; } has_more = (next_octet & 0x80) != 0; @@ -89,6 +93,7 @@ bool HpackInputStream::DecodeNextUint32(uint32_t* I) { uint32_t addend = next_octet << shift; // Check for overflow. if ((addend >> shift) != next_octet) { + DVLOG(1) << "HpackInputStream::DecodeNextUint32 overflow"; return false; } *I += addend; @@ -117,14 +122,17 @@ bool HpackInputStream::DecodeNextIdentityString(StringPiece* str) { return true; } -bool HpackInputStream::DecodeNextHuffmanString(const HpackHuffmanTable& table, - string* str) { +bool HpackInputStream::DecodeNextHuffmanString(string* str) { uint32_t encoded_size = 0; if (!DecodeNextUint32(&encoded_size)) { + DVLOG(1) << "HpackInputStream::DecodeNextHuffmanString " + << "unable to decode size"; return false; } if (encoded_size > buffer_.size()) { + DVLOG(1) << "HpackInputStream::DecodeNextHuffmanString " << encoded_size + << " > " << buffer_.size(); return false; } @@ -132,8 +140,10 @@ bool HpackInputStream::DecodeNextHuffmanString(const HpackHuffmanTable& table, 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); + // DecodeString will not append more than |max_string_literal_size_| chars + // to |str|. + return HpackHuffmanDecoder::DecodeString(&bounded_reader, + max_string_literal_size_, str); } bool HpackInputStream::PeekBits(size_t* peeked_count, uint32_t* out) const { @@ -161,6 +171,41 @@ bool HpackInputStream::PeekBits(size_t* peeked_count, uint32_t* out) const { return true; } +std::pair HpackInputStream::InitializePeekBits() { + size_t peeked_count = 0; + uint32_t bits = 0; + if (bit_offset_ == 0) { + switch (buffer_.size()) { + default: + DCHECK_LE(4u, buffer_.size()); + bits = static_cast(static_cast(buffer_[3])); + peeked_count += 8; + /* FALLTHROUGH */ + case 3: + bits |= (static_cast(static_cast(buffer_[2])) + << 8); + peeked_count += 8; + /* FALLTHROUGH */ + case 2: + bits |= (static_cast(static_cast(buffer_[1])) + << 16); + peeked_count += 8; + /* FALLTHROUGH */ + case 1: + bits |= (static_cast(static_cast(buffer_[0])) + << 24); + peeked_count += 8; + break; + case 0: + break; + } + } else { + LOG(DFATAL) << "InitializePeekBits called with non-zero bit_offset_: " + << bit_offset_; + } + return std::make_pair(peeked_count, bits); +} + void HpackInputStream::ConsumeBits(size_t bit_count) { size_t byte_count = (bit_offset_ + bit_count) / 8; bit_offset_ = (bit_offset_ + bit_count) % 8; diff --git a/src/net/spdy/hpack/hpack_input_stream.h b/src/net/spdy/hpack/hpack_input_stream.h index e7787f7c..ab716d0e 100644 --- a/src/net/spdy/hpack/hpack_input_stream.h +++ b/src/net/spdy/hpack/hpack_input_stream.h @@ -9,6 +9,7 @@ #include #include +#include #include "base/macros.h" #include "base/strings/string_piece.h" @@ -21,6 +22,8 @@ namespace net { +typedef std::pair InitialPeekResult; + // An HpackInputStream handles all the low-level details of decoding // header fields. class NET_EXPORT_PRIVATE HpackInputStream { @@ -42,16 +45,26 @@ class NET_EXPORT_PRIVATE HpackInputStream { bool DecodeNextUint32(uint32_t* I); bool DecodeNextIdentityString(base::StringPiece* str); - bool DecodeNextHuffmanString(const HpackHuffmanTable& table, - std::string* str); + bool DecodeNextHuffmanString(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. + // or more additional bits were added to |out|, and false otherwise. bool PeekBits(size_t* peeked_count, uint32_t* out) const; + // Similar to PeekBits, but intended to be used when starting to decode a + // Huffman encoded string. Returns a pair containing the peeked_count and + // out values as described for PeekBits, with the bits from the first N bytes + // of buffer_, where N == min(4, buffer_.size()), starting with the high + // order bits. + // Should only be called when first peeking at bits from the input stream as + // it does not take peeked_count as an input, so doesn't know how many bits + // have already been returned by previous calls to InitializePeekBits and + // PeekBits. + InitialPeekResult InitializePeekBits(); + // Consumes |count| bits of input. Generally paired with PeekBits(). void ConsumeBits(size_t count); diff --git a/src/net/spdy/hpack/hpack_static_table.cc b/src/net/spdy/hpack/hpack_static_table.cc index b954c348..5588ebe1 100644 --- a/src/net/spdy/hpack/hpack_static_table.cc +++ b/src/net/spdy/hpack/hpack_static_table.cc @@ -26,7 +26,10 @@ void HpackStaticTable::Initialize(const HpackStaticEntry* static_entry_table, base::StringPiece(it->value, it->value_len), true, // is_static total_insertions)); - CHECK(static_index_.insert(&static_entries_.back()).second); + HpackEntry* entry = &static_entries_.back(); + CHECK(static_index_.insert(entry).second); + // Multiple static entries may have the same name, so inserts may fail. + static_name_index_.insert(make_pair(entry->name(), entry)); ++total_insertions; } diff --git a/src/net/spdy/hpack/hpack_static_table.h b/src/net/spdy/hpack/hpack_static_table.h index e7616b33..42798bfc 100644 --- a/src/net/spdy/hpack/hpack_static_table.h +++ b/src/net/spdy/hpack/hpack_static_table.h @@ -34,13 +34,17 @@ class NET_EXPORT_PRIVATE HpackStaticTable { const HpackHeaderTable::EntryTable& GetStaticEntries() const { return static_entries_; } - const HpackHeaderTable::OrderedEntrySet& GetStaticIndex() const { + const HpackHeaderTable::UnorderedEntrySet& GetStaticIndex() const { return static_index_; } + const HpackHeaderTable::NameToEntryMap& GetStaticNameIndex() const { + return static_name_index_; + } private: HpackHeaderTable::EntryTable static_entries_; - HpackHeaderTable::OrderedEntrySet static_index_; + HpackHeaderTable::UnorderedEntrySet static_index_; + HpackHeaderTable::NameToEntryMap static_name_index_; }; } // namespace net diff --git a/src/net/spdy/priority_write_scheduler.h b/src/net/spdy/priority_write_scheduler.h index dd16b41c..e1e144dd 100644 --- a/src/net/spdy/priority_write_scheduler.h +++ b/src/net/spdy/priority_write_scheduler.h @@ -130,16 +130,43 @@ class PriorityWriteScheduler { return stream_id; } + // Returns true if there's another stream of greater or equal priority ahead + // of |stream_id| in the queue. This function can be called to see if + // |stream_id| should yield work to another stream. + bool ShouldYield(StreamIdType stream_id) const { + // If there's a higher priority stream, this stream should yield. + if (HasHigherPriorityReadyStream(stream_id)) { + return true; + } + + auto it = stream_infos_.find(stream_id); + if (it == stream_infos_.end()) { + LOG(DFATAL) << "Stream " << stream_id << " not registered"; + return false; + } + + // If this priority level is empty, or this stream is the next up, there's + // no need to yield. + auto ready_list = ready_lists_[it->second.priority]; + if (ready_list.empty() || ready_list.front() == stream_id) { + return false; + } + + // There are other streams in this priority level which take precedence. + // Yield. + return true; + } + // Returns true if the scheduler has any ready streams with a higher priority // than that of the specified stream. If the stream is not registered, logs // DFATAL and returns false. - bool HasHigherPriorityReadyStream(StreamIdType stream_id) { + bool HasHigherPriorityReadyStream(StreamIdType stream_id) const { auto it = stream_infos_.find(stream_id); if (it == stream_infos_.end()) { LOG(DFATAL) << "Stream " << stream_id << " not registered"; return false; } - StreamInfo& stream_info = it->second; + const StreamInfo& stream_info = it->second; for (SpdyPriority p = kV3HighestPriority; p < stream_info.priority; ++p) { if (!ready_lists_[p].empty()) { return true; diff --git a/src/net/spdy/spdy_alt_svc_wire_format.cc b/src/net/spdy/spdy_alt_svc_wire_format.cc index ba32160e..2ec67775 100644 --- a/src/net/spdy/spdy_alt_svc_wire_format.cc +++ b/src/net/spdy/spdy_alt_svc_wire_format.cc @@ -4,6 +4,7 @@ #include "net/spdy/spdy_alt_svc_wire_format.h" +#include #include #include @@ -54,6 +55,9 @@ SpdyAltSvcWireFormat::AlternativeService::AlternativeService( SpdyAltSvcWireFormat::AlternativeService::~AlternativeService() {} +SpdyAltSvcWireFormat::AlternativeService::AlternativeService( + const AlternativeService& other) = default; + // static bool SpdyAltSvcWireFormat::ParseHeaderFieldValue( StringPiece value, diff --git a/src/net/spdy/spdy_alt_svc_wire_format.h b/src/net/spdy/spdy_alt_svc_wire_format.h index 28b45723..de404352 100644 --- a/src/net/spdy/spdy_alt_svc_wire_format.h +++ b/src/net/spdy/spdy_alt_svc_wire_format.h @@ -47,6 +47,7 @@ class NET_EXPORT_PRIVATE SpdyAltSvcWireFormat { uint32_t max_age, double probability, VersionVector version); + AlternativeService(const AlternativeService& other); ~AlternativeService(); bool operator==(const AlternativeService& other) const { diff --git a/src/net/spdy/spdy_frame_builder.cc b/src/net/spdy/spdy_frame_builder.cc index fb8cdccb..7911b808 100644 --- a/src/net/spdy/spdy_frame_builder.cc +++ b/src/net/spdy/spdy_frame_builder.cc @@ -62,14 +62,13 @@ bool SpdyFrameBuilder::Seek(size_t length) { bool SpdyFrameBuilder::WriteControlFrameHeader(const SpdyFramer& framer, SpdyFrameType type, uint8_t flags) { - DCHECK_GE(SPDY3, version_); + DCHECK_EQ(SPDY3, version_); DCHECK(SpdyConstants::IsValidFrameType( version_, SpdyConstants::SerializeFrameType(version_, type))); bool success = true; FlagsAndLength flags_length = CreateFlagsAndLength( flags, capacity_ - framer.GetControlFrameHeaderSize()); - success &= WriteUInt16(kControlFlagMask | - SpdyConstants::SerializeMajorVersion(version_)); + success &= WriteUInt16(kControlFlagMask | kSpdy3Version); success &= WriteUInt16( SpdyConstants::SerializeFrameType(framer.protocol_version(), type)); success &= WriteBytes(&flags_length, sizeof(flags_length)); @@ -80,7 +79,7 @@ bool SpdyFrameBuilder::WriteControlFrameHeader(const SpdyFramer& framer, bool SpdyFrameBuilder::WriteDataFrameHeader(const SpdyFramer& framer, SpdyStreamId stream_id, uint8_t flags) { - if (version_ > SPDY3) { + if (version_ == HTTP2) { return BeginNewFrame(framer, DATA, flags, stream_id); } DCHECK_EQ(0u, stream_id & ~kStreamIdMask); @@ -104,7 +103,7 @@ bool SpdyFrameBuilder::BeginNewFrame(const SpdyFramer& framer, DCHECK(SpdyConstants::IsValidFrameType( version_, SpdyConstants::SerializeFrameType(version_, type))); DCHECK_EQ(0u, stream_id & ~kStreamIdMask); - DCHECK_GT(framer.protocol_version(), SPDY3); + DCHECK_EQ(HTTP2, framer.protocol_version()); bool success = true; if (length_ > 0) { // Update length field for previous frame. @@ -169,7 +168,7 @@ bool SpdyFrameBuilder::RewriteLength(const SpdyFramer& framer) { bool SpdyFrameBuilder::OverwriteLength(const SpdyFramer& framer, size_t length) { - if (version_ < HTTP2) { + if (version_ == SPDY3) { DCHECK_LE(length, SpdyConstants::GetFrameMaximumSize(version_) - framer.GetFrameMinimumSize()); @@ -179,7 +178,7 @@ bool SpdyFrameBuilder::OverwriteLength(const SpdyFramer& framer, bool success = false; const size_t old_length = length_; - if (version_ < HTTP2) { + if (version_ == SPDY3) { FlagsAndLength flags_length = CreateFlagsAndLength( 0, // We're not writing over the flags value anyway. length); @@ -198,7 +197,7 @@ bool SpdyFrameBuilder::OverwriteLength(const SpdyFramer& framer, } bool SpdyFrameBuilder::OverwriteFlags(const SpdyFramer& framer, uint8_t flags) { - DCHECK_GT(framer.protocol_version(), SPDY3); + DCHECK_EQ(HTTP2, framer.protocol_version()); bool success = false; const size_t old_length = length_; // Flags are the fifth octet in the frame prefix. diff --git a/src/net/spdy/spdy_frame_builder.h b/src/net/spdy/spdy_frame_builder.h index 8d75aef7..29cb5546 100644 --- a/src/net/spdy/spdy_frame_builder.h +++ b/src/net/spdy/spdy_frame_builder.h @@ -75,7 +75,7 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder { // Takes the buffer from the SpdyFrameBuilder. SpdyFrame* take() { - if (version_ > SPDY3) { + if (version_ == HTTP2) { DLOG_IF(DFATAL, SpdyConstants::GetFrameMaximumSize(version_) < length_) << "Frame length " << length_ << " is longer than the maximum allowed length."; diff --git a/src/net/spdy/spdy_framer.cc b/src/net/spdy/spdy_framer.cc index 08f32dd3..c51ea584 100644 --- a/src/net/spdy/spdy_framer.cc +++ b/src/net/spdy/spdy_framer.cc @@ -82,10 +82,8 @@ void UnpackStreamDependencyValues(uint32_t packed, struct DictionaryIds { DictionaryIds() - : v2_dictionary_id(CalculateDictionaryId(kV2Dictionary, kV2DictionarySize)), - v3_dictionary_id(CalculateDictionaryId(kV3Dictionary, kV3DictionarySize)) - {} - const uLong v2_dictionary_id; + : v3_dictionary_id( + CalculateDictionaryId(kV3Dictionary, kV3DictionarySize)) {} const uLong v3_dictionary_id; }; @@ -107,8 +105,10 @@ const size_t kPadLengthFieldSize = 1; const SpdyStreamId SpdyFramer::kInvalidStream = static_cast(-1); const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024; -// We fragment sent control frames at smaller payload boundaries. -const size_t SpdyFramer::kMaxControlFrameSize = 1024; +// Even though the length field is 24 bits, we keep this 16 kB +// limit on control frame size for legacy reasons and to +// mitigate DOS attacks. +const size_t SpdyFramer::kMaxControlFrameSize = (1 << 14) - 1; // 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. @@ -137,9 +137,6 @@ const size_t SpdyFramer::kControlFrameBufferSize = 19; SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat(SpdyMajorVersion version, uint32_t wire) { - if (version < SPDY3) { - ConvertFlagsAndIdForSpdy2(&wire); - } return SettingsFlagsAndId(base::NetToHost32(wire) >> 24, base::NetToHost32(wire) & 0x00ffffff); } @@ -150,23 +147,7 @@ SettingsFlagsAndId::SettingsFlagsAndId(uint8_t flags, uint32_t id) } uint32_t SettingsFlagsAndId::GetWireFormat(SpdyMajorVersion version) const { - uint32_t wire = - base::HostToNet32(id_ & 0x00ffffff) | base::HostToNet32(flags_ << 24); - if (version < SPDY3) { - ConvertFlagsAndIdForSpdy2(&wire); - } - return wire; -} - -// SPDY 2 had a bug in it with respect to byte ordering of id/flags field. -// This method is used to preserve buggy behavior and works on both -// little-endian and big-endian hosts. -// This method is also bidirectional (can be used to translate SPDY 2 to SPDY 3 -// as well as vice versa). -void SettingsFlagsAndId::ConvertFlagsAndIdForSpdy2(uint32_t* val) { - uint8_t* wire_array = reinterpret_cast(val); - std::swap(wire_array[0], wire_array[3]); - std::swap(wire_array[1], wire_array[2]); + return base::HostToNet32(id_ & 0x00ffffff) | base::HostToNet32(flags_ << 24); } bool SpdyFramerVisitorInterface::OnGoAwayFrameData(const char* goaway_data, @@ -191,8 +172,7 @@ SpdyFramer::SpdyFramer(SpdyMajorVersion version) syn_frame_processed_(false), probable_http_response_(false), end_stream_when_done_(false) { - DCHECK_GE(protocol_version_, SPDY_MIN_VERSION); - DCHECK_LE(protocol_version_, SPDY_MAX_VERSION); + DCHECK(protocol_version_ == SPDY3 || protocol_version_ == HTTP2); DCHECK_LE(kMaxControlFrameSize, SpdyConstants::GetFrameMaximumSize(protocol_version_) + SpdyConstants::GetControlFrameHeaderSize(protocol_version_)); @@ -225,18 +205,18 @@ void SpdyFramer::Reset() { } size_t SpdyFramer::GetDataFrameMinimumSize() const { - return SpdyConstants::GetDataFrameMinimumSize(protocol_version()); + return SpdyConstants::GetDataFrameMinimumSize(protocol_version_); } // Size, in bytes, of the control frame header. size_t SpdyFramer::GetControlFrameHeaderSize() const { - return SpdyConstants::GetControlFrameHeaderSize(protocol_version()); + return SpdyConstants::GetControlFrameHeaderSize(protocol_version_); } size_t SpdyFramer::GetSynStreamMinimumSize() const { // Size, in bytes, of a SYN_STREAM frame not including the variable-length // header block. - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { // Calculated as: // control frame header + 2 * 4 (stream IDs) + 1 (priority) // + 1 (unused) @@ -252,23 +232,18 @@ size_t SpdyFramer::GetSynReplyMinimumSize() const { // Size, in bytes, of a SYN_REPLY frame not including the variable-length // header block. size_t size = GetControlFrameHeaderSize(); - if (protocol_version() <= SPDY3) { + 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() < SPDY3) { - size += 2; - } - return size; } size_t SpdyFramer::GetRstStreamMinimumSize() const { // Size, in bytes, of a RST_STREAM frame. - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { // Calculated as: // control frame header + 4 (stream id) + 4 (status code) return GetControlFrameHeaderSize() + 8; @@ -283,7 +258,7 @@ 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) - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { return GetControlFrameHeaderSize() + 4; } else { return GetControlFrameHeaderSize(); @@ -292,7 +267,7 @@ size_t SpdyFramer::GetSettingsMinimumSize() const { size_t SpdyFramer::GetPingSize() const { // Size, in bytes, of this PING frame. - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { // Calculated as: // control frame header + 4 (id) return GetControlFrameHeaderSize() + 4; @@ -305,41 +280,26 @@ size_t SpdyFramer::GetPingSize() const { size_t SpdyFramer::GetGoAwayMinimumSize() const { // Size, in bytes, of this GOAWAY frame. Calculated as: - // 1. Control frame header size - size_t size = GetControlFrameHeaderSize(); - - // 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; - } - - return size; + // Control frame header + last stream id (4 bytes) + error code (4 bytes). + return GetControlFrameHeaderSize() + 8; } size_t SpdyFramer::GetHeadersMinimumSize() const { // Size, in bytes, of a HEADERS frame not including the variable-length // header block. size_t size = GetControlFrameHeaderSize(); - if (protocol_version() <= SPDY3) { + 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() <= SPDY2) { - size += 2; - } - return size; } size_t SpdyFramer::GetWindowUpdateSize() const { // Size, in bytes, of a WINDOW_UPDATE frame. - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { // Calculated as: // control frame header + 4 (stream id) + 4 (delta) return GetControlFrameHeaderSize() + 8; @@ -351,14 +311,14 @@ size_t SpdyFramer::GetWindowUpdateSize() const { } size_t SpdyFramer::GetBlockedSize() const { - DCHECK_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, 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_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, 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; @@ -391,7 +351,7 @@ size_t SpdyFramer::GetFrameMinimumSize() const { } size_t SpdyFramer::GetFrameMaximumSize() const { - return SpdyConstants::GetFrameMaximumSize(protocol_version()); + return SpdyConstants::GetFrameMaximumSize(protocol_version_); } size_t SpdyFramer::GetDataFrameMaximumPayload() const { @@ -399,7 +359,7 @@ size_t SpdyFramer::GetDataFrameMaximumPayload() const { } size_t SpdyFramer::GetPrefixLength(SpdyFrameType type) const { - return SpdyConstants::GetPrefixLength(type, protocol_version()); + return SpdyConstants::GetPrefixLength(type, protocol_version_); } const char* SpdyFramer::StateToString(int state) { @@ -430,6 +390,8 @@ const char* SpdyFramer::StateToString(int state) { return "SPDY_GOAWAY_FRAME_PAYLOAD"; case SPDY_RST_STREAM_FRAME_PAYLOAD: return "SPDY_RST_STREAM_FRAME_PAYLOAD"; + case SPDY_SETTINGS_FRAME_HEADER: + return "SPDY_SETTINGS_FRAME_HEADER"; case SPDY_SETTINGS_FRAME_PAYLOAD: return "SPDY_SETTINGS_FRAME_PAYLOAD"; case SPDY_ALTSVC_FRAME_PAYLOAD: @@ -583,23 +545,23 @@ 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, CONTINUATION) - // take a different path through the state machine - they + // take a special path through the state machine - they // will go: // 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK // 2. SPDY_CONTROL_FRAME_HEADER_BLOCK - // - // SETTINGS frames take a slightly modified route: - // 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK - // 2. SPDY_SETTINGS_FRAME_PAYLOAD - // - // All other control frames will use the alternate route directly to - // SPDY_CONTROL_FRAME_PAYLOAD int bytes_read = ProcessControlFrameBeforeHeaderBlock(data, len); len -= bytes_read; data += bytes_read; break; } + case SPDY_SETTINGS_FRAME_HEADER: { + int bytes_read = ProcessSettingsFrameHeader(data, len); + len -= bytes_read; + data += bytes_read; + break; + } + case SPDY_SETTINGS_FRAME_PAYLOAD: { int bytes_read = ProcessSettingsFramePayload(data, len); len -= bytes_read; @@ -609,7 +571,7 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { case SPDY_CONTROL_FRAME_HEADER_BLOCK: { int bytes_read = ProcessControlFrameHeaderBlock( - data, len, protocol_version() > SPDY3); + data, len, protocol_version_ == HTTP2); len -= bytes_read; data += bytes_read; break; @@ -737,26 +699,21 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { bool is_control_frame = false; int control_frame_type_field = - SpdyConstants::DataFrameType(protocol_version()); + SpdyConstants::DataFrameType(protocol_version_); // ProcessControlFrameHeader() will set current_frame_type_ to the // correct value if this is a valid control frame. current_frame_type_ = DATA; - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { uint16_t version = 0; 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 &= ~kControlFlagMask; + if (version != kSpdy3Version) { // Version does not match the version the framer was initialized with. - DVLOG(1) << "Unsupported SPDY version " - << version - << " (expected " << protocol_version() << ")"; + DVLOG(1) << "Unsupported SPDY version " << version << " (expected " + << kSpdy3Version << ")"; set_error(SPDY_UNSUPPORTED_VERSION); return 0; } @@ -790,8 +747,9 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { // We check control_frame_type_field's validity in // ProcessControlFrameHeader(). control_frame_type_field = control_frame_type_field_uint8; - is_control_frame = control_frame_type_field != - SpdyConstants::SerializeFrameType(protocol_version(), DATA); + is_control_frame = + control_frame_type_field != + SpdyConstants::SerializeFrameType(protocol_version_, DATA); if (is_control_frame) { current_frame_length_ = length_field + GetControlFrameHeaderSize(); @@ -809,8 +767,9 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { // 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)); + 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 " @@ -846,18 +805,18 @@ 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 (protocol_version() > SPDY3) { + if (protocol_version_ == HTTP2) { // Catch bogus tests sending oversized DATA frames. DCHECK_GE(GetFrameMaximumSize(), current_frame_length_) << "DATA frame too large for SPDY >= 4."; } uint8_t valid_data_flags = 0; - if (protocol_version() > SPDY3) { + if (protocol_version_ == SPDY3) { + valid_data_flags = DATA_FLAG_FIN; + } else { valid_data_flags = DATA_FLAG_FIN | DATA_FLAG_END_SEGMENT | DATA_FLAG_PADDED; - } else { - valid_data_flags = DATA_FLAG_FIN; } if (current_frame_flags_ & ~valid_data_flags) { @@ -888,11 +847,11 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { DCHECK_EQ(SPDY_NO_ERROR, error_code_); DCHECK_LE(GetControlFrameHeaderSize(), current_frame_buffer_.len()); - if (!SpdyConstants::IsValidFrameType(protocol_version(), + if (!SpdyConstants::IsValidFrameType(protocol_version_, control_frame_type_field)) { - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { DLOG(WARNING) << "Invalid control frame type " << control_frame_type_field - << " (protocol version: " << protocol_version() << ")"; + << " (protocol version: " << protocol_version_ << ")"; set_error(SPDY_INVALID_CONTROL_FRAME); return; } else { @@ -917,7 +876,7 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { } } - current_frame_type_ = SpdyConstants::ParseFrameType(protocol_version(), + current_frame_type_ = SpdyConstants::ParseFrameType(protocol_version_, control_frame_type_field); // Do some sanity checking on the control frame sizes and flags. @@ -941,9 +900,9 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { // TODO(bnc): Enforce the length of the header, and change error to // FRAME_SIZE_ERROR. if ((current_frame_length_ != GetRstStreamMinimumSize() && - protocol_version() <= SPDY3) || + protocol_version_ == SPDY3) || (current_frame_length_ < GetRstStreamMinimumSize() && - protocol_version() > SPDY3)) { + protocol_version_ == HTTP2)) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ != 0) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); @@ -953,23 +912,23 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { { // Make sure that we have an integral number of 8-byte key/value pairs, // plus a 4-byte length field in SPDY3 and below. - size_t values_prefix_size = (protocol_version() <= SPDY3 ? 4 : 0); + size_t values_prefix_size = (protocol_version_ == SPDY3 ? 4 : 0); // Size of each key/value pair in bytes. - size_t setting_size = SpdyConstants::GetSettingSize(protocol_version()); + size_t setting_size = SpdyConstants::GetSettingSize(protocol_version_); if (current_frame_length_ < GetSettingsMinimumSize() || (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 (protocol_version() <= SPDY3 && + } else if (protocol_version_ == SPDY3 && current_frame_flags_ & - ~SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) { + ~SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); - } else if (protocol_version() > SPDY3 && + } else if (protocol_version_ == HTTP2 && current_frame_flags_ & ~SETTINGS_FLAG_ACK) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); - } else if (protocol_version() > SPDY3 && + } else if (protocol_version_ == HTTP2 && current_frame_flags_ & SETTINGS_FLAG_ACK && current_frame_length_ > GetSettingsMinimumSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); @@ -979,21 +938,20 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { case PING: if (current_frame_length_ != GetPingSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); - } else if ((protocol_version() <= SPDY3 && 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: { - // 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)) { + // For SPDY/3, there are only mandatory fields and the header has a + // fixed length. For HTTP/2, optional opaque data may be appended to the + // GOAWAY frame, thus there is only a minimal length restriction. + if ((protocol_version_ == SPDY3 && + current_frame_length_ != GetGoAwayMinimumSize()) || + (protocol_version_ == HTTP2 && + current_frame_length_ < GetGoAwayMinimumSize())) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ != 0) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); @@ -1003,7 +961,7 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { case HEADERS: { size_t min_size = GetHeadersMinimumSize(); - if (protocol_version() > SPDY3 && + if (protocol_version_ == HTTP2 && (current_frame_flags_ & HEADERS_FLAG_PRIORITY)) { min_size += 4; } @@ -1011,10 +969,10 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { // TODO(mlavan): check here for HEADERS with no payload? // (not allowed in HTTP2) set_error(SPDY_INVALID_CONTROL_FRAME); - } else if (protocol_version() <= SPDY3 && + } else if (protocol_version_ == SPDY3 && current_frame_flags_ & ~CONTROL_FLAG_FIN) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); - } else if (protocol_version() > SPDY3 && + } else if (protocol_version_ == HTTP2 && current_frame_flags_ & ~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY | HEADERS_FLAG_END_HEADERS | HEADERS_FLAG_END_SEGMENT | @@ -1031,8 +989,8 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { } break; case BLOCKED: - if (current_frame_length_ != GetBlockedSize() || - protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3 || + current_frame_length_ != GetBlockedSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ != 0) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); @@ -1041,9 +999,9 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { case PUSH_PROMISE: if (current_frame_length_ < GetPushPromiseMinimumSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); - } else if (protocol_version() <= SPDY3 && current_frame_flags_ != 0) { + } else if (protocol_version_ == SPDY3 && current_frame_flags_ != 0) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); - } else if (protocol_version() > SPDY3 && + } else if (protocol_version_ == HTTP2 && current_frame_flags_ & ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE | HEADERS_FLAG_PADDED)) { @@ -1051,8 +1009,8 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { } break; case CONTINUATION: - if (current_frame_length_ < GetContinuationMinimumSize() || - protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3 || + current_frame_length_ < GetContinuationMinimumSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ & ~HEADERS_FLAG_END_HEADERS) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); @@ -1066,8 +1024,8 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { } break; case PRIORITY: - if (current_frame_length_ != GetPrioritySize() || - protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3 || + current_frame_length_ != GetPrioritySize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ != 0) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); @@ -1091,8 +1049,8 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { } if (current_frame_length_ > - SpdyConstants::GetFrameMaximumSize(protocol_version()) + - SpdyConstants::GetControlFrameHeaderSize(protocol_version())) { + SpdyConstants::GetFrameMaximumSize(protocol_version_) + + SpdyConstants::GetControlFrameHeaderSize(protocol_version_)) { DLOG(WARNING) << "Received control frame of type " << current_frame_type_ << " with way too big of a payload: " << current_frame_length_; @@ -1130,7 +1088,7 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { break; case HEADERS: frame_size_without_variable_data = GetHeadersMinimumSize(); - if (protocol_version() > SPDY3) { + if (protocol_version_ == HTTP2) { if (current_frame_flags_ & HEADERS_FLAG_PADDED) { frame_size_without_variable_data += kPadLengthFieldSize; } @@ -1143,7 +1101,7 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { break; case PUSH_PROMISE: frame_size_without_variable_data = GetPushPromiseMinimumSize(); - if (protocol_version() > SPDY3 && + if (protocol_version_ == HTTP2 && current_frame_flags_ & PUSH_PROMISE_FLAG_PADDED) { frame_size_without_variable_data += kPadLengthFieldSize; } @@ -1169,15 +1127,19 @@ void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { } if (frame_size_without_variable_data > 0) { - // We have a control frame with a header block. We need to parse the - // remainder of the control frame's header before we can parse the header - // block. The start of the header block varies with the control type. + // We have a control frame with variable-size data. We need to parse the + // remainder of the control frame's header before we can parse the payload. + // The start of the payload varies with the control frame type. DCHECK_GE(frame_size_without_variable_data, static_cast(current_frame_buffer_.len())); remaining_control_header_ = frame_size_without_variable_data - current_frame_buffer_.len(); - CHANGE_STATE(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK); + if (current_frame_type_ == SETTINGS) { + CHANGE_STATE(SPDY_SETTINGS_FRAME_HEADER); + } else { + CHANGE_STATE(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK); + } return; } @@ -1198,8 +1160,7 @@ size_t SpdyFramer::UpdateCurrentFrameBuffer(const char** data, size_t* len, size_t SpdyFramer::GetSerializedLength( const SpdyMajorVersion spdy_version, const SpdyHeaderBlock* headers) { - const size_t num_name_value_pairs_size = - (spdy_version < SPDY3) ? sizeof(uint16_t) : sizeof(uint32_t); + const size_t num_name_value_pairs_size = sizeof(uint32_t); const size_t length_of_name_size = num_name_value_pairs_size; const size_t length_of_value_size = num_name_value_pairs_size; @@ -1216,20 +1177,11 @@ size_t SpdyFramer::GetSerializedLength( void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame, const SpdyMajorVersion spdy_version, const SpdyHeaderBlock* headers) { - if (spdy_version < SPDY3) { - frame->WriteUInt16(static_cast(headers->size())); - } else { - frame->WriteUInt32(headers->size()); - } + frame->WriteUInt32(headers->size()); SpdyHeaderBlock::const_iterator it; for (it = headers->begin(); it != headers->end(); ++it) { - if (spdy_version < SPDY3) { - frame->WriteStringPiece16(it->first); - frame->WriteStringPiece16(it->second); - } else { - frame->WriteStringPiece32(it->first); - frame->WriteStringPiece32(it->second); - } + frame->WriteStringPiece32(it->first); + frame->WriteStringPiece32(it->second); } } @@ -1307,10 +1259,7 @@ static void WriteLengthZ(size_t n, // cookie data. void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers, z_stream* z) const { - unsigned length_length = 4; - if (protocol_version() < 3) - length_length = 2; - + const size_t length_length = 4; WriteLengthZ(headers->size(), length_length, kZStandardData, z); SpdyHeaderBlock::const_iterator it; @@ -1427,13 +1376,13 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, switch (current_frame_type_) { case SYN_STREAM: { - DCHECK_GE(SPDY3, protocol_version()); + DCHECK_EQ(SPDY3, protocol_version_); bool successful_read = true; successful_read = reader.ReadUInt31(¤t_frame_stream_id_); DCHECK(successful_read); if (current_frame_stream_id_ == 0) { set_error(SPDY_INVALID_CONTROL_FRAME); - break; + return original_len - len; } SpdyStreamId associated_to_stream_id = kInvalidStream; @@ -1443,11 +1392,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, SpdyPriority priority = 0; successful_read = reader.ReadUInt8(&priority); DCHECK(successful_read); - if (protocol_version() <= SPDY2) { - priority = priority >> 6; - } else { - priority = priority >> 5; - } + priority = priority >> 5; // Seek past unused byte. reader.Seek(1); @@ -1466,46 +1411,29 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, (current_frame_flags_ & CONTROL_FLAG_FIN) != 0, (current_frame_flags_ & CONTROL_FLAG_UNIDIRECTIONAL) != 0); } - CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); - break; - case SETTINGS: - if (protocol_version() > SPDY3 && - current_frame_flags_ & SETTINGS_FLAG_ACK) { - visitor_->OnSettingsAck(); - CHANGE_STATE(SPDY_FRAME_COMPLETE); - } else { - visitor_->OnSettings(current_frame_flags_ & - SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS); - CHANGE_STATE(SPDY_SETTINGS_FRAME_PAYLOAD); - } break; case SYN_REPLY: + DCHECK_EQ(SPDY3, protocol_version_); + /* FALLTHROUGH */ 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 (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { successful_read = reader.ReadUInt31(¤t_frame_stream_id_); DCHECK(successful_read); } if (current_frame_stream_id_ == 0) { set_error(SPDY_INVALID_CONTROL_FRAME); - break; - } - if (protocol_version() <= SPDY2) { - // SPDY 2 had two unused bytes here. Seek past them. - reader.Seek(2); + return original_len - len; } - if (protocol_version() > SPDY3 && - !(current_frame_flags_ & HEADERS_FLAG_END_HEADERS) && - current_frame_type_ == HEADERS) { + if (protocol_version_ == HTTP2 && + !(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; } - if (protocol_version() > SPDY3 && + if (protocol_version_ == HTTP2 && current_frame_flags_ & HEADERS_FLAG_PADDED) { uint8_t pad_payload_len = 0; DCHECK_EQ(remaining_padding_payload_length_, 0u); @@ -1518,7 +1446,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, SpdyPriority priority = 0; uint32_t parent_stream_id = 0; bool exclusive = false; - if (protocol_version() > SPDY3 && has_priority) { + if (protocol_version_ == HTTP2 && has_priority) { uint32_t stream_dependency; successful_read = reader.ReadUInt32(&stream_dependency); DCHECK(successful_read); @@ -1551,17 +1479,16 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, expect_continuation_ == 0); } } - CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); break; case PUSH_PROMISE: { - DCHECK_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); if (current_frame_stream_id_ == 0) { set_error(SPDY_INVALID_CONTROL_FRAME); - break; + return original_len - len; } bool successful_read = true; - if (protocol_version() > SPDY3 && + if (protocol_version_ == HTTP2 && current_frame_flags_ & PUSH_PROMISE_FLAG_PADDED) { DCHECK_EQ(remaining_padding_payload_length_, 0u); uint8_t pad_payload_len = 0; @@ -1577,7 +1504,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, DCHECK(reader.IsDoneReading()); if (promised_stream_id == 0) { set_error(SPDY_INVALID_CONTROL_FRAME); - break; + return original_len - len; } if (!(current_frame_flags_ & PUSH_PROMISE_FLAG_END_PUSH_PROMISE)) { expect_continuation_ = current_frame_stream_id_; @@ -1593,7 +1520,6 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, (current_frame_flags_ & PUSH_PROMISE_FLAG_END_PUSH_PROMISE) != 0); } - CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); break; case CONTINUATION: { @@ -1604,7 +1530,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, // that current_frame_stream_id != 0. if (current_frame_stream_id_ != expect_continuation_) { set_error(SPDY_INVALID_CONTROL_FRAME); - break; + return original_len - len; } if (current_frame_flags_ & HEADERS_FLAG_END_HEADERS) { expect_continuation_ = 0; @@ -1619,11 +1545,16 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, (current_frame_flags_ & HEADERS_FLAG_END_HEADERS) != 0); } - CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); break; default: - DCHECK(false); +#ifndef NDEBUG + LOG(FATAL) << "Invalid control frame type: " << current_frame_type_; +#else + set_error(SPDY_INVALID_CONTROL_FRAME); + return original_len - len; +#endif } + CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); } return original_len - len; } @@ -1655,7 +1586,7 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, processed_successfully = false; } } else if (process_bytes > 0) { - if (enable_compression_ && protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3 && enable_compression_) { processed_successfully = IncrementallyDecompressControlFrameHeaderData( current_frame_stream_id_, data, process_bytes); } else { @@ -1702,6 +1633,31 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, return process_bytes; } +size_t SpdyFramer::ProcessSettingsFrameHeader(const char* data, size_t len) { + // TODO(birenroy): Remove this state when removing SPDY3. I think it only + // exists to read the number of settings in the frame for SPDY3. This value + // is never parsed or used. + size_t bytes_read = 0; + if (remaining_control_header_ > 0) { + bytes_read = + UpdateCurrentFrameBuffer(&data, &len, remaining_control_header_); + remaining_control_header_ -= bytes_read; + remaining_data_length_ -= bytes_read; + } + if (remaining_control_header_ == 0) { + if (protocol_version_ == HTTP2 && + current_frame_flags_ & SETTINGS_FLAG_ACK) { + visitor_->OnSettingsAck(); + CHANGE_STATE(SPDY_FRAME_COMPLETE); + } else { + visitor_->OnSettings(current_frame_flags_ & + SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS); + CHANGE_STATE(SPDY_SETTINGS_FRAME_PAYLOAD); + } + } + return bytes_read; +} + size_t SpdyFramer::ProcessSettingsFramePayload(const char* data, size_t data_len) { DCHECK_EQ(SPDY_SETTINGS_FRAME_PAYLOAD, state_); @@ -1709,7 +1665,7 @@ 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 = SpdyConstants::GetSettingSize(protocol_version()); + size_t setting_size = SpdyConstants::GetSettingSize(protocol_version_); // Loop over our incoming data. while (unprocessed_bytes > 0) { @@ -1755,7 +1711,7 @@ size_t SpdyFramer::ProcessSettingsFramePayload(const char* data, } void SpdyFramer::DeliverHpackBlockAsSpdy3Block(size_t compressed_len) { - DCHECK_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); DCHECK_EQ(remaining_padding_payload_length_, remaining_data_length_); const SpdyHeaderBlock& block = GetHpackDecoder()->decoded_block(); @@ -1764,7 +1720,7 @@ void SpdyFramer::DeliverHpackBlockAsSpdy3Block(size_t compressed_len) { ProcessControlFrameHeaderBlock(NULL, 0, false); return; } - size_t payload_len = GetSerializedLength(protocol_version(), &block); + size_t payload_len = GetSerializedLength(protocol_version_, &block); SpdyFrameBuilder builder(payload_len, SPDY3); SerializeHeaderBlockWithoutCompression(&builder, block); @@ -1797,11 +1753,11 @@ bool SpdyFramer::ProcessSetting(const char* data) { // Extract fields. // Maintain behavior of old SPDY 2 bug with byte ordering of flags/id. - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { const uint32_t id_and_flags_wire = *(reinterpret_cast(data)); - SettingsFlagsAndId id_and_flags = - SettingsFlagsAndId::FromWireFormat(protocol_version(), id_and_flags_wire); + SettingsFlagsAndId id_and_flags = SettingsFlagsAndId::FromWireFormat( + protocol_version_, id_and_flags_wire); id_field = id_and_flags.id(); flags = id_and_flags.flags(); value = base::NetToHost32(*(reinterpret_cast(data + 4))); @@ -1811,18 +1767,18 @@ bool SpdyFramer::ProcessSetting(const char* data) { } // Validate id. - if (!SpdyConstants::IsValidSettingId(protocol_version(), id_field)) { + if (!SpdyConstants::IsValidSettingId(protocol_version_, id_field)) { DLOG(WARNING) << "Unknown SETTINGS ID: " << id_field; - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { return false; } else { // In HTTP2 we ignore unknown settings for extensibility. return true; } } - id = SpdyConstants::ParseSettingId(protocol_version(), id_field); + id = SpdyConstants::ParseSettingId(protocol_version_, id_field); - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { // Detect duplicates. if (id <= settings_scratch_.last_setting_id) { DLOG(WARNING) << "Duplicate entry or invalid ordering for id " << id @@ -1861,10 +1817,10 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { switch (current_frame_type_) { case PING: { SpdyPingId id = 0; - bool is_ack = protocol_version() > SPDY3 && - (current_frame_flags_ & PING_FLAG_ACK); + bool is_ack = protocol_version_ == HTTP2 && + (current_frame_flags_ & PING_FLAG_ACK); bool successful_read = true; - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { uint32_t id32 = 0; successful_read = reader.ReadUInt32(&id32); id = id32; @@ -1879,7 +1835,7 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { case WINDOW_UPDATE: { uint32_t delta_window_size = 0; bool successful_read = true; - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { successful_read = reader.ReadUInt31(¤t_frame_stream_id_); DCHECK(successful_read); } @@ -1891,13 +1847,13 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { } break; case BLOCKED: { - DCHECK_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); DCHECK(reader.IsDoneReading()); visitor_->OnBlocked(current_frame_stream_id_); } break; case PRIORITY: { - DCHECK_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); uint32_t stream_dependency; uint32_t parent_stream_id; bool exclusive; @@ -1951,22 +1907,19 @@ size_t SpdyFramer::ProcessGoAwayFramePayload(const char* data, size_t len) { bool successful_read = reader.ReadUInt31(¤t_frame_stream_id_); DCHECK(successful_read); - // In SPDYv3 and up, frames also specify a status code - parse it out. + // Parse status code. SpdyGoAwayStatus status = GOAWAY_OK; - if (protocol_version() >= SPDY3) { - uint32_t 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 { - if (protocol_version() > SPDY3) { - // Treat unrecognized status codes as INTERNAL_ERROR as - // recommended by the HTTP/2 spec. - status = GOAWAY_INTERNAL_ERROR; - } + uint32_t 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 { + if (protocol_version_ == HTTP2) { + // Treat unrecognized status codes as INTERNAL_ERROR as + // recommended by the HTTP/2 spec. + status = GOAWAY_INTERNAL_ERROR; } } // Finished parsing the GOAWAY header, call frame handler. @@ -2015,7 +1968,7 @@ size_t SpdyFramer::ProcessRstStreamFramePayload(const char* data, size_t len) { SpdyFrameReader reader(current_frame_buffer_.data(), current_frame_buffer_.len()); reader.Seek(GetControlFrameHeaderSize()); // Seek past frame header. - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { bool successful_read = reader.ReadUInt31(¤t_frame_stream_id_); DCHECK(successful_read); } @@ -2024,12 +1977,12 @@ size_t SpdyFramer::ProcessRstStreamFramePayload(const char* data, size_t len) { uint32_t status_raw = status; bool successful_read = reader.ReadUInt32(&status_raw); DCHECK(successful_read); - if (SpdyConstants::IsValidRstStreamStatus(protocol_version(), + if (SpdyConstants::IsValidRstStreamStatus(protocol_version_, status_raw)) { status = - SpdyConstants::ParseRstStreamStatus(protocol_version(), status_raw); + SpdyConstants::ParseRstStreamStatus(protocol_version_, status_raw); } else { - if (protocol_version() > SPDY3) { + if (protocol_version_ == HTTP2) { // Treat unrecognized status codes as INTERNAL_ERROR as // recommended by the HTTP/2 spec. status = RST_STREAM_INTERNAL_ERROR; @@ -2141,7 +2094,7 @@ size_t SpdyFramer::ProcessFramePadding(const char* data, size_t len) { 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) { - DCHECK_LE(HTTP2, protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); visitor_->OnStreamPadding(current_frame_stream_id_, amount_to_discard); } data += amount_to_discard; @@ -2209,18 +2162,9 @@ bool SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data, // Read number of headers. uint32_t num_headers; - if (protocol_version() <= SPDY2) { - uint16_t temp; - if (!reader.ReadUInt16(&temp)) { - DVLOG(1) << "Unable to read number of headers."; - return false; - } - num_headers = temp; - } else { - if (!reader.ReadUInt32(&num_headers)) { - DVLOG(1) << "Unable to read number of headers."; - return false; - } + if (!reader.ReadUInt32(&num_headers)) { + DVLOG(1) << "Unable to read number of headers."; + return false; } // Read each header. @@ -2228,8 +2172,7 @@ bool SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data, base::StringPiece temp; // Read header name. - if ((protocol_version() <= SPDY2) ? !reader.ReadStringPiece16(&temp) - : !reader.ReadStringPiece32(&temp)) { + if (!reader.ReadStringPiece32(&temp)) { DVLOG(1) << "Unable to read header name (" << index + 1 << " of " << num_headers << ")."; return false; @@ -2237,8 +2180,7 @@ bool SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data, std::string name = temp.as_string(); // Read header value. - if ((protocol_version() <= SPDY2) ? !reader.ReadStringPiece16(&temp) - : !reader.ReadStringPiece32(&temp)) { + if (!reader.ReadStringPiece32(&temp)) { DVLOG(1) << "Unable to read header value (" << index + 1 << " of " << num_headers << ")."; return false; @@ -2272,7 +2214,14 @@ SpdySerializedFrame* SpdyFramer::SerializeData( flags = DATA_FLAG_FIN; } - if (protocol_version() > SPDY3) { + if (protocol_version_ == SPDY3) { + 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(); + } else { int num_padding_fields = 0; if (data_ir.padded()) { flags |= DATA_FLAG_PADDED; @@ -2282,7 +2231,7 @@ SpdySerializedFrame* SpdyFramer::SerializeData( 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()); + SpdyFrameBuilder builder(size_with_padding, protocol_version_); builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); if (data_ir.padded()) { builder.WriteUInt8(data_ir.padding_payload_len() & 0xff); @@ -2294,13 +2243,6 @@ SpdySerializedFrame* SpdyFramer::SerializeData( } 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(); } } @@ -2313,7 +2255,7 @@ SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField( size_t frame_size = GetDataFrameMinimumSize(); size_t num_padding_fields = 0; - if (protocol_version() > SPDY3) { + if (protocol_version_ == HTTP2) { if (data_ir.padded()) { flags |= DATA_FLAG_PADDED; ++num_padding_fields; @@ -2321,9 +2263,9 @@ SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField( frame_size += num_padding_fields; } - SpdyFrameBuilder builder(frame_size, protocol_version()); + SpdyFrameBuilder builder(frame_size, protocol_version_); builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); - if (protocol_version() > SPDY3) { + if (protocol_version_ == HTTP2) { if (data_ir.padded()) { builder.WriteUInt8(data_ir.padding_payload_len() & 0xff); } @@ -2338,13 +2280,12 @@ SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField( SpdySerializedFrame* SpdyFramer::SerializeSynStream( const SpdySynStreamIR& syn_stream) { - DCHECK_GE(SPDY3, protocol_version()); + DCHECK_EQ(SPDY3, protocol_version_); uint8_t flags = 0; if (syn_stream.fin()) { flags |= CONTROL_FLAG_FIN; } if (syn_stream.unidirectional()) { - // TODO(hkhalil): invalid for HTTP2. flags |= CONTROL_FLAG_UNIDIRECTIONAL; } @@ -2359,18 +2300,18 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream( size_t size = GetSynStreamMinimumSize() + GetSerializedLength(syn_stream.header_block()); - SpdyFrameBuilder builder(size, protocol_version()); + SpdyFrameBuilder builder(size, protocol_version_); 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(priority << 5); builder.WriteUInt8(0); // Unused byte. DCHECK_EQ(GetSynStreamMinimumSize(), builder.length()); SerializeHeaderBlock(&builder, syn_stream); if (debug_visitor_) { const size_t payload_len = - GetSerializedLength(protocol_version(), &(syn_stream.header_block())); + GetSerializedLength(protocol_version_, &(syn_stream.header_block())); debug_visitor_->OnSendCompressedFrame(syn_stream.stream_id(), SYN_STREAM, payload_len, @@ -2382,7 +2323,7 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream( SpdySerializedFrame* SpdyFramer::SerializeSynReply( const SpdySynReplyIR& syn_reply) { - DCHECK_GE(SPDY3, protocol_version()); + DCHECK_EQ(SPDY3, protocol_version_); uint8_t flags = 0; if (syn_reply.fin()) { flags |= CONTROL_FLAG_FIN; @@ -2392,25 +2333,15 @@ SpdySerializedFrame* SpdyFramer::SerializeSynReply( const size_t size = GetSynReplyMinimumSize() + GetSerializedLength(syn_reply.header_block()); - SpdyFrameBuilder builder(size, protocol_version()); - if (protocol_version() <= SPDY3) { - builder.WriteControlFrameHeader(*this, SYN_REPLY, flags); - builder.WriteUInt32(syn_reply.stream_id()); - } else { - builder.BeginNewFrame(*this, - HEADERS, - flags, - syn_reply.stream_id()); - } - if (protocol_version() < SPDY3) { - builder.WriteUInt16(0); // Unused. - } + SpdyFrameBuilder builder(size, protocol_version_); + builder.WriteControlFrameHeader(*this, SYN_REPLY, flags); + builder.WriteUInt32(syn_reply.stream_id()); DCHECK_EQ(GetSynReplyMinimumSize(), builder.length()); SerializeHeaderBlock(&builder, syn_reply); if (debug_visitor_) { const size_t payload_len = - GetSerializedLength(protocol_version(), &(syn_reply.header_block())); + GetSerializedLength(protocol_version_, &(syn_reply.header_block())); debug_visitor_->OnSendCompressedFrame(syn_reply.stream_id(), SYN_REPLY, payload_len, @@ -2428,10 +2359,10 @@ SpdySerializedFrame* SpdyFramer::SerializeRstStream( // commented but left in place to simplify future patching. // Compute the output buffer size, taking opaque data into account. size_t expected_length = GetRstStreamMinimumSize(); - SpdyFrameBuilder builder(expected_length, protocol_version()); + SpdyFrameBuilder builder(expected_length, protocol_version_); // Serialize the RST_STREAM frame. - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { builder.WriteControlFrameHeader(*this, RST_STREAM, 0); builder.WriteUInt32(rst_stream.stream_id()); } else { @@ -2439,7 +2370,7 @@ SpdySerializedFrame* SpdyFramer::SerializeRstStream( } builder.WriteUInt32(SpdyConstants::SerializeRstStreamStatus( - protocol_version(), rst_stream.status())); + protocol_version_, rst_stream.status())); DCHECK_EQ(expected_length, builder.length()); return builder.take(); @@ -2449,7 +2380,7 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( const SpdySettingsIR& settings) const { uint8_t flags = 0; - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { if (settings.clear_settings()) { flags |= SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS; } @@ -2460,23 +2391,23 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( } const SpdySettingsIR::ValueMap* values = &(settings.values()); - size_t setting_size = SpdyConstants::GetSettingSize(protocol_version()); + size_t setting_size = SpdyConstants::GetSettingSize(protocol_version_); // Size, in bytes, of this SETTINGS frame. const size_t size = GetSettingsMinimumSize() + (values->size() * setting_size); - SpdyFrameBuilder builder(size, protocol_version()); - if (protocol_version() <= SPDY3) { + SpdyFrameBuilder builder(size, protocol_version_); + if (protocol_version_ == SPDY3) { builder.WriteControlFrameHeader(*this, SETTINGS, flags); } else { builder.BeginNewFrame(*this, SETTINGS, flags, 0); } // If this is an ACK, payload should be empty. - if (protocol_version() > SPDY3 && settings.is_ack()) { + if (protocol_version_ == HTTP2 && settings.is_ack()) { return builder.take(); } - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { builder.WriteUInt32(values->size()); } DCHECK_EQ(GetSettingsMinimumSize(), builder.length()); @@ -2484,9 +2415,9 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( it != values->end(); ++it) { int setting_id = - SpdyConstants::SerializeSettingId(protocol_version(), it->first); + SpdyConstants::SerializeSettingId(protocol_version_, it->first); DCHECK_GE(setting_id, 0); - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { uint8_t setting_flags = 0; if (it->second.persist_value) { setting_flags |= SETTINGS_FLAG_PLEASE_PERSIST; @@ -2496,7 +2427,7 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( } SettingsFlagsAndId flags_and_id(setting_flags, setting_id); uint32_t id_and_flags_wire = - flags_and_id.GetWireFormat(protocol_version()); + flags_and_id.GetWireFormat(protocol_version_); builder.WriteBytes(&id_and_flags_wire, 4); } else { builder.WriteUInt16(static_cast(setting_id)); @@ -2508,8 +2439,8 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( } SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const { - SpdyFrameBuilder builder(GetPingSize(), protocol_version()); - if (protocol_version() <= SPDY3) { + SpdyFrameBuilder builder(GetPingSize(), protocol_version_); + if (protocol_version_ == SPDY3) { builder.WriteControlFrameHeader(*this, PING, kNoFlags); builder.WriteUInt32(static_cast(ping.id())); } else { @@ -2529,13 +2460,13 @@ SpdySerializedFrame* SpdyFramer::SerializeGoAway( // Compute the output buffer size, take opaque data into account. size_t expected_length = GetGoAwayMinimumSize(); - if (protocol_version() > SPDY3) { + if (protocol_version_ == HTTP2) { expected_length += goaway.description().size(); } - SpdyFrameBuilder builder(expected_length, protocol_version()); + SpdyFrameBuilder builder(expected_length, protocol_version_); // Serialize the GOAWAY frame. - if (protocol_version() <= SPDY3) { + if (protocol_version_ == SPDY3) { builder.WriteControlFrameHeader(*this, GOAWAY, kNoFlags); } else { builder.BeginNewFrame(*this, GOAWAY, 0, 0); @@ -2544,15 +2475,12 @@ SpdySerializedFrame* SpdyFramer::SerializeGoAway( // GOAWAY frames specify the last good stream id for all SPDY versions. builder.WriteUInt32(goaway.last_good_stream_id()); - // 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())); - } + // GOAWAY frames also specify the error status code. + builder.WriteUInt32( + SpdyConstants::SerializeGoAwayStatus(protocol_version_, goaway.status())); - // In HTTP2 and up, GOAWAY frames may also specify opaque data. - if ((protocol_version() > SPDY3) && (goaway.description().size() > 0)) { + // In HTTP2, GOAWAY frames may also specify opaque data. + if ((protocol_version_ == HTTP2) && (goaway.description().size() > 0)) { builder.WriteBytes(goaway.description().data(), goaway.description().size()); } @@ -2567,7 +2495,7 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( if (headers.fin()) { flags |= CONTROL_FLAG_FIN; } - if (protocol_version() > SPDY3) { + if (protocol_version_ == HTTP2) { // This will get overwritten if we overflow into a CONTINUATION frame. flags |= HEADERS_FLAG_END_HEADERS; if (headers.has_priority()) { @@ -2582,7 +2510,7 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( // variable-length header block. size_t size = GetHeadersMinimumSize(); - if (protocol_version() > SPDY3 && headers.padded()) { + if (protocol_version_ == HTTP2 && headers.padded()) { size += kPadLengthFieldSize; size += headers.padding_payload_len(); } @@ -2597,7 +2525,9 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( } string hpack_encoding; - if (protocol_version() > SPDY3) { + if (protocol_version_ == SPDY3) { + size += GetSerializedLength(headers.header_block()); + } else { if (enable_compression_) { GetHpackEncoder()->EncodeHeaderSet(headers.header_block(), &hpack_encoding); @@ -2611,12 +2541,10 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( GetContinuationMinimumSize(); flags &= ~HEADERS_FLAG_END_HEADERS; } - } else { - size += GetSerializedLength(headers.header_block()); } - SpdyFrameBuilder builder(size, protocol_version()); - if (protocol_version() <= SPDY3) { + SpdyFrameBuilder builder(size, protocol_version_); + if (protocol_version_ == SPDY3) { builder.WriteControlFrameHeader(*this, HEADERS, flags); builder.WriteUInt32(headers.stream_id()); } else { @@ -2625,12 +2553,11 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( flags, headers.stream_id()); } - if (protocol_version() <= SPDY2) { - builder.WriteUInt16(0); // Unused. - } DCHECK_EQ(GetHeadersMinimumSize(), builder.length()); - if (protocol_version() > SPDY3) { + if (protocol_version_ == SPDY3) { + SerializeHeaderBlock(&builder, headers); + } else { int padding_payload_len = 0; if (headers.padded()) { builder.WriteUInt8(headers.padding_payload_len()); @@ -2646,8 +2573,6 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( headers.stream_id(), HEADERS, padding_payload_len); - } else { - SerializeHeaderBlock(&builder, headers); } if (debug_visitor_) { @@ -2655,7 +2580,7 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( // use GetSerializedLength() for an apples-to-apples comparision of // compression performance between HPACK and SPDY w/ deflate. const size_t payload_len = - GetSerializedLength(protocol_version(), &(headers.header_block())); + GetSerializedLength(protocol_version_, &(headers.header_block())); debug_visitor_->OnSendCompressedFrame(headers.stream_id(), HEADERS, payload_len, @@ -2667,8 +2592,8 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( SpdySerializedFrame* SpdyFramer::SerializeWindowUpdate( const SpdyWindowUpdateIR& window_update) const { - SpdyFrameBuilder builder(GetWindowUpdateSize(), protocol_version()); - if (protocol_version() <= SPDY3) { + SpdyFrameBuilder builder(GetWindowUpdateSize(), protocol_version_); + if (protocol_version_ == SPDY3) { builder.WriteControlFrameHeader(*this, WINDOW_UPDATE, kNoFlags); builder.WriteUInt32(window_update.stream_id()); } else { @@ -2683,15 +2608,15 @@ SpdySerializedFrame* SpdyFramer::SerializeWindowUpdate( } SpdyFrame* SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked) const { - DCHECK_LT(SPDY3, protocol_version()); - SpdyFrameBuilder builder(GetBlockedSize(), protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); + SpdyFrameBuilder builder(GetBlockedSize(), protocol_version_); builder.BeginNewFrame(*this, BLOCKED, kNoFlags, blocked.stream_id()); return builder.take(); } SpdyFrame* SpdyFramer::SerializePushPromise( const SpdyPushPromiseIR& push_promise) { - DCHECK_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); uint8_t flags = 0; // This will get overwritten if we overflow into a CONTINUATION frame. flags |= PUSH_PROMISE_FLAG_END_PUSH_PROMISE; @@ -2719,7 +2644,7 @@ SpdyFrame* SpdyFramer::SerializePushPromise( flags &= ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE; } - SpdyFrameBuilder builder(size, protocol_version()); + SpdyFrameBuilder builder(size, protocol_version_); builder.BeginNewFrame(*this, PUSH_PROMISE, flags, @@ -2748,7 +2673,7 @@ SpdyFrame* SpdyFramer::SerializePushPromise( // use GetSerializedLength() for an apples-to-apples comparision of // compression performance between HPACK and SPDY w/ deflate. const size_t payload_len = - GetSerializedLength(protocol_version(), &(push_promise.header_block())); + GetSerializedLength(protocol_version_, &(push_promise.header_block())); debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(), PUSH_PROMISE, payload_len, @@ -2763,7 +2688,7 @@ SpdyFrame* SpdyFramer::SerializePushPromise( // new one. Figure out whether it makes sense to keep SerializeContinuation(). SpdyFrame* SpdyFramer::SerializeContinuation( const SpdyContinuationIR& continuation) { - CHECK_LT(SPDY3, protocol_version()); + CHECK_EQ(HTTP2, protocol_version_); uint8_t flags = 0; if (continuation.end_headers()) { flags |= HEADERS_FLAG_END_HEADERS; @@ -2781,7 +2706,7 @@ SpdyFrame* SpdyFramer::SerializeContinuation( } size += hpack_encoding.size(); - SpdyFrameBuilder builder(size, protocol_version()); + SpdyFrameBuilder builder(size, protocol_version_); builder.BeginNewFrame(*this, CONTINUATION, flags, continuation.stream_id()); DCHECK_EQ(GetContinuationMinimumSize(), builder.length()); @@ -2791,7 +2716,7 @@ SpdyFrame* SpdyFramer::SerializeContinuation( } SpdyFrame* SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir) { - DCHECK_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); size_t size = GetAltSvcMinimumSize(); size += altsvc_ir.origin().length(); @@ -2799,7 +2724,7 @@ SpdyFrame* SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir) { altsvc_ir.altsvc_vector()); size += value.length(); - SpdyFrameBuilder builder(size, protocol_version()); + SpdyFrameBuilder builder(size, protocol_version_); builder.BeginNewFrame(*this, ALTSVC, kNoFlags, altsvc_ir.stream_id()); builder.WriteUInt16(altsvc_ir.origin().length()); @@ -2810,10 +2735,10 @@ SpdyFrame* SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir) { } SpdyFrame* SpdyFramer::SerializePriority(const SpdyPriorityIR& priority) const { - DCHECK_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); size_t size = GetPrioritySize(); - SpdyFrameBuilder builder(size, protocol_version()); + SpdyFrameBuilder builder(size, protocol_version_); builder.BeginNewFrame(*this, PRIORITY, kNoFlags, priority.stream_id()); builder.WriteUInt32(PackStreamDependencyValues(priority.exclusive(), @@ -2889,9 +2814,8 @@ 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; } @@ -2902,7 +2826,7 @@ size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock& headers) { } size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) { - DCHECK_GT(protocol_version(), SPDY3); + DCHECK_EQ(HTTP2, protocol_version_); DCHECK_GT(size, kMaxControlFrameSize); size_t overflow = size - kMaxControlFrameSize; size_t payload_size = kMaxControlFrameSize - GetContinuationMinimumSize(); @@ -2990,10 +2914,8 @@ z_stream* SpdyFramer::GetHeaderCompressor() { kCompressorMemLevel, Z_DEFAULT_STRATEGY); if (success == Z_OK) { - const char* dictionary = (protocol_version() <= SPDY2) ? - kV2Dictionary : kV3Dictionary; - const int dictionary_size = (protocol_version() <= SPDY2) ? - kV2DictionarySize : kV3DictionarySize; + const char* dictionary = kV3Dictionary; + const int dictionary_size = kV3DictionarySize; success = deflateSetDictionary(header_compressor_.get(), reinterpret_cast(dictionary), dictionary_size); @@ -3024,7 +2946,7 @@ z_stream* SpdyFramer::GetHeaderDecompressor() { } HpackEncoder* SpdyFramer::GetHpackEncoder() { - DCHECK_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); if (hpack_encoder_.get() == nullptr) { hpack_encoder_.reset(new HpackEncoder(ObtainHpackHuffmanTable())); } @@ -3032,9 +2954,9 @@ HpackEncoder* SpdyFramer::GetHpackEncoder() { } HpackDecoder* SpdyFramer::GetHpackDecoder() { - DCHECK_LT(SPDY3, protocol_version()); + DCHECK_EQ(HTTP2, protocol_version_); if (hpack_decoder_.get() == nullptr) { - hpack_decoder_.reset(new HpackDecoder(ObtainHpackHuffmanTable())); + hpack_decoder_.reset(new HpackDecoder()); } return hpack_decoder_.get(); } @@ -3080,13 +3002,10 @@ bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData( int rv = inflate(decomp, Z_SYNC_FLUSH); if (rv == Z_NEED_DICT) { - const char* dictionary = (protocol_version() <= SPDY2) ? kV2Dictionary - : kV3Dictionary; - const int dictionary_size = (protocol_version() <= SPDY2) ? - kV2DictionarySize : kV3DictionarySize; + const char* dictionary = kV3Dictionary; + const int dictionary_size = kV3DictionarySize; const DictionaryIds& ids = g_dictionary_ids.Get(); - const uLong dictionary_id = (protocol_version() <= SPDY2) ? - ids.v2_dictionary_id : ids.v3_dictionary_id; + const uLong dictionary_id = ids.v3_dictionary_id; // Need to try again with the right dictionary. if (decomp->adler == dictionary_id) { rv = inflateSetDictionary(decomp, @@ -3157,27 +3076,17 @@ void SpdyFramer::SerializeHeaderBlockWithoutCompression( SpdyFrameBuilder* builder, const SpdyHeaderBlock& header_block) const { // Serialize number of headers. - if (protocol_version() <= SPDY2) { - builder->WriteUInt16(static_cast(header_block.size())); - } else { - builder->WriteUInt32(header_block.size()); - } + builder->WriteUInt32(header_block.size()); // Serialize each header. for (const auto& header : header_block) { - if (protocol_version() <= SPDY2) { - builder->WriteStringPiece16(header.first); - builder->WriteStringPiece16(header.second); - } else { - builder->WriteStringPiece32(header.first); - builder->WriteStringPiece32(header.second); - } + builder->WriteStringPiece32(header.first); + builder->WriteStringPiece32(header.second); } } void SpdyFramer::SerializeHeaderBlock(SpdyFrameBuilder* builder, const SpdyFrameWithHeaderBlockIR& frame) { - CHECK_GE(SPDY3, protocol_version()); if (!enable_compression_) { return SerializeHeaderBlockWithoutCompression(builder, frame.header_block()); @@ -3185,8 +3094,8 @@ void SpdyFramer::SerializeHeaderBlock(SpdyFrameBuilder* builder, // First build an uncompressed version to be fed into the compressor. const size_t uncompressed_len = - GetSerializedLength(protocol_version(), &(frame.header_block())); - SpdyFrameBuilder uncompressed_builder(uncompressed_len, protocol_version()); + GetSerializedLength(protocol_version_, &(frame.header_block())); + SpdyFrameBuilder uncompressed_builder(uncompressed_len, protocol_version_); SerializeHeaderBlockWithoutCompression(&uncompressed_builder, frame.header_block()); scoped_ptr uncompressed_payload(uncompressed_builder.take()); diff --git a/src/net/spdy/spdy_framer.h b/src/net/spdy/spdy_framer.h index 13b243f8..ac8543a8 100644 --- a/src/net/spdy/spdy_framer.h +++ b/src/net/spdy/spdy_framer.h @@ -20,9 +20,7 @@ #include "net/spdy/hpack/hpack_decoder.h" #include "net/spdy/hpack/hpack_encoder.h" #include "net/spdy/spdy_alt_svc_wire_format.h" -#if 0 #include "net/spdy/spdy_header_block.h" -#endif #include "net/spdy/spdy_protocol.h" typedef struct z_stream_s z_stream; // Forward declaration for zlib. @@ -66,8 +64,6 @@ class NET_EXPORT_PRIVATE SettingsFlagsAndId { uint8_t flags() const { return flags_; } private: - static void ConvertFlagsAndIdForSpdy2(uint32_t* val); - uint8_t flags_; uint32_t id_; }; @@ -199,13 +195,13 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface { // OnControlFrameHeaderData(). // |stream_id| The stream receiving the header. // |has_priority| Whether or not the headers frame included a priority value, - // and, if protocol version >= HTTP2, stream dependency info. - // |priority| If |has_priority| is true and protocol version > SPDY3, - // priority value for the receiving stream, else 0. + // and, if protocol version == HTTP2, stream dependency info. + // |priority| If |has_priority| is true, then priority value for the receiving + // stream, otherwise 0. // |parent_stream_id| If |has_priority| is true and protocol - // version >= HTTP2, the parent stream of the receiving stream, else 0. + // version == HTTP2, the parent stream of the receiving stream, else 0. // |exclusive| If |has_priority| is true and protocol - // version >= HTTP2, the exclusivity of dependence on the parent stream, + // version == HTTP2, the exclusivity of dependence on the parent stream, // else false. // |fin| Whether FIN flag is set in frame headers. // |end| False if HEADERs frame is to be followed by a CONTINUATION frame, @@ -321,6 +317,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { SPDY_CONTROL_FRAME_HEADER_BLOCK, SPDY_GOAWAY_FRAME_PAYLOAD, SPDY_RST_STREAM_FRAME_PAYLOAD, + SPDY_SETTINGS_FRAME_HEADER, SPDY_SETTINGS_FRAME_PAYLOAD, SPDY_ALTSVC_FRAME_PAYLOAD, }; @@ -550,9 +547,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { bool probable_http_response() const { return probable_http_response_; } - SpdyPriority GetLowestPriority() const { - return protocol_version_ < SPDY3 ? 3 : 7; - } + SpdyPriority GetLowestPriority() const { return 7; } SpdyPriority GetHighestPriority() const { return 0; } @@ -635,6 +630,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { 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 ProcessSettingsFrameHeader(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); diff --git a/src/net/spdy/spdy_header_block.cc b/src/net/spdy/spdy_header_block.cc index c312bed7..c0765ee0 100644 --- a/src/net/spdy/spdy_header_block.cc +++ b/src/net/spdy/spdy_header_block.cc @@ -114,6 +114,9 @@ SpdyHeaderBlock::StringPieceProxy::StringPieceProxy( lookup_result_(lookup_result), key_(key) {} +SpdyHeaderBlock::StringPieceProxy::StringPieceProxy( + const StringPieceProxy& other) = default; + SpdyHeaderBlock::StringPieceProxy::~StringPieceProxy() {} SpdyHeaderBlock::StringPieceProxy& SpdyHeaderBlock::StringPieceProxy::operator=( diff --git a/src/net/spdy/spdy_header_block.h b/src/net/spdy/spdy_header_block.h index 686b65c0..58d6cf4c 100644 --- a/src/net/spdy/spdy_header_block.h +++ b/src/net/spdy/spdy_header_block.h @@ -35,7 +35,9 @@ using ::operator<<; // It's expected that keys are rarely deleted from a SpdyHeaderBlock. class NET_EXPORT SpdyHeaderBlock { private: - using MapType = linked_hash_map; + using MapType = linked_hash_map; class Storage; public: @@ -87,6 +89,7 @@ class NET_EXPORT SpdyHeaderBlock { class NET_EXPORT StringPieceProxy { public: ~StringPieceProxy(); + StringPieceProxy(const StringPieceProxy& other); // Assignment modifies the underlying SpdyHeaderBlock. StringPieceProxy& operator=(const base::StringPiece other); @@ -126,12 +129,10 @@ class NET_EXPORT SpdyHeaderBlock { scoped_ptr storage_; }; -#if 0 // Converts a SpdyHeaderBlock into NetLog event parameters. NET_EXPORT scoped_ptr SpdyHeaderBlockNetLogCallback( const SpdyHeaderBlock* headers, NetLogCaptureMode capture_mode); -#endif // Converts NetLog event parameters into a SPDY header block and writes them // to |headers|. |event_param| must have been created by diff --git a/src/net/spdy/spdy_headers_handler_interface.h b/src/net/spdy/spdy_headers_handler_interface.h index 55431947..f15ea8e7 100644 --- a/src/net/spdy/spdy_headers_handler_interface.h +++ b/src/net/spdy/spdy_headers_handler_interface.h @@ -28,7 +28,7 @@ class SpdyHeadersHandlerInterface { // A callback method which notifies when the parser finishes handling a // header block fragment. Also indicates the total number of bytes in this // block. - virtual void OnHeaderBlockEnd(size_t header_bytes_parsed) = 0; + virtual void OnHeaderBlockEnd(size_t uncompressed_header_bytes) = 0; }; } // namespace net diff --git a/src/net/spdy/spdy_protocol.cc b/src/net/spdy/spdy_protocol.cc index 1ef08b24..f69e02c1 100644 --- a/src/net/spdy/spdy_protocol.cc +++ b/src/net/spdy/spdy_protocol.cc @@ -9,7 +9,6 @@ namespace net { 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)) { @@ -49,7 +48,6 @@ bool SpdyConstants::IsValidFrameType(SpdyMajorVersion version, SpdyFrameType SpdyConstants::ParseFrameType(SpdyMajorVersion version, int frame_type_field) { switch (version) { - case SPDY2: case SPDY3: switch (frame_type_field) { case 1: @@ -107,7 +105,6 @@ SpdyFrameType SpdyConstants::ParseFrameType(SpdyMajorVersion version, int SpdyConstants::SerializeFrameType(SpdyMajorVersion version, SpdyFrameType frame_type) { switch (version) { - case SPDY2: case SPDY3: switch (frame_type) { case SYN_STREAM: @@ -169,7 +166,6 @@ int SpdyConstants::SerializeFrameType(SpdyMajorVersion version, int SpdyConstants::DataFrameType(SpdyMajorVersion version) { switch (version) { - case SPDY2: case SPDY3: return 0; case HTTP2: @@ -183,7 +179,6 @@ int SpdyConstants::DataFrameType(SpdyMajorVersion version) { 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 < @@ -221,7 +216,6 @@ bool SpdyConstants::IsValidSettingId(SpdyMajorVersion version, SpdySettingsIds SpdyConstants::ParseSettingId(SpdyMajorVersion version, int setting_id_field) { switch (version) { - case SPDY2: case SPDY3: switch (setting_id_field) { case 1: @@ -265,7 +259,6 @@ SpdySettingsIds SpdyConstants::ParseSettingId(SpdyMajorVersion version, int SpdyConstants::SerializeSettingId(SpdyMajorVersion version, SpdySettingsIds id) { switch (version) { - case SPDY2: case SPDY3: switch (id) { case SETTINGS_UPLOAD_BANDWIDTH: @@ -312,7 +305,6 @@ int SpdyConstants::SerializeSettingId(SpdyMajorVersion version, 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 < @@ -361,7 +353,6 @@ SpdyRstStreamStatus SpdyConstants::ParseRstStreamStatus( SpdyMajorVersion version, int rst_stream_status_field) { switch (version) { - case SPDY2: case SPDY3: switch (rst_stream_status_field) { case 1: @@ -422,7 +413,6 @@ int SpdyConstants::SerializeRstStreamStatus( SpdyMajorVersion version, SpdyRstStreamStatus rst_stream_status) { switch (version) { - case SPDY2: case SPDY3: switch (rst_stream_status) { case RST_STREAM_PROTOCOL_ERROR: @@ -487,7 +477,6 @@ int SpdyConstants::SerializeRstStreamStatus( 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)) { @@ -523,7 +512,6 @@ bool SpdyConstants::IsValidGoAwayStatus(SpdyMajorVersion version, SpdyGoAwayStatus SpdyConstants::ParseGoAwayStatus(SpdyMajorVersion version, int goaway_status_field) { switch (version) { - case SPDY2: case SPDY3: switch (goaway_status_field) { case 0: @@ -575,7 +563,6 @@ SpdyGoAwayStatus SpdyConstants::ParseGoAwayStatus(SpdyMajorVersion version, int SpdyConstants::SerializeGoAwayStatus(SpdyMajorVersion version, SpdyGoAwayStatus status) { switch (version) { - case SPDY2: case SPDY3: // TODO(jgraettinger): Merge this back to server-side. switch (status) { @@ -640,7 +627,6 @@ int SpdyConstants::SerializeGoAwayStatus(SpdyMajorVersion version, size_t SpdyConstants::GetDataFrameMinimumSize(SpdyMajorVersion version) { switch (version) { - case SPDY2: case SPDY3: return 8; case HTTP2: @@ -652,7 +638,6 @@ size_t SpdyConstants::GetDataFrameMinimumSize(SpdyMajorVersion version) { size_t SpdyConstants::GetControlFrameHeaderSize(SpdyMajorVersion version) { switch (version) { - case SPDY2: case SPDY3: return 8; case HTTP2: @@ -672,9 +657,9 @@ size_t SpdyConstants::GetPrefixLength(SpdyFrameType type, } size_t SpdyConstants::GetFrameMaximumSize(SpdyMajorVersion version) { - if (version < HTTP2) { + if (version == SPDY3) { // 24-bit length field plus eight-byte frame header. - return ((1<<24) - 1) + 8; + return ((1 << 24) - 1) + 8; } else { // Max payload of 2^14 plus nine-byte frame header. // TODO(mlavan): In HTTP/2 this is actually not a constant; @@ -684,54 +669,24 @@ size_t SpdyConstants::GetFrameMaximumSize(SpdyMajorVersion version) { } } -size_t SpdyConstants::GetSizeOfSizeField(SpdyMajorVersion version) { - return (version < SPDY3) ? sizeof(uint16_t) : sizeof(uint32_t); +size_t SpdyConstants::GetSizeOfSizeField() { + return sizeof(uint32_t); } size_t SpdyConstants::GetSettingSize(SpdyMajorVersion version) { - return version <= SPDY3 ? 8 : 6; + return version == SPDY3 ? 8 : 6; } int32_t SpdyConstants::GetInitialStreamWindowSize(SpdyMajorVersion version) { - return (version <= SPDY3) ? (64 * 1024) : (64 * 1024 - 1); + return (version == SPDY3) ? (64 * 1024) : (64 * 1024 - 1); } int32_t SpdyConstants::GetInitialSessionWindowSize(SpdyMajorVersion version) { - return (version <= SPDY3) ? (64 * 1024) : (64 * 1024 - 1); -} - -SpdyMajorVersion SpdyConstants::ParseMajorVersion(int version_number) { - switch (version_number) { - case 2: - return SPDY2; - case 3: - return SPDY3; - case 4: - return HTTP2; - 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 HTTP2: - return 4; - default: - LOG(DFATAL) << "Unsupported SPDY major version: " << version; - return -1; - } + return (version == SPDY3) ? (64 * 1024) : (64 * 1024 - 1); } std::string SpdyConstants::GetVersionString(SpdyMajorVersion version) { switch (version) { - case SPDY2: - return "spdy/2"; case SPDY3: return "spdy/3"; case HTTP2: diff --git a/src/net/spdy/spdy_protocol.h b/src/net/spdy/spdy_protocol.h index 1a39fee0..2f2d06de 100644 --- a/src/net/spdy/spdy_protocol.h +++ b/src/net/spdy/spdy_protocol.h @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This file contains some protocol structures for use with SPDY 2 and 3 -// The SPDY 2 spec can be found at: -// http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2 +// This file contains some protocol structures for use with SPDY 3 and HTTP 2 // The SPDY 3 spec can be found at: // http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3 @@ -33,16 +31,16 @@ 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. Do not rely on -// the mapping from enum value SPDYn to the integer n. +// which indicate application-layer incompatibility. It is NOT guaranteed +// that the enum value SPDYn maps to the integer n. enum SpdyMajorVersion { - SPDY2 = 2, - SPDY_MIN_VERSION = SPDY2, - SPDY3 = 3, - HTTP2 = 4, - SPDY_MAX_VERSION = HTTP2 + SPDY3 = 1, + HTTP2, }; +// 15 bit version field for SPDY/3 frames. +const uint16_t kSpdy3Version = 3; + // A SPDY stream id is a 31 bit entity. typedef uint32_t SpdyStreamId; @@ -62,24 +60,6 @@ const int32_t kSpdyMaximumWindowSize = 0x7FFFFFFF; // Max signed 32bit int // Maximum padding size in octets for one DATA or HEADERS or PUSH_PROMISE frame. const int32_t kPaddingSizePerFrame = 256; -// SPDY 2 dictionary. -// This is just a hacked dictionary to use for shrinking HTTP-like headers. -const char kV2Dictionary[] = - "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-" - "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi" - "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser" - "-agent10010120020120220320420520630030130230330430530630740040140240340440" - "5406407408409410411412413414415416417500501502503504505accept-rangesageeta" - "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic" - "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran" - "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati" - "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo" - "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe" - "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic" - "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1" - ".1statusversionurl"; -const int kV2DictionarySize = arraysize(kV2Dictionary); - // SPDY 3 dictionary. const char kV3Dictionary[] = { 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // ....opti @@ -418,8 +398,6 @@ enum SpdyGoAwayStatus { }; // A SPDY priority is a number between 0 and 7 (inclusive). -// SPDY priority range is version-dependent. For SPDY 2 and below, priority is a -// number between 0 and 3. typedef uint8_t SpdyPriority; // Lowest and Highest here refer to SPDY priorities as described in @@ -529,9 +507,8 @@ class NET_EXPORT_PRIVATE SpdyConstants { 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); + // Returns the size of a header block size field. Valid only for SPDY 3. + static size_t GetSizeOfSizeField(); // Returns the size (in bytes) of a wire setting ID and value. static size_t GetSettingSize(SpdyMajorVersion version); @@ -542,10 +519,6 @@ class NET_EXPORT_PRIVATE SpdyConstants { // Initial window size for a session in bytes. static int32_t GetInitialSessionWindowSize(SpdyMajorVersion version); - static SpdyMajorVersion ParseMajorVersion(int version_number); - - static int SerializeMajorVersion(SpdyMajorVersion version); - static std::string GetVersionString(SpdyMajorVersion version); }; diff --git a/src/net/spdy/write_blocked_list.h b/src/net/spdy/write_blocked_list.h deleted file mode 100644 index 76d22da2..00000000 --- a/src/net/spdy/write_blocked_list.h +++ /dev/null @@ -1,194 +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_SPDY_WRITE_BLOCKED_LIST_H_ -#define NET_SPDY_WRITE_BLOCKED_LIST_H_ - -#include - -#include -#include - -#include "base/containers/hash_tables.h" -#include "base/logging.h" -#include "net/spdy/spdy_protocol.h" - -namespace net { - -namespace test { -class WriteBlockedListPeer; -} // namespace test - -template -class WriteBlockedList { - public: - // 0(1) size lookup. 0(1) insert at front or back. - typedef std::deque BlockedList; - typedef typename BlockedList::iterator iterator; - - WriteBlockedList() {} - - static SpdyPriority ClampPriority(SpdyPriority priority) { - if (priority < kV3HighestPriority) { - LOG(DFATAL) << "Invalid priority: " << static_cast(priority); - return kV3HighestPriority; - } - if (priority > kV3LowestPriority) { - LOG(DFATAL) << "Invalid priority: " << static_cast(priority); - return kV3LowestPriority; - } - return priority; - } - - // Returns the priority of the highest priority list with sessions on it. - SpdyPriority GetHighestPriorityWriteBlockedList() const { - for (SpdyPriority i = 0; i <= kV3LowestPriority; ++i) { - if (write_blocked_lists_[i].size() > 0) { - return i; - } - } - LOG(DFATAL) << "No blocked streams"; - return kV3HighestPriority; - } - - IdType PopFront(SpdyPriority priority) { - priority = ClampPriority(priority); - DCHECK(!write_blocked_lists_[priority].empty()); - IdType stream_id = write_blocked_lists_[priority].front(); - write_blocked_lists_[priority].pop_front(); - stream_to_priority_.erase(stream_id); - return stream_id; - } - - bool HasWriteBlockedStreamsGreaterThanPriority(SpdyPriority priority) const { - priority = ClampPriority(priority); - for (SpdyPriority i = kV3HighestPriority; i < priority; ++i) { - if (!write_blocked_lists_[i].empty()) { - return true; - } - } - return false; - } - - bool HasWriteBlockedStreams() const { - for (SpdyPriority i = kV3HighestPriority; i <= kV3LowestPriority; ++i) { - if (!write_blocked_lists_[i].empty()) { - return true; - } - } - return false; - } - - // Add this stream to the back of the write blocked list for this priority - // level. If the stream is already on that write blocked list this is a - // no-op. If the stream is on a write blocked list for a different priority - // it will be removed from that list. - void PushBack(IdType stream_id, SpdyPriority priority) { - AddStream(stream_id, priority, true); - } - - // Add this stream to the front of the write blocked list for this priority - // level. If the stream is already on that write blocked list this is a - // no-op. If the stream is on a write blocked list for a different priority - // it will be removed from that list. - void PushFront(IdType stream_id, SpdyPriority priority) { - AddStream(stream_id, priority, false); - } - - bool RemoveStreamFromWriteBlockedList(IdType stream_id, - SpdyPriority priority) { - typename StreamToPriorityMap::iterator iter = - stream_to_priority_.find(stream_id); - if (iter == stream_to_priority_.end()) { - // The stream is not present in the write blocked list. - return false; - } else if (iter->second == priority) { - stream_to_priority_.erase(iter); - } else { - // The stream is not present at the specified priority level. - return false; - } - // We shouldn't really add a stream_id to a list multiple times, - // but under some conditions it does happen. Doing a check in PushBack - // would be too costly, so instead we check here to eliminate duplicates. - bool found = false; - iterator it = std::find(write_blocked_lists_[priority].begin(), - write_blocked_lists_[priority].end(), stream_id); - while (it != write_blocked_lists_[priority].end()) { - found = true; - iterator next_it = write_blocked_lists_[priority].erase(it); - it = std::find(next_it, write_blocked_lists_[priority].end(), stream_id); - } - return found; - } - - void UpdateStreamPriorityInWriteBlockedList(IdType stream_id, - SpdyPriority old_priority, - SpdyPriority new_priority) { - if (old_priority == new_priority) { - return; - } - bool found = RemoveStreamFromWriteBlockedList(stream_id, old_priority); - if (found) { - PushBack(stream_id, new_priority); - } - } - - size_t NumBlockedStreams() const { - size_t num_blocked_streams = 0; - for (SpdyPriority i = kV3HighestPriority; i <= kV3LowestPriority; ++i) { - num_blocked_streams += write_blocked_lists_[i].size(); - } - return num_blocked_streams; - } - - size_t NumBlockedStreams(SpdyPriority priority) const { - priority = ClampPriority(priority); - return write_blocked_lists_[priority].size(); - } - - private: - friend class net::test::WriteBlockedListPeer; - - typedef base::hash_map StreamToPriorityMap; - - void AddStream(IdType stream_id, SpdyPriority priority, bool push_back) { - priority = ClampPriority(priority); - DVLOG(2) << "Adding stream " << stream_id << " at priority " - << static_cast(priority); - bool should_insert_stream = true; - typename StreamToPriorityMap::iterator iter = - stream_to_priority_.find(stream_id); - // Ensure the stream is not in the write blocked list multiple times. - if (iter != stream_to_priority_.end()) { - DVLOG(1) << "Stream " << stream_id << " already in write blocked list."; - if (iter->second == priority) { - // The stream is already in the write blocked list for the priority. - // It will not be inserted again but will retain its place in the list. - should_insert_stream = false; - } else { - // The stream is in a write blocked list for a different priority. - // Remove it from that list and allow it to be added to the list for - // this priority. - bool removed = - RemoveStreamFromWriteBlockedList(stream_id, iter->second); - DCHECK(removed); - } - } - if (should_insert_stream) { - stream_to_priority_[stream_id] = priority; - if (push_back) { - write_blocked_lists_[priority].push_back(stream_id); - } else { - write_blocked_lists_[priority].push_front(stream_id); - } - } - } - BlockedList write_blocked_lists_[kV3LowestPriority + 1]; - StreamToPriorityMap stream_to_priority_; -}; - -} // namespace net - -#endif // NET_SPDY_WRITE_BLOCKED_LIST_H_ diff --git a/src/third_party/zlib/x86.c b/src/third_party/zlib/x86.c index 0649306f..e6532fd1 100644 --- a/src/third_party/zlib/x86.c +++ b/src/third_party/zlib/x86.c @@ -56,43 +56,21 @@ static void _x86_check_features(void) #else #include #include -#include -static volatile int32_t once_control = 0; -static void _x86_check_features(void); -static int fake_pthread_once(volatile int32_t *once_control, - void (*init_routine)(void)); +static BOOL CALLBACK _x86_check_features(PINIT_ONCE once, + PVOID param, + PVOID *context); +static INIT_ONCE cpu_check_inited_once = INIT_ONCE_STATIC_INIT; void x86_check_features(void) { - fake_pthread_once(&once_control, _x86_check_features); -} - -/* Copied from "perftools_pthread_once" in tcmalloc */ -static int fake_pthread_once(volatile int32_t *once_control, - void (*init_routine)(void)) { - // Try for a fast path first. Note: this should be an acquire semantics read - // It is on x86 and x64, where Windows runs. - if (*once_control != 1) { - while (1) { - switch (InterlockedCompareExchange(once_control, 2, 0)) { - case 0: - init_routine(); - InterlockedExchange(once_control, 1); - return 0; - case 1: - // The initializer has already been executed - return 0; - default: - // The initializer is being processed by another thread - SwitchToThread(); - } - } - } - return 0; + InitOnceExecuteOnce(&cpu_check_inited_once, _x86_check_features, + NULL, NULL); } -static void _x86_check_features(void) +static BOOL CALLBACK _x86_check_features(PINIT_ONCE once, + PVOID param, + PVOID *context) { int x86_cpu_has_sse2; int x86_cpu_has_sse42; @@ -108,5 +86,6 @@ static void _x86_check_features(void) x86_cpu_enable_simd = x86_cpu_has_sse2 && x86_cpu_has_sse42 && x86_cpu_has_pclmulqdq; + return TRUE; } #endif /* _MSC_VER */ diff --git a/src/url/gurl.cc b/src/url/gurl.cc index b75bb4fd..2b67beeb 100644 --- a/src/url/gurl.cc +++ b/src/url/gurl.cc @@ -78,16 +78,16 @@ GURL::GURL(const GURL& other) DCHECK(!is_valid_ || !SchemeIsFileSystem() || inner_url_); } -GURL::GURL(const std::string& url_string) { +GURL::GURL(base::StringPiece url_string) { InitCanonical(url_string, true); } -GURL::GURL(const base::string16& url_string) { +GURL::GURL(base::StringPiece16 url_string) { InitCanonical(url_string, true); } GURL::GURL(const std::string& url_string, RetainWhiteSpaceSelector) { - InitCanonical(url_string, false); + InitCanonical(base::StringPiece(url_string), false); } GURL::GURL(const char* canonical_spec, @@ -108,7 +108,8 @@ GURL::GURL(std::string canonical_spec, const url::Parsed& parsed, bool is_valid) } template -void GURL::InitCanonical(const STR& input_spec, bool trim_path_end) { +void GURL::InitCanonical(base::BasicStringPiece input_spec, + bool trim_path_end) { // Reserve enough room in the output for the input, plus some extra so that // we have room if we have to escape a few things without reallocating. spec_.reserve(input_spec.size() + 32); @@ -331,7 +332,7 @@ GURL GURL::GetOrigin() const { } GURL GURL::GetAsReferrer() const { - if (!is_valid_ || !SchemeIsHTTPOrHTTPS()) + if (!SchemeIsValidForReferrer()) return GURL(); if (!has_ref() && !has_username() && !has_password()) @@ -385,6 +386,10 @@ bool GURL::SchemeIsHTTPOrHTTPS() const { return SchemeIs(url::kHttpScheme) || SchemeIs(url::kHttpsScheme); } +bool GURL::SchemeIsValidForReferrer() const { + return is_valid_ && IsReferrerScheme(spec_.data(), parsed_.scheme); +} + bool GURL::SchemeIsWSOrWSS() const { return SchemeIs(url::kWsScheme) || SchemeIs(url::kWssScheme); } diff --git a/src/url/gurl.h b/src/url/gurl.h index b49d2fdb..c55959bb 100644 --- a/src/url/gurl.h +++ b/src/url/gurl.h @@ -56,8 +56,8 @@ class URL_EXPORT GURL { GURL(const GURL& other); // The strings to this contructor should be UTF-8 / UTF-16. - explicit GURL(const std::string& url_string); - explicit GURL(const base::string16& url_string); + explicit GURL(base::StringPiece url_string); + explicit GURL(base::StringPiece16 url_string); // Constructor for URLs that have already been parsed and canonicalized. This // is used for conversions from KURL, for example. The caller must supply all @@ -215,6 +215,9 @@ class URL_EXPORT GURL { // Returns true if the scheme is "http" or "https". bool SchemeIsHTTPOrHTTPS() const; + // Returns true if the scheme is valid for use as a referrer. + bool SchemeIsValidForReferrer() const; + // Returns true is the scheme is "ws" or "wss". bool SchemeIsWSOrWSS() const; @@ -405,7 +408,8 @@ class URL_EXPORT GURL { GURL(const std::string& url_string, RetainWhiteSpaceSelector); template - void InitCanonical(const STR& input_spec, bool trim_path_end); + void InitCanonical(base::BasicStringPiece input_spec, + bool trim_path_end); void InitializeFromCanonicalSpec(); diff --git a/src/url/third_party/mozilla/url_parse.h b/src/url/third_party/mozilla/url_parse.h index 7bfcdc81..222d6053 100644 --- a/src/url/third_party/mozilla/url_parse.h +++ b/src/url/third_party/mozilla/url_parse.h @@ -10,10 +10,6 @@ namespace url { -// Deprecated, but WebKit/WebCore/platform/KURLGooglePrivate.h and -// KURLGoogle.cpp still rely on this type. -typedef base::char16 UTF16Char; - // Component ------------------------------------------------------------------ // Represents a substring for URL parsing. diff --git a/src/url/url_canon_etc.cc b/src/url/url_canon_etc.cc new file mode 100644 index 00000000..e9da94cc --- /dev/null +++ b/src/url/url_canon_etc.cc @@ -0,0 +1,367 @@ +// 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. + +// Canonicalizers for random bits that aren't big enough for their own files. + +#include + +#include "url/url_canon.h" +#include "url/url_canon_internal.h" + +namespace url { + +namespace { + +// Returns true if the given character should be removed from the middle of a +// URL. +inline bool IsRemovableURLWhitespace(int ch) { + return ch == '\r' || ch == '\n' || ch == '\t'; +} + +// Backend for RemoveURLWhitespace (see declaration in url_canon.h). +// It sucks that we have to do this, since this takes about 13% of the total URL +// canonicalization time. +template +const CHAR* DoRemoveURLWhitespace(const CHAR* input, int input_len, + CanonOutputT* buffer, + int* output_len) { + // Fast verification that there's nothing that needs removal. This is the 99% + // case, so we want it to be fast and don't care about impacting the speed + // when we do find whitespace. + int found_whitespace = false; + for (int i = 0; i < input_len; i++) { + if (!IsRemovableURLWhitespace(input[i])) + continue; + found_whitespace = true; + break; + } + + if (!found_whitespace) { + // Didn't find any whitespace, we don't need to do anything. We can just + // return the input as the output. + *output_len = input_len; + return input; + } + + // Remove the whitespace into the new buffer and return it. + for (int i = 0; i < input_len; i++) { + if (!IsRemovableURLWhitespace(input[i])) + buffer->push_back(input[i]); + } + *output_len = buffer->length(); + return buffer->data(); +} + +// Contains the canonical version of each possible input letter in the scheme +// (basically, lower-cased). The corresponding entry will be 0 if the letter +// is not allowed in a scheme. +const char kSchemeCanonical[0x80] = { +// 00-1f: all are invalid + 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 1 2 3 4 5 6 7 8 9 : ; < = > ? + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0 , 0 , 0 , 0 , 0 , 0 , +// @ A B C D E F G H I J K L M N O + 0 , 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +// P Q R S T U V W X Y Z [ \ ] ^ _ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0 , 0, 0 , 0, +// ` a b c d e f g h i j k l m n o + 0 , 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +// p q r s t u v w x y z { | } ~ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0 , 0 , 0 , 0 , 0 }; + +// This could be a table lookup as well by setting the high bit for each +// valid character, but it's only called once per URL, and it makes the lookup +// table easier to read not having extra stuff in it. +inline bool IsSchemeFirstChar(unsigned char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +template +bool DoScheme(const CHAR* spec, + const Component& scheme, + CanonOutput* output, + Component* out_scheme) { + if (scheme.len <= 0) { + // Scheme is unspecified or empty, convert to empty by appending a colon. + *out_scheme = Component(output->length(), 0); + output->push_back(':'); + return true; + } + + // The output scheme starts from the current position. + out_scheme->begin = output->length(); + + // Danger: it's important that this code does not strip any characters; + // it only emits the canonical version (be it valid or escaped) for each + // of the input characters. Stripping would put it out of sync with + // FindAndCompareScheme, which could cause some security checks on + // schemes to be incorrect. + bool success = true; + int end = scheme.end(); + for (int i = scheme.begin; i < end; i++) { + UCHAR ch = static_cast(spec[i]); + char replacement = 0; + if (ch < 0x80) { + if (i == scheme.begin) { + // Need to do a special check for the first letter of the scheme. + if (IsSchemeFirstChar(static_cast(ch))) + replacement = kSchemeCanonical[ch]; + } else { + replacement = kSchemeCanonical[ch]; + } + } + + if (replacement) { + output->push_back(replacement); + } else if (ch == '%') { + // Canonicalizing the scheme multiple times should lead to the same + // result. Since invalid characters will be escaped, we need to preserve + // the percent to avoid multiple escaping. The scheme will be invalid. + success = false; + output->push_back('%'); + } else { + // Invalid character, store it but mark this scheme as invalid. + success = false; + + // This will escape the output and also handle encoding issues. + // Ignore the return value since we already failed. + AppendUTF8EscapedChar(spec, &i, end, output); + } + } + + // The output scheme ends with the the current position, before appending + // the colon. + out_scheme->len = output->length() - out_scheme->begin; + output->push_back(':'); + return success; +} + +// The username and password components reference ranges in the corresponding +// *_spec strings. Typically, these specs will be the same (we're +// canonicalizing a single source string), but may be different when +// replacing components. +template +bool DoUserInfo(const CHAR* username_spec, + const Component& username, + const CHAR* password_spec, + const Component& password, + CanonOutput* output, + Component* out_username, + Component* out_password) { + if (username.len <= 0 && password.len <= 0) { + // Common case: no user info. We strip empty username/passwords. + *out_username = Component(); + *out_password = Component(); + return true; + } + + // Write the username. + out_username->begin = output->length(); + if (username.len > 0) { + // This will escape characters not valid for the username. + AppendStringOfType(&username_spec[username.begin], username.len, + CHAR_USERINFO, output); + } + out_username->len = output->length() - out_username->begin; + + // When there is a password, we need the separator. Note that we strip + // empty but specified passwords. + if (password.len > 0) { + output->push_back(':'); + out_password->begin = output->length(); + AppendStringOfType(&password_spec[password.begin], password.len, + CHAR_USERINFO, output); + out_password->len = output->length() - out_password->begin; + } else { + *out_password = Component(); + } + + output->push_back('@'); + return true; +} + +// Helper functions for converting port integers to strings. +inline void WritePortInt(char* output, int output_len, int port) { + _itoa_s(port, output, output_len, 10); +} + +// This function will prepend the colon if there will be a port. +template +bool DoPort(const CHAR* spec, + const Component& port, + int default_port_for_scheme, + CanonOutput* output, + Component* out_port) { + int port_num = ParsePort(spec, port); + if (port_num == PORT_UNSPECIFIED || port_num == default_port_for_scheme) { + *out_port = Component(); + return true; // Leave port empty. + } + + if (port_num == PORT_INVALID) { + // Invalid port: We'll copy the text from the input so the user can see + // what the error was, and mark the URL as invalid by returning false. + output->push_back(':'); + out_port->begin = output->length(); + AppendInvalidNarrowString(spec, port.begin, port.end(), output); + out_port->len = output->length() - out_port->begin; + return false; + } + + // Convert port number back to an integer. Max port value is 5 digits, and + // the Parsed::ExtractPort will have made sure the integer is in range. + const int buf_size = 6; + char buf[buf_size]; + WritePortInt(buf, buf_size, port_num); + + // Append the port number to the output, preceded by a colon. + output->push_back(':'); + out_port->begin = output->length(); + for (int i = 0; i < buf_size && buf[i]; i++) + output->push_back(buf[i]); + + out_port->len = output->length() - out_port->begin; + return true; +} + +template +void DoCanonicalizeRef(const CHAR* spec, + const Component& ref, + CanonOutput* output, + Component* out_ref) { + if (ref.len < 0) { + // Common case of no ref. + *out_ref = Component(); + return; + } + + // Append the ref separator. Note that we need to do this even when the ref + // is empty but present. + output->push_back('#'); + out_ref->begin = output->length(); + + // Now iterate through all the characters, converting to UTF-8 and validating. + int end = ref.end(); + for (int i = ref.begin; i < end; i++) { + if (spec[i] == 0) { + // IE just strips NULLs, so we do too. + continue; + } else if (static_cast(spec[i]) < 0x20) { + // Unline IE seems to, we escape control characters. This will probably + // make the reference fragment unusable on a web page, but people + // shouldn't be using control characters in their anchor names. + AppendEscapedChar(static_cast(spec[i]), output); + } else if (static_cast(spec[i]) < 0x80) { + // Normal ASCII characters are just appended. + output->push_back(static_cast(spec[i])); + } else { + // Non-ASCII characters are appended unescaped, but only when they are + // valid. Invalid Unicode characters are replaced with the "invalid + // character" as IE seems to (ReadUTFChar puts the unicode replacement + // character in the output on failure for us). + unsigned code_point; + ReadUTFChar(spec, &i, end, &code_point); + AppendUTF8Value(code_point, output); + } + } + + out_ref->len = output->length() - out_ref->begin; +} + +} // namespace + +const char* RemoveURLWhitespace(const char* input, int input_len, + CanonOutputT* buffer, + int* output_len) { + return DoRemoveURLWhitespace(input, input_len, buffer, output_len); +} + +const base::char16* RemoveURLWhitespace(const base::char16* input, + int input_len, + CanonOutputT* buffer, + int* output_len) { + return DoRemoveURLWhitespace(input, input_len, buffer, output_len); +} + +char CanonicalSchemeChar(base::char16 ch) { + if (ch >= 0x80) + return 0; // Non-ASCII is not supported by schemes. + return kSchemeCanonical[ch]; +} + +bool CanonicalizeScheme(const char* spec, + const Component& scheme, + CanonOutput* output, + Component* out_scheme) { + return DoScheme(spec, scheme, output, out_scheme); +} + +bool CanonicalizeScheme(const base::char16* spec, + const Component& scheme, + CanonOutput* output, + Component* out_scheme) { + return DoScheme(spec, scheme, output, out_scheme); +} + +bool CanonicalizeUserInfo(const char* username_source, + const Component& username, + const char* password_source, + const Component& password, + CanonOutput* output, + Component* out_username, + Component* out_password) { + return DoUserInfo( + username_source, username, password_source, password, + output, out_username, out_password); +} + +bool CanonicalizeUserInfo(const base::char16* username_source, + const Component& username, + const base::char16* password_source, + const Component& password, + CanonOutput* output, + Component* out_username, + Component* out_password) { + return DoUserInfo( + username_source, username, password_source, password, + output, out_username, out_password); +} + +bool CanonicalizePort(const char* spec, + const Component& port, + int default_port_for_scheme, + CanonOutput* output, + Component* out_port) { + return DoPort(spec, port, + default_port_for_scheme, + output, out_port); +} + +bool CanonicalizePort(const base::char16* spec, + const Component& port, + int default_port_for_scheme, + CanonOutput* output, + Component* out_port) { + return DoPort(spec, port, default_port_for_scheme, + output, out_port); +} + +void CanonicalizeRef(const char* spec, + const Component& ref, + CanonOutput* output, + Component* out_ref) { + DoCanonicalizeRef(spec, ref, output, out_ref); +} + +void CanonicalizeRef(const base::char16* spec, + const Component& ref, + CanonOutput* output, + Component* out_ref) { + DoCanonicalizeRef(spec, ref, output, out_ref); +} + +} // namespace url diff --git a/src/url/url_canon_filesystemurl.cc b/src/url/url_canon_filesystemurl.cc new file mode 100644 index 00000000..18e90552 --- /dev/null +++ b/src/url/url_canon_filesystemurl.cc @@ -0,0 +1,129 @@ +// 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. + +// Functions for canonicalizing "filesystem:file:" URLs. + +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_file.h" +#include "url/url_parse_internal.h" +#include "url/url_util.h" +#include "url/url_util_internal.h" + +namespace url { + +namespace { + +// We use the URLComponentSource for the outer URL, as it can have replacements, +// whereas the inner_url can't, so it uses spec. +template +bool DoCanonicalizeFileSystemURL(const CHAR* spec, + const URLComponentSource& source, + const Parsed& parsed, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* new_parsed) { + // filesystem only uses {scheme, path, query, ref} -- clear the rest. + new_parsed->username.reset(); + new_parsed->password.reset(); + new_parsed->host.reset(); + new_parsed->port.reset(); + + const Parsed* inner_parsed = parsed.inner_parsed(); + Parsed new_inner_parsed; + + // Scheme (known, so we don't bother running it through the more + // complicated scheme canonicalizer). + new_parsed->scheme.begin = output->length(); + output->Append("filesystem:", 11); + new_parsed->scheme.len = 10; + + if (!parsed.inner_parsed() || !parsed.inner_parsed()->scheme.is_valid()) + return false; + + bool success = true; + if (CompareSchemeComponent(spec, inner_parsed->scheme, url::kFileScheme)) { + new_inner_parsed.scheme.begin = output->length(); + output->Append("file://", 7); + new_inner_parsed.scheme.len = 4; + success &= CanonicalizePath(spec, inner_parsed->path, output, + &new_inner_parsed.path); + } else if (IsStandard(spec, inner_parsed->scheme)) { + success = CanonicalizeStandardURL(spec, parsed.inner_parsed()->Length(), + *parsed.inner_parsed(), charset_converter, + output, &new_inner_parsed); + } else { + // TODO(ericu): The URL is wrong, but should we try to output more of what + // we were given? Echoing back filesystem:mailto etc. doesn't seem all that + // useful. + return false; + } + // The filesystem type must be more than just a leading slash for validity. + success &= parsed.inner_parsed()->path.len > 1; + + success &= CanonicalizePath(source.path, parsed.path, output, + &new_parsed->path); + + // Ignore failures for query/ref since the URL can probably still be loaded. + CanonicalizeQuery(source.query, parsed.query, charset_converter, + output, &new_parsed->query); + CanonicalizeRef(source.ref, parsed.ref, output, &new_parsed->ref); + if (success) + new_parsed->set_inner_parsed(new_inner_parsed); + + return success; +} + +} // namespace + +bool CanonicalizeFileSystemURL(const char* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeFileSystemURL( + spec, URLComponentSource(spec), parsed, charset_converter, output, + new_parsed); +} + +bool CanonicalizeFileSystemURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeFileSystemURL( + spec, URLComponentSource(spec), parsed, charset_converter, + output, new_parsed); +} + +bool ReplaceFileSystemURL(const char* base, + const Parsed& base_parsed, + const Replacements& replacements, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* new_parsed) { + URLComponentSource source(base); + Parsed parsed(base_parsed); + SetupOverrideComponents(base, replacements, &source, &parsed); + return DoCanonicalizeFileSystemURL( + base, source, parsed, charset_converter, output, new_parsed); +} + +bool ReplaceFileSystemURL(const char* base, + const Parsed& base_parsed, + const Replacements& replacements, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* new_parsed) { + RawCanonOutput<1024> utf8; + URLComponentSource source(base); + Parsed parsed(base_parsed); + SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); + return DoCanonicalizeFileSystemURL( + base, source, parsed, charset_converter, output, new_parsed); +} + +} // namespace url diff --git a/src/url/url_canon_fileurl.cc b/src/url/url_canon_fileurl.cc new file mode 100644 index 00000000..6191f8f9 --- /dev/null +++ b/src/url/url_canon_fileurl.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. + +// Functions for canonicalizing "file:" URLs. + +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_file.h" +#include "url/url_parse_internal.h" + +namespace url { + +namespace { + +#ifdef WIN32 + +// Given a pointer into the spec, this copies and canonicalizes the drive +// letter and colon to the output, if one is found. If there is not a drive +// spec, it won't do anything. The index of the next character in the input +// spec is returned (after the colon when a drive spec is found, the begin +// offset if one is not). +template +int FileDoDriveSpec(const CHAR* spec, int begin, int end, + CanonOutput* output) { + // The path could be one of several things: /foo/bar, c:/foo/bar, /c:/foo, + // (with backslashes instead of slashes as well). + int num_slashes = CountConsecutiveSlashes(spec, begin, end); + int after_slashes = begin + num_slashes; + + if (!DoesBeginWindowsDriveSpec(spec, after_slashes, end)) + return begin; // Haven't consumed any characters + + // A drive spec is the start of a path, so we need to add a slash for the + // authority terminator (typically the third slash). + output->push_back('/'); + + // DoesBeginWindowsDriveSpec will ensure that the drive letter is valid + // and that it is followed by a colon/pipe. + + // Normalize Windows drive letters to uppercase + if (spec[after_slashes] >= 'a' && spec[after_slashes] <= 'z') + output->push_back(static_cast(spec[after_slashes] - 'a' + 'A')); + else + output->push_back(static_cast(spec[after_slashes])); + + // Normalize the character following it to a colon rather than pipe. + output->push_back(':'); + return after_slashes + 2; +} + +#endif // WIN32 + +template +bool DoFileCanonicalizePath(const CHAR* spec, + const Component& path, + CanonOutput* output, + Component* out_path) { + // Copies and normalizes the "c:" at the beginning, if present. + out_path->begin = output->length(); + int after_drive; +#ifdef WIN32 + after_drive = FileDoDriveSpec(spec, path.begin, path.end(), output); +#else + after_drive = path.begin; +#endif + + // Copies the rest of the path, starting from the slash following the + // drive colon (if any, Windows only), or the first slash of the path. + bool success = true; + if (after_drive < path.end()) { + // Use the regular path canonicalizer to canonicalize the rest of the + // path. Give it a fake output component to write into. DoCanonicalizeFile + // will compute the full path component. + Component sub_path = MakeRange(after_drive, path.end()); + Component fake_output_path; + success = CanonicalizePath(spec, sub_path, output, &fake_output_path); + } else { + // No input path, canonicalize to a slash. + output->push_back('/'); + } + + out_path->len = output->length() - out_path->begin; + return success; +} + +template +bool DoCanonicalizeFileURL(const URLComponentSource& source, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + // Things we don't set in file: URLs. + new_parsed->username = Component(); + new_parsed->password = Component(); + new_parsed->port = Component(); + + // Scheme (known, so we don't bother running it through the more + // complicated scheme canonicalizer). + new_parsed->scheme.begin = output->length(); + output->Append("file://", 7); + new_parsed->scheme.len = 4; + + // Append the host. For many file URLs, this will be empty. For UNC, this + // will be present. + // TODO(brettw) This doesn't do any checking for host name validity. We + // should probably handle validity checking of UNC hosts differently than + // for regular IP hosts. + bool success = CanonicalizeHost(source.host, parsed.host, + output, &new_parsed->host); + success &= DoFileCanonicalizePath(source.path, parsed.path, + output, &new_parsed->path); + CanonicalizeQuery(source.query, parsed.query, query_converter, + output, &new_parsed->query); + + // Ignore failure for refs since the URL can probably still be loaded. + CanonicalizeRef(source.ref, parsed.ref, output, &new_parsed->ref); + + return success; +} + +} // namespace + +bool CanonicalizeFileURL(const char* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeFileURL( + URLComponentSource(spec), parsed, query_converter, + output, new_parsed); +} + +bool CanonicalizeFileURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeFileURL( + URLComponentSource(spec), parsed, query_converter, + output, new_parsed); +} + +bool FileCanonicalizePath(const char* spec, + const Component& path, + CanonOutput* output, + Component* out_path) { + return DoFileCanonicalizePath(spec, path, + output, out_path); +} + +bool FileCanonicalizePath(const base::char16* spec, + const Component& path, + CanonOutput* output, + Component* out_path) { + return DoFileCanonicalizePath(spec, path, + output, out_path); +} + +bool ReplaceFileURL(const char* base, + const Parsed& base_parsed, + const Replacements& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + URLComponentSource source(base); + Parsed parsed(base_parsed); + SetupOverrideComponents(base, replacements, &source, &parsed); + return DoCanonicalizeFileURL( + source, parsed, query_converter, output, new_parsed); +} + +bool ReplaceFileURL(const char* base, + const Parsed& base_parsed, + const Replacements& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + RawCanonOutput<1024> utf8; + URLComponentSource source(base); + Parsed parsed(base_parsed); + SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); + return DoCanonicalizeFileURL( + source, parsed, query_converter, output, new_parsed); +} + +} // namespace url diff --git a/src/url/url_canon_host.cc b/src/url/url_canon_host.cc new file mode 100644 index 00000000..d7959c73 --- /dev/null +++ b/src/url/url_canon_host.cc @@ -0,0 +1,403 @@ +// 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 "base/logging.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" + +namespace url { + +namespace { + +// For reference, here's what IE supports: +// Key: 0 (disallowed: failure if present in the input) +// + (allowed either escaped or unescaped, and unmodified) +// U (allowed escaped or unescaped but always unescaped if present in +// escaped form) +// E (allowed escaped or unescaped but always escaped if present in +// unescaped form) +// % (only allowed escaped in the input, will be unmodified). +// I left blank alpha numeric characters. +// +// 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f +// ----------------------------------------------- +// 0 0 E E E E E E E E E E E E E E E +// 1 E E E E E E E E E E E E E E E E +// 2 E + E E + E + + + + + + + U U 0 +// 3 % % E + E 0 <-- Those are : ; < = > ? +// 4 % +// 5 U 0 U U U <-- Those are [ \ ] ^ _ +// 6 E <-- That's ` +// 7 E E E U E <-- Those are { | } ~ (UNPRINTABLE) +// +// NOTE: I didn't actually test all the control characters. Some may be +// disallowed in the input, but they are all accepted escaped except for 0. +// I also didn't test if characters affecting HTML parsing are allowed +// unescaped, e.g. (") or (#), which would indicate the beginning of the path. +// Surprisingly, space is accepted in the input and always escaped. + +// This table lists the canonical version of all characters we allow in the +// input, with 0 indicating it is disallowed. We use the magic kEscapedHostChar +// value to indicate that this character should be escaped. We are a little more +// restrictive than IE, but less restrictive than Firefox. +// +// Note that we disallow the % character. We will allow it when part of an +// escape sequence, of course, but this disallows "%25". Even though IE allows +// it, allowing it would put us in a funny state. If there was an invalid +// escape sequence like "%zz", we'll add "%25zz" to the output and fail. +// Allowing percents means we'll succeed a second time, so validity would change +// based on how many times you run the canonicalizer. We prefer to always report +// the same vailidity, so reject this. +const unsigned char kEsc = 0xff; +const unsigned char kHostCharLookup[0x80] = { +// 00-1f: all are invalid + 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, +// ' ' ! " # $ % & ' ( ) * + , - . / + kEsc,kEsc,kEsc,kEsc,kEsc, 0, kEsc,kEsc,kEsc,kEsc,kEsc, '+',kEsc, '-', '.', 0, +// 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', 0 ,kEsc,kEsc,kEsc, 0 , +// @ A B C D E F G H I J K L M N O + kEsc, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +// P Q R S T U V W X Y Z [ \ ] ^ _ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', 0 , ']', 0 , '_', +// ` a b c d e f g h i j k l m n o + kEsc, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +// p q r s t u v w x y z { | } ~ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',kEsc,kEsc,kEsc, 0 , 0 }; + +const int kTempHostBufferLen = 1024; +typedef RawCanonOutputT StackBuffer; +typedef RawCanonOutputT StackBufferW; + +// Scans a host name and fills in the output flags according to what we find. +// |has_non_ascii| will be true if there are any non-7-bit characters, and +// |has_escaped| will be true if there is a percent sign. +template +void ScanHostname(const CHAR* spec, + const Component& host, + bool* has_non_ascii, + bool* has_escaped) { + int end = host.end(); + *has_non_ascii = false; + *has_escaped = false; + for (int i = host.begin; i < end; i++) { + if (static_cast(spec[i]) >= 0x80) + *has_non_ascii = true; + else if (spec[i] == '%') + *has_escaped = true; + } +} + +// Canonicalizes a host name that is entirely 8-bit characters (even though +// the type holding them may be 16 bits. Escaped characters will be unescaped. +// Non-7-bit characters (for example, UTF-8) will be passed unchanged. +// +// The |*has_non_ascii| flag will be true if there are non-7-bit characters in +// the output. +// +// This function is used in two situations: +// +// * When the caller knows there is no non-ASCII or percent escaped +// characters. This is what DoHost does. The result will be a completely +// canonicalized host since we know nothing weird can happen (escaped +// characters could be unescaped to non-7-bit, so they have to be treated +// with suspicion at this point). It does not use the |has_non_ascii| flag. +// +// * When the caller has an 8-bit string that may need unescaping. +// DoComplexHost calls us this situation to do unescaping and validation. +// After this, it may do other IDN operations depending on the value of the +// |*has_non_ascii| flag. +// +// The return value indicates if the output is a potentially valid host name. +template +bool DoSimpleHost(const INCHAR* host, + int host_len, + CanonOutputT* output, + bool* has_non_ascii) { + *has_non_ascii = false; + + bool success = true; + for (int i = 0; i < host_len; ++i) { + unsigned int source = host[i]; + if (source == '%') { + // Unescape first, if possible. + // Source will be used only if decode operation was successful. + if (!DecodeEscaped(host, &i, host_len, + reinterpret_cast(&source))) { + // Invalid escaped character. There is nothing that can make this + // host valid. We append an escaped percent so the URL looks reasonable + // and mark as failed. + AppendEscapedChar('%', output); + success = false; + continue; + } + } + + if (source < 0x80) { + // We have ASCII input, we can use our lookup table. + unsigned char replacement = kHostCharLookup[source]; + if (!replacement) { + // Invalid character, add it as percent-escaped and mark as failed. + AppendEscapedChar(source, output); + success = false; + } else if (replacement == kEsc) { + // This character is valid but should be escaped. + AppendEscapedChar(source, output); + } else { + // Common case, the given character is valid in a hostname, the lookup + // table tells us the canonical representation of that character (lower + // cased). + output->push_back(replacement); + } + } else { + // It's a non-ascii char. Just push it to the output. + // In case where we have char16 input, and char output it's safe to + // cast char16->char only if input string was converted to ASCII. + output->push_back(static_cast(source)); + *has_non_ascii = true; + } + } + + return success; +} + +// Canonicalizes a host that requires IDN conversion. Returns true on success +bool DoIDNHost(const base::char16* src, int src_len, CanonOutput* output) { + int original_output_len = output->length(); // So we can rewind below. + + // We need to escape URL before doing IDN conversion, since punicode strings + // cannot be escaped after they are created. + RawCanonOutputW url_escaped_host; + bool has_non_ascii; + DoSimpleHost(src, src_len, &url_escaped_host, &has_non_ascii); + + StackBufferW wide_output; +#if 0 + if (!IDNToASCII(url_escaped_host.data(), + url_escaped_host.length(), + &wide_output)) { +#else + if (true) { +#endif + // Some error, give up. This will write some reasonable looking + // representation of the string to the output. + AppendInvalidNarrowString(src, 0, src_len, output); + return false; + } + + // Now we check the ASCII output like a normal host. It will also handle + // unescaping. Although we unescaped everything before this function call, if + // somebody does %00 as fullwidth, ICU will convert this to ASCII. + bool success = DoSimpleHost(wide_output.data(), + wide_output.length(), + output, &has_non_ascii); + if (has_non_ascii) { + // ICU generated something that DoSimpleHost didn't think looked like + // ASCII. This is quite rare, but ICU might convert some characters to + // percent signs which might generate new escape sequences which might in + // turn be invalid. An example is U+FE6A "small percent" which ICU will + // name prep into an ASCII percent and then we can interpret the following + // characters as escaped characters. + // + // If DoSimpleHost didn't think the output was ASCII, just escape the + // thing we gave ICU and give up. DoSimpleHost will have handled a further + // level of escaping from ICU for simple ASCII cases (i.e. if ICU generates + // a new escaped ASCII sequence like "%41" we'll unescape it) but it won't + // do more (like handle escaped non-ASCII sequences). Handling the escaped + // ASCII isn't strictly necessary, but DoSimpleHost handles this case + // anyway so we handle it/ + output->set_length(original_output_len); + AppendInvalidNarrowString(wide_output.data(), 0, wide_output.length(), + output); + return false; + } + return success; +} + +// 8-bit convert host to its ASCII version: this converts the UTF-8 input to +// UTF-16. The has_escaped flag should be set if the input string requires +// unescaping. +bool DoComplexHost(const char* host, int host_len, + bool has_non_ascii, bool has_escaped, CanonOutput* output) { + // Save the current position in the output. We may write stuff and rewind it + // below, so we need to know where to rewind to. + int begin_length = output->length(); + + // Points to the UTF-8 data we want to convert. This will either be the + // input or the unescaped version written to |*output| if necessary. + const char* utf8_source; + int utf8_source_len; + if (has_escaped) { + // Unescape before converting to UTF-16 for IDN. We write this into the + // output because it most likely does not require IDNization, and we can + // save another huge stack buffer. It will be replaced below if it requires + // IDN. This will also update our non-ASCII flag so we know whether the + // unescaped input requires IDN. + if (!DoSimpleHost(host, host_len, output, &has_non_ascii)) { + // Error with some escape sequence. We'll call the current output + // complete. DoSimpleHost will have written some "reasonable" output. + return false; + } + + // Unescaping may have left us with ASCII input, in which case the + // unescaped version we wrote to output is complete. + if (!has_non_ascii) { + return true; + } + + // Save the pointer into the data was just converted (it may be appended to + // other data in the output buffer). + utf8_source = &output->data()[begin_length]; + utf8_source_len = output->length() - begin_length; + } else { + // We don't need to unescape, use input for IDNization later. (We know the + // input has non-ASCII, or the simple version would have been called + // instead of us.) + utf8_source = host; + utf8_source_len = host_len; + } + + // Non-ASCII input requires IDN, convert to UTF-16 and do the IDN conversion. + // Above, we may have used the output to write the unescaped values to, so + // we have to rewind it to where we started after we convert it to UTF-16. + StackBufferW utf16; + if (!ConvertUTF8ToUTF16(utf8_source, utf8_source_len, &utf16)) { + // In this error case, the input may or may not be the output. + StackBuffer utf8; + for (int i = 0; i < utf8_source_len; i++) + utf8.push_back(utf8_source[i]); + output->set_length(begin_length); + AppendInvalidNarrowString(utf8.data(), 0, utf8.length(), output); + return false; + } + output->set_length(begin_length); + + // This will call DoSimpleHost which will do normal ASCII canonicalization + // and also check for IP addresses in the outpt. + return DoIDNHost(utf16.data(), utf16.length(), output); +} + +// UTF-16 convert host to its ASCII version. The set up is already ready for +// the backend, so we just pass through. The has_escaped flag should be set if +// the input string requires unescaping. +bool DoComplexHost(const base::char16* host, int host_len, + bool has_non_ascii, bool has_escaped, CanonOutput* output) { + if (has_escaped) { + // Yikes, we have escaped characters with wide input. The escaped + // characters should be interpreted as UTF-8. To solve this problem, + // we convert to UTF-8, unescape, then convert back to UTF-16 for IDN. + // + // We don't bother to optimize the conversion in the ASCII case (which + // *could* just be a copy) and use the UTF-8 path, because it should be + // very rare that host names have escaped characters, and it is relatively + // fast to do the conversion anyway. + StackBuffer utf8; + if (!ConvertUTF16ToUTF8(host, host_len, &utf8)) { + AppendInvalidNarrowString(host, 0, host_len, output); + return false; + } + + // Once we convert to UTF-8, we can use the 8-bit version of the complex + // host handling code above. + return DoComplexHost(utf8.data(), utf8.length(), has_non_ascii, + has_escaped, output); + } + + // No unescaping necessary, we can safely pass the input to ICU. This + // function will only get called if we either have escaped or non-ascii + // input, so it's safe to just use ICU now. Even if the input is ASCII, + // this function will do the right thing (just slower than we could). + return DoIDNHost(host, host_len, output); +} + +template +void DoHost(const CHAR* spec, + const Component& host, + CanonOutput* output, + CanonHostInfo* host_info) { + if (host.len <= 0) { + // Empty hosts don't need anything. + host_info->family = CanonHostInfo::NEUTRAL; + host_info->out_host = Component(); + return; + } + + bool has_non_ascii, has_escaped; + ScanHostname(spec, host, &has_non_ascii, &has_escaped); + + // Keep track of output's initial length, so we can rewind later. + const int output_begin = output->length(); + + bool success; + if (!has_non_ascii && !has_escaped) { + success = DoSimpleHost(&spec[host.begin], host.len, + output, &has_non_ascii); + DCHECK(!has_non_ascii); + } else { + success = DoComplexHost(&spec[host.begin], host.len, + has_non_ascii, has_escaped, output); + } + + if (!success) { + // Canonicalization failed. Set BROKEN to notify the caller. + host_info->family = CanonHostInfo::BROKEN; + } else { + // After all the other canonicalization, check if we ended up with an IP + // address. IP addresses are small, so writing into this temporary buffer + // should not cause an allocation. + RawCanonOutput<64> canon_ip; + CanonicalizeIPAddress(output->data(), + MakeRange(output_begin, output->length()), + &canon_ip, host_info); + + // If we got an IPv4/IPv6 address, copy the canonical form back to the + // real buffer. Otherwise, it's a hostname or broken IP, in which case + // we just leave it in place. + if (host_info->IsIPAddress()) { + output->set_length(output_begin); + output->Append(canon_ip.data(), canon_ip.length()); + } + } + + host_info->out_host = MakeRange(output_begin, output->length()); +} + +} // namespace + +bool CanonicalizeHost(const char* spec, + const Component& host, + CanonOutput* output, + Component* out_host) { + CanonHostInfo host_info; + DoHost(spec, host, output, &host_info); + *out_host = host_info.out_host; + return (host_info.family != CanonHostInfo::BROKEN); +} + +bool CanonicalizeHost(const base::char16* spec, + const Component& host, + CanonOutput* output, + Component* out_host) { + CanonHostInfo host_info; + DoHost(spec, host, output, &host_info); + *out_host = host_info.out_host; + return (host_info.family != CanonHostInfo::BROKEN); +} + +void CanonicalizeHostVerbose(const char* spec, + const Component& host, + CanonOutput* output, + CanonHostInfo* host_info) { + DoHost(spec, host, output, host_info); +} + +void CanonicalizeHostVerbose(const base::char16* spec, + const Component& host, + CanonOutput* output, + CanonHostInfo* host_info) { + DoHost(spec, host, output, host_info); +} + +} // namespace url diff --git a/src/url/url_canon_icu.cc b/src/url/url_canon_icu.cc new file mode 100644 index 00000000..d0468ccc --- /dev/null +++ b/src/url/url_canon_icu.cc @@ -0,0 +1,190 @@ +// 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. + +// ICU integration functions. + +#include +#include +#include + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "third_party/icu/source/common/unicode/ucnv.h" +#include "third_party/icu/source/common/unicode/ucnv_cb.h" +#include "third_party/icu/source/common/unicode/uidna.h" +#include "url/url_canon_icu.h" +#include "url/url_canon_internal.h" // for _itoa_s + +namespace url { + +namespace { + +// Called when converting a character that can not be represented, this will +// append an escaped version of the numerical character reference for that code +// point. It is of the form "Ӓ" and we will escape the non-digits to +// "%26%231234%3B". Why? This is what Netscape did back in the olden days. +void appendURLEscapedChar(const void* context, + UConverterFromUnicodeArgs* from_args, + const UChar* code_units, + int32_t length, + UChar32 code_point, + UConverterCallbackReason reason, + UErrorCode* err) { + if (reason == UCNV_UNASSIGNED) { + *err = U_ZERO_ERROR; + + const static int prefix_len = 6; + const static char prefix[prefix_len + 1] = "%26%23"; // "&#" percent-escaped + ucnv_cbFromUWriteBytes(from_args, prefix, prefix_len, 0, err); + + DCHECK(code_point < 0x110000); + char number[8]; // Max Unicode code point is 7 digits. + _itoa_s(code_point, number, 10); + int number_len = static_cast(strlen(number)); + ucnv_cbFromUWriteBytes(from_args, number, number_len, 0, err); + + const static int postfix_len = 3; + const static char postfix[postfix_len + 1] = "%3B"; // ";" percent-escaped + ucnv_cbFromUWriteBytes(from_args, postfix, postfix_len, 0, err); + } +} + +// A class for scoping the installation of the invalid character callback. +class AppendHandlerInstaller { + public: + // The owner of this object must ensure that the converter is alive for the + // duration of this object's lifetime. + AppendHandlerInstaller(UConverter* converter) : converter_(converter) { + UErrorCode err = U_ZERO_ERROR; + ucnv_setFromUCallBack(converter_, appendURLEscapedChar, 0, + &old_callback_, &old_context_, &err); + } + + ~AppendHandlerInstaller() { + UErrorCode err = U_ZERO_ERROR; + ucnv_setFromUCallBack(converter_, old_callback_, old_context_, 0, 0, &err); + } + + private: + UConverter* converter_; + + UConverterFromUCallback old_callback_; + const void* old_context_; +}; + +// 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 (with unassigned +// code points allowed) 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)) { + CHECK(false) << "failed to open UTS46 data with error: " << err; + value = NULL; + } + } + + UIDNA* value; +}; + +} // namespace + +ICUCharsetConverter::ICUCharsetConverter(UConverter* converter) + : converter_(converter) { +} + +ICUCharsetConverter::~ICUCharsetConverter() { +} + +void ICUCharsetConverter::ConvertFromUTF16(const base::char16* input, + int input_len, + CanonOutput* output) { + // Install our error handler. It will be called for character that can not + // be represented in the destination character set. + AppendHandlerInstaller handler(converter_); + + int begin_offset = output->length(); + int dest_capacity = output->capacity() - begin_offset; + output->set_length(output->length()); + + do { + UErrorCode err = U_ZERO_ERROR; + char* dest = &output->data()[begin_offset]; + int required_capacity = ucnv_fromUChars(converter_, dest, dest_capacity, + input, input_len, &err); + if (err != U_BUFFER_OVERFLOW_ERROR) { + output->set_length(begin_offset + required_capacity); + return; + } + + // Output didn't fit, expand + dest_capacity = required_capacity; + output->Resize(begin_offset + dest_capacity); + } while (true); +} + +static base::LazyInstance::Leaky + g_uidna = LAZY_INSTANCE_INITIALIZER; + +// Converts the Unicode input representing a hostname to ASCII using IDN rules. +// The output must be ASCII, but is represented as wide characters. +// +// On success, the output will be filled with the ASCII host name and it will +// return true. Unlike most other canonicalization functions, this assumes that +// the output is empty. The beginning of the host will be at offset 0, and +// the length of the output will be set to the length of the new host name. +// +// On error, this will return false. The output in this case is undefined. +// TODO(jungshik): use UTF-8/ASCII version of nameToASCII. +// Change the function signature and callers accordingly to avoid unnecessary +// conversions in our code. In addition, consider using icu::IDNA's UTF-8/ASCII +// version with StringByteSink. That way, we can avoid C wrappers and additional +// string conversion. +bool IDNToASCII(const base::char16* src, int src_len, CanonOutputW* output) { + DCHECK(output->length() == 0); // Output buffer is assumed empty. + + UIDNA* uidna = g_uidna.Get().value; + DCHECK(uidna != NULL); + while (true) { + UErrorCode err = U_ZERO_ERROR; + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + int output_length = uidna_nameToASCII(uidna, src, src_len, output->data(), + output->capacity(), &info, &err); + if (U_SUCCESS(err) && info.errors == 0) { + output->set_length(output_length); + return true; + } + + // TODO(jungshik): Look at info.errors to handle them case-by-case basis + // if necessary. + if (err != U_BUFFER_OVERFLOW_ERROR || info.errors != 0) + return false; // Unknown error, give up. + + // Not enough room in our buffer, expand. + output->Resize(output_length); + } +} + +} // namespace url diff --git a/src/url/url_canon_icu_alternatives_android.cc b/src/url/url_canon_icu_alternatives_android.cc new file mode 100644 index 00000000..f43efce5 --- /dev/null +++ b/src/url/url_canon_icu_alternatives_android.cc @@ -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. + +#include + +#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/IDNStringUtil_jni.h" +#include "url/url_canon_internal.h" + +namespace url { + +// This uses the JDK's conversion function, which uses IDNA 2003, unlike the +// ICU implementation. +bool IDNToASCII(const base::char16* src, int src_len, CanonOutputW* output) { + DCHECK_EQ(0, output->length()); // Output buffer is assumed empty. + + JNIEnv* env = base::android::AttachCurrentThread(); + base::android::ScopedJavaLocalRef java_src = + base::android::ConvertUTF16ToJavaString( + env, base::StringPiece16(src, src_len)); + ScopedJavaLocalRef java_result = + android::Java_IDNStringUtil_idnToASCII(env, java_src.obj()); + // NULL indicates failure. + if (java_result.is_null()) + return false; + + base::string16 utf16_result = + base::android::ConvertJavaStringToUTF16(java_result); + output->Append(utf16_result.data(), static_cast(utf16_result.size())); + return true; +} + +bool RegisterIcuAlternativesJni(JNIEnv* env) { + return android::RegisterNativesImpl(env); +} + +} // namespace url diff --git a/src/url/url_canon_icu_unittest.cc b/src/url/url_canon_icu_unittest.cc new file mode 100644 index 00000000..13601f02 --- /dev/null +++ b/src/url/url_canon_icu_unittest.cc @@ -0,0 +1,162 @@ +// 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 + +#include "base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/icu/source/common/unicode/ucnv.h" +#include "url/url_canon.h" +#include "url/url_canon_icu.h" +#include "url/url_canon_stdstring.h" +#include "url/url_test_utils.h" + +namespace url { + +using test_utils::WStringToUTF16; + +namespace { + +// Wrapper around a UConverter object that managers creation and destruction. +class UConvScoper { + public: + explicit UConvScoper(const char* charset_name) { + UErrorCode err = U_ZERO_ERROR; + converter_ = ucnv_open(charset_name, &err); + } + + ~UConvScoper() { + if (converter_) + ucnv_close(converter_); + } + + // Returns the converter object, may be NULL. + UConverter* converter() const { return converter_; } + + private: + UConverter* converter_; +}; + +TEST(URLCanonIcuTest, ICUCharsetConverter) { + struct ICUCase { + const wchar_t* input; + const char* encoding; + const char* expected; + } icu_cases[] = { + // UTF-8. + {L"Hello, world", "utf-8", "Hello, world"}, + {L"\x4f60\x597d", "utf-8", "\xe4\xbd\xa0\xe5\xa5\xbd"}, + // Non-BMP UTF-8. + {L"!\xd800\xdf00!", "utf-8", "!\xf0\x90\x8c\x80!"}, + // Big5 + {L"\x4f60\x597d", "big5", "\xa7\x41\xa6\x6e"}, + // Unrepresentable character in the destination set. + {L"hello\x4f60\x06de\x597dworld", "big5", + "hello\xa7\x41%26%231758%3B\xa6\x6eworld"}, + }; + + for (size_t i = 0; i < arraysize(icu_cases); i++) { + UConvScoper conv(icu_cases[i].encoding); + ASSERT_TRUE(conv.converter() != NULL); + ICUCharsetConverter converter(conv.converter()); + + std::string str; + StdStringCanonOutput output(&str); + + base::string16 input_str(WStringToUTF16(icu_cases[i].input)); + int input_len = static_cast(input_str.length()); + converter.ConvertFromUTF16(input_str.c_str(), input_len, &output); + output.Complete(); + + EXPECT_STREQ(icu_cases[i].expected, str.c_str()); + } + + // Test string sizes around the resize boundary for the output to make sure + // the converter resizes as needed. + const int static_size = 16; + UConvScoper conv("utf-8"); + ASSERT_TRUE(conv.converter()); + ICUCharsetConverter converter(conv.converter()); + for (int i = static_size - 2; i <= static_size + 2; i++) { + // Make a string with the appropriate length. + base::string16 input; + for (int ch = 0; ch < i; ch++) + input.push_back('a'); + + RawCanonOutput output; + converter.ConvertFromUTF16(input.c_str(), static_cast(input.length()), + &output); + EXPECT_EQ(input.length(), static_cast(output.length())); + } +} + +TEST(URLCanonIcuTest, QueryWithConverter) { + struct QueryCase { + const char* input8; + const wchar_t* input16; + const char* encoding; + const char* expected; + } query_cases[] = { + // Regular ASCII case in some different encodings. + {"foo=bar", L"foo=bar", "utf-8", "?foo=bar"}, + {"foo=bar", L"foo=bar", "shift_jis", "?foo=bar"}, + {"foo=bar", L"foo=bar", "gb2312", "?foo=bar"}, + // Chinese input/output + {"q=\xe4\xbd\xa0\xe5\xa5\xbd", L"q=\x4f60\x597d", "gb2312", + "?q=%C4%E3%BA%C3"}, + {"q=\xe4\xbd\xa0\xe5\xa5\xbd", L"q=\x4f60\x597d", "big5", "?q=%A7A%A6n"}, + // Unencodable character in the destination character set should be + // escaped. The escape sequence unescapes to be the entity name: + // "?q=你" + {"q=Chinese\xef\xbc\xa7", L"q=Chinese\xff27", "iso-8859-1", + "?q=Chinese%26%2365319%3B"}, + }; + + for (size_t i = 0; i < arraysize(query_cases); i++) { + Component out_comp; + + UConvScoper conv(query_cases[i].encoding); + ASSERT_TRUE(!query_cases[i].encoding || conv.converter()); + ICUCharsetConverter converter(conv.converter()); + + if (query_cases[i].input8) { + int len = static_cast(strlen(query_cases[i].input8)); + Component in_comp(0, len); + std::string out_str; + + StdStringCanonOutput output(&out_str); + CanonicalizeQuery(query_cases[i].input8, in_comp, &converter, &output, + &out_comp); + output.Complete(); + + EXPECT_EQ(query_cases[i].expected, out_str); + } + + if (query_cases[i].input16) { + base::string16 input16(WStringToUTF16(query_cases[i].input16)); + int len = static_cast(input16.length()); + Component in_comp(0, len); + std::string out_str; + + StdStringCanonOutput output(&out_str); + CanonicalizeQuery(input16.c_str(), in_comp, &converter, &output, + &out_comp); + output.Complete(); + + EXPECT_EQ(query_cases[i].expected, out_str); + } + } + + // Extra test for input with embedded NULL; + std::string out_str; + StdStringCanonOutput output(&out_str); + Component out_comp; + CanonicalizeQuery("a \x00z\x01", Component(0, 5), NULL, &output, &out_comp); + output.Complete(); + EXPECT_EQ("?a%20%00z%01", out_str); +} + +} // namespace + +} // namespace url diff --git a/src/url/url_canon_mailtourl.cc b/src/url/url_canon_mailtourl.cc new file mode 100644 index 00000000..fb6bc9ab --- /dev/null +++ b/src/url/url_canon_mailtourl.cc @@ -0,0 +1,110 @@ +// 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. + +// Functions for canonicalizing "mailto:" URLs. + +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_file.h" +#include "url/url_parse_internal.h" + +namespace url { + +namespace { + +template +bool DoCanonicalizeMailtoURL(const URLComponentSource& source, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + // mailto: only uses {scheme, path, query} -- clear the rest. + new_parsed->username = Component(); + new_parsed->password = Component(); + new_parsed->host = Component(); + new_parsed->port = Component(); + new_parsed->ref = Component(); + + // Scheme (known, so we don't bother running it through the more + // complicated scheme canonicalizer). + new_parsed->scheme.begin = output->length(); + output->Append("mailto:", 7); + new_parsed->scheme.len = 6; + + bool success = true; + + // Path + if (parsed.path.is_valid()) { + new_parsed->path.begin = output->length(); + + // Copy the path using path URL's more lax escaping rules. + // We convert to UTF-8 and escape non-ASCII, but leave all + // ASCII characters alone. + int end = parsed.path.end(); + for (int i = parsed.path.begin; i < end; ++i) { + UCHAR uch = static_cast(source.path[i]); + if (uch < 0x20 || uch >= 0x80) + success &= AppendUTF8EscapedChar(source.path, &i, end, output); + else + output->push_back(static_cast(uch)); + } + + new_parsed->path.len = output->length() - new_parsed->path.begin; + } else { + // No path at all + new_parsed->path.reset(); + } + + // Query -- always use the default UTF8 charset converter. + CanonicalizeQuery(source.query, parsed.query, NULL, + output, &new_parsed->query); + + return success; +} + +} // namespace + +bool CanonicalizeMailtoURL(const char* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeMailtoURL( + URLComponentSource(spec), parsed, output, new_parsed); +} + +bool CanonicalizeMailtoURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeMailtoURL( + URLComponentSource(spec), parsed, output, new_parsed); +} + +bool ReplaceMailtoURL(const char* base, + const Parsed& base_parsed, + const Replacements& replacements, + CanonOutput* output, + Parsed* new_parsed) { + URLComponentSource source(base); + Parsed parsed(base_parsed); + SetupOverrideComponents(base, replacements, &source, &parsed); + return DoCanonicalizeMailtoURL( + source, parsed, output, new_parsed); +} + +bool ReplaceMailtoURL(const char* base, + const Parsed& base_parsed, + const Replacements& replacements, + CanonOutput* output, + Parsed* new_parsed) { + RawCanonOutput<1024> utf8; + URLComponentSource source(base); + Parsed parsed(base_parsed); + SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); + return DoCanonicalizeMailtoURL( + source, parsed, output, new_parsed); +} + +} // namespace url diff --git a/src/url/url_canon_path.cc b/src/url/url_canon_path.cc new file mode 100644 index 00000000..2e088a03 --- /dev/null +++ b/src/url/url_canon_path.cc @@ -0,0 +1,437 @@ +// 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 + +#include "base/logging.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_parse_internal.h" + +namespace url { + +namespace { + +enum CharacterFlags { + // Pass through unchanged, whether escaped or unescaped. This doesn't + // actually set anything so you can't OR it to check, it's just to make the + // table below more clear when neither ESCAPE or UNESCAPE is set. + PASS = 0, + + // This character requires special handling in DoPartialPath. Doing this test + // first allows us to filter out the common cases of regular characters that + // can be directly copied. + SPECIAL = 1, + + // This character must be escaped in the canonical output. Note that all + // escaped chars also have the "special" bit set so that the code that looks + // for this is triggered. Not valid with PASS or ESCAPE + ESCAPE_BIT = 2, + ESCAPE = ESCAPE_BIT | SPECIAL, + + // This character must be unescaped in canonical output. Not valid with + // ESCAPE or PASS. We DON'T set the SPECIAL flag since if we encounter these + // characters unescaped, they should just be copied. + UNESCAPE = 4, + + // This character is disallowed in URLs. Note that the "special" bit is also + // set to trigger handling. + INVALID_BIT = 8, + INVALID = INVALID_BIT | SPECIAL, +}; + +// This table contains one of the above flag values. Note some flags are more +// than one bits because they also turn on the "special" flag. Special is the +// only flag that may be combined with others. +// +// This table is designed to match exactly what IE does with the characters. +// +// Dot is even more special, and the escaped version is handled specially by +// IsDot. Therefore, we don't need the "escape" flag, and even the "unescape" +// bit is never handled (we just need the "special") bit. +const unsigned char kPathCharLookup[0x100] = { +// NULL control chars... + INVALID, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, +// control chars... + ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, +// ' ' ! " # $ % & ' ( ) * + , - . / + ESCAPE, PASS, ESCAPE, ESCAPE, PASS, ESCAPE, PASS, PASS, PASS, PASS, PASS, PASS, PASS, UNESCAPE,SPECIAL, PASS, +// 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,PASS, PASS, ESCAPE, PASS, ESCAPE, ESCAPE, +// @ A B C D E F G H I J K L M N O + PASS, UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE, +// P Q R S T U V W X Y Z [ \ ] ^ _ + UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,PASS, ESCAPE, PASS, ESCAPE, UNESCAPE, +// ` a b c d e f g h i j k l m n o + ESCAPE, UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE, +// p q r s t u v w x y z { | } ~ + UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,UNESCAPE,ESCAPE, ESCAPE, ESCAPE, UNESCAPE,ESCAPE, +// ...all the high-bit characters are escaped + ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, + ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, + ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, + ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, + ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, + ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, + ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, + ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE, ESCAPE}; + +enum DotDisposition { + // The given dot is just part of a filename and is not special. + NOT_A_DIRECTORY, + + // The given dot is the current directory. + DIRECTORY_CUR, + + // The given dot is the first of a double dot that should take us up one. + DIRECTORY_UP +}; + +// When the path resolver finds a dot, this function is called with the +// character following that dot to see what it is. The return value +// indicates what type this dot is (see above). This code handles the case +// where the dot is at the end of the input. +// +// |*consumed_len| will contain the number of characters in the input that +// express what we found. +// +// If the input is "../foo", |after_dot| = 1, |end| = 6, and +// at the end, |*consumed_len| = 2 for the "./" this function consumed. The +// original dot length should be handled by the caller. +template +DotDisposition ClassifyAfterDot(const CHAR* spec, int after_dot, + int end, int* consumed_len) { + if (after_dot == end) { + // Single dot at the end. + *consumed_len = 0; + return DIRECTORY_CUR; + } + if (IsURLSlash(spec[after_dot])) { + // Single dot followed by a slash. + *consumed_len = 1; // Consume the slash + return DIRECTORY_CUR; + } + + int second_dot_len = IsDot(spec, after_dot, end); + if (second_dot_len) { + int after_second_dot = after_dot + second_dot_len; + if (after_second_dot == end) { + // Double dot at the end. + *consumed_len = second_dot_len; + return DIRECTORY_UP; + } + if (IsURLSlash(spec[after_second_dot])) { + // Double dot followed by a slash. + *consumed_len = second_dot_len + 1; + return DIRECTORY_UP; + } + } + + // The dots are followed by something else, not a directory. + *consumed_len = 0; + return NOT_A_DIRECTORY; +} + +// Rewinds the output to the previous slash. It is assumed that the output +// ends with a slash and this doesn't count (we call this when we are +// appending directory paths, so the previous path component has and ending +// slash). +// +// This will stop at the first slash (assumed to be at position +// |path_begin_in_output| and not go any higher than that. Some web pages +// do ".." too many times, so we need to handle that brokenness. +// +// It searches for a literal slash rather than including a backslash as well +// because it is run only on the canonical output. +// +// The output is guaranteed to end in a slash when this function completes. +void BackUpToPreviousSlash(int path_begin_in_output, + CanonOutput* output) { + DCHECK(output->length() > 0); + + int i = output->length() - 1; + DCHECK(output->at(i) == '/'); + if (i == path_begin_in_output) + return; // We're at the first slash, nothing to do. + + // Now back up (skipping the trailing slash) until we find another slash. + i--; + while (output->at(i) != '/' && i > path_begin_in_output) + i--; + + // Now shrink the output to just include that last slash we found. + output->set_length(i + 1); +} + +// Looks for problematic nested escape sequences and escapes the output as +// needed to ensure they can't be misinterpreted. +// +// Our concern is that in input escape sequence that's invalid because it +// contains nested escape sequences might look valid once those are unescaped. +// For example, "%%300" is not a valid escape sequence, but after unescaping the +// inner "%30" this becomes "%00" which is valid. Leaving this in the output +// string can result in callers re-canonicalizing the string and unescaping this +// sequence, thus resulting in something fundamentally different than the +// original input here. This can cause a variety of problems. +// +// This function is called after we've just unescaped a sequence that's within +// two output characters of a previous '%' that we know didn't begin a valid +// escape sequence in the input string. We look for whether the output is going +// to turn into a valid escape sequence, and if so, convert the initial '%' into +// an escaped "%25" so the output can't be misinterpreted. +// +// |spec| is the input string we're canonicalizing. +// |next_input_index| is the index of the next unprocessed character in |spec|. +// |input_len| is the length of |spec|. +// |last_invalid_percent_index| is the index in |output| of a previously-seen +// '%' character. The caller knows this '%' character isn't followed by a valid +// escape sequence in the input string. +// |output| is the canonicalized output thus far. The caller guarantees this +// ends with a '%' followed by one or two characters, and the '%' is the one +// pointed to by |last_invalid_percent_index|. The last character in the string +// was just unescaped. +template +void CheckForNestedEscapes(const CHAR* spec, + int next_input_index, + int input_len, + int last_invalid_percent_index, + CanonOutput* output) { + const int length = output->length(); + const char last_unescaped_char = output->at(length - 1); + + // If |output| currently looks like "%c", we need to try appending the next + // input character to see if this will result in a problematic escape + // sequence. Note that this won't trigger on the first nested escape of a + // two-escape sequence like "%%30%30" -- we'll allow the conversion to + // "%0%30" -- but the second nested escape will be caught by this function + // when it's called again in that case. + const bool append_next_char = last_invalid_percent_index == length - 2; + if (append_next_char) { + // If the input doesn't contain a 7-bit character next, this case won't be a + // problem. + if ((next_input_index == input_len) || (spec[next_input_index] >= 0x80)) + return; + output->push_back(static_cast(spec[next_input_index])); + } + + // Now output ends like "%cc". Try to unescape this. + int begin = last_invalid_percent_index; + unsigned char temp; + if (DecodeEscaped(output->data(), &begin, output->length(), &temp)) { + // New escape sequence found. Overwrite the characters following the '%' + // with "25", and push_back() the one or two characters that were following + // the '%' when we were called. + if (!append_next_char) + output->push_back(output->at(last_invalid_percent_index + 1)); + output->set(last_invalid_percent_index + 1, '2'); + output->set(last_invalid_percent_index + 2, '5'); + output->push_back(last_unescaped_char); + } else if (append_next_char) { + // Not a valid escape sequence, but we still need to undo appending the next + // source character so the caller can process it normally. + output->set_length(length); + } +} + +// Appends the given path to the output. It assumes that if the input path +// starts with a slash, it should be copied to the output. If no path has +// already been appended to the output (the case when not resolving +// relative URLs), the path should begin with a slash. +// +// If there are already path components (this mode is used when appending +// relative paths for resolving), it assumes that the output already has +// a trailing slash and that if the input begins with a slash, it should be +// copied to the output. +// +// We do not collapse multiple slashes in a row to a single slash. It seems +// no web browsers do this, and we don't want incompatibilities, even though +// it would be correct for most systems. +template +bool DoPartialPath(const CHAR* spec, + const Component& path, + int path_begin_in_output, + CanonOutput* output) { + int end = path.end(); + + // We use this variable to minimize the amount of work done when unescaping -- + // we'll only call CheckForNestedEscapes() when this points at one of the last + // couple of characters in |output|. + int last_invalid_percent_index = INT_MIN; + + bool success = true; + for (int i = path.begin; i < end; i++) { + UCHAR uch = static_cast(spec[i]); + if (sizeof(CHAR) > 1 && uch >= 0x80) { + // We only need to test wide input for having non-ASCII characters. For + // narrow input, we'll always just use the lookup table. We don't try to + // do anything tricky with decoding/validating UTF-8. This function will + // read one or two UTF-16 characters and append the output as UTF-8. This + // call will be removed in 8-bit mode. + success &= AppendUTF8EscapedChar(spec, &i, end, output); + } else { + // Normal ASCII character or 8-bit input, use the lookup table. + unsigned char out_ch = static_cast(uch); + unsigned char flags = kPathCharLookup[out_ch]; + if (flags & SPECIAL) { + // Needs special handling of some sort. + int dotlen; + if ((dotlen = IsDot(spec, i, end)) > 0) { + // See if this dot was preceded by a slash in the output. We + // assume that when canonicalizing paths, they will always + // start with a slash and not a dot, so we don't have to + // bounds check the output. + // + // Note that we check this in the case of dots so we don't have to + // special case slashes. Since slashes are much more common than + // dots, this actually increases performance measurably (though + // slightly). + DCHECK(output->length() > path_begin_in_output); + if (output->length() > path_begin_in_output && + output->at(output->length() - 1) == '/') { + // Slash followed by a dot, check to see if this is means relative + int consumed_len; + switch (ClassifyAfterDot(spec, i + dotlen, end, + &consumed_len)) { + case NOT_A_DIRECTORY: + // Copy the dot to the output, it means nothing special. + output->push_back('.'); + i += dotlen - 1; + break; + case DIRECTORY_CUR: // Current directory, just skip the input. + i += dotlen + consumed_len - 1; + break; + case DIRECTORY_UP: + BackUpToPreviousSlash(path_begin_in_output, output); + i += dotlen + consumed_len - 1; + break; + } + } else { + // This dot is not preceded by a slash, it is just part of some + // file name. + output->push_back('.'); + i += dotlen - 1; + } + + } else if (out_ch == '\\') { + // Convert backslashes to forward slashes + output->push_back('/'); + + } else if (out_ch == '%') { + // Handle escape sequences. + unsigned char unescaped_value; + if (DecodeEscaped(spec, &i, end, &unescaped_value)) { + // Valid escape sequence, see if we keep, reject, or unescape it. + // Note that at this point DecodeEscape() will have advanced |i| to + // the last character of the escape sequence. + char unescaped_flags = kPathCharLookup[unescaped_value]; + + if (unescaped_flags & UNESCAPE) { + // This escaped value shouldn't be escaped. Try to copy it. + output->push_back(unescaped_value); + // If we just unescaped a value within 2 output characters of the + // '%' from a previously-detected invalid escape sequence, we + // might have an input string with problematic nested escape + // sequences; detect and fix them. + if (last_invalid_percent_index >= (output->length() - 3)) { + CheckForNestedEscapes(spec, i + 1, end, + last_invalid_percent_index, output); + } + } else { + // Either this is an invalid escaped character, or it's a valid + // escaped character we should keep escaped. In the first case we + // should just copy it exactly and remember the error. In the + // second we also copy exactly in case the server is sensitive to + // changing the case of any hex letters. + output->push_back('%'); + output->push_back(static_cast(spec[i - 1])); + output->push_back(static_cast(spec[i])); + if (unescaped_flags & INVALID_BIT) + success = false; + } + } else { + // Invalid escape sequence. IE7+ rejects any URLs with such + // sequences, while other browsers pass them through unchanged. We + // use the permissive behavior. + // TODO(brettw): Consider testing IE's strict behavior, which would + // allow removing the code to handle nested escapes above. + last_invalid_percent_index = output->length(); + output->push_back('%'); + } + + } else if (flags & INVALID_BIT) { + // For NULLs, etc. fail. + AppendEscapedChar(out_ch, output); + success = false; + + } else if (flags & ESCAPE_BIT) { + // This character should be escaped. + AppendEscapedChar(out_ch, output); + } + } else { + // Nothing special about this character, just append it. + output->push_back(out_ch); + } + } + } + return success; +} + +template +bool DoPath(const CHAR* spec, + const Component& path, + CanonOutput* output, + Component* out_path) { + bool success = true; + out_path->begin = output->length(); + if (path.len > 0) { + // Write out an initial slash if the input has none. If we just parse a URL + // and then canonicalize it, it will of course have a slash already. This + // check is for the replacement and relative URL resolving cases of file + // URLs. + if (!IsURLSlash(spec[path.begin])) + output->push_back('/'); + + success = DoPartialPath(spec, path, out_path->begin, output); + } else { + // No input, canonical path is a slash. + output->push_back('/'); + } + out_path->len = output->length() - out_path->begin; + return success; +} + +} // namespace + +bool CanonicalizePath(const char* spec, + const Component& path, + CanonOutput* output, + Component* out_path) { + return DoPath(spec, path, output, out_path); +} + +bool CanonicalizePath(const base::char16* spec, + const Component& path, + CanonOutput* output, + Component* out_path) { + return DoPath(spec, path, output, out_path); +} + +bool CanonicalizePartialPath(const char* spec, + const Component& path, + int path_begin_in_output, + CanonOutput* output) { + return DoPartialPath(spec, path, path_begin_in_output, + output); +} + +bool CanonicalizePartialPath(const base::char16* spec, + const Component& path, + int path_begin_in_output, + CanonOutput* output) { + return DoPartialPath(spec, path, + path_begin_in_output, + output); +} + +} // namespace url diff --git a/src/url/url_canon_pathurl.cc b/src/url/url_canon_pathurl.cc new file mode 100644 index 00000000..494fbdaf --- /dev/null +++ b/src/url/url_canon_pathurl.cc @@ -0,0 +1,121 @@ +// 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. + +// Functions for canonicalizing "path" URLs. Not to be confused with the path +// of a URL, these are URLs that have no authority section, only a path. For +// example, "javascript:" and "data:". + +#include "url/url_canon.h" +#include "url/url_canon_internal.h" + +namespace url { + +namespace { + +// Canonicalize the given |component| from |source| into |output| and +// |new_component|. If |separator| is non-zero, it is pre-pended to |output| +// prior to the canonicalized component; i.e. for the '?' or '#' characters. +template +bool DoCanonicalizePathComponent(const CHAR* source, + const Component& component, + char separator, + CanonOutput* output, + Component* new_component) { + bool success = true; + if (component.is_valid()) { + if (separator) + output->push_back(separator); + // Copy the path using path URL's more lax escaping rules (think for + // javascript:). We convert to UTF-8 and escape non-ASCII, but leave all + // ASCII characters alone. This helps readability of JavaStript. + new_component->begin = output->length(); + int end = component.end(); + for (int i = component.begin; i < end; i++) { + UCHAR uch = static_cast(source[i]); + if (uch < 0x20 || uch >= 0x80) + success &= AppendUTF8EscapedChar(source, &i, end, output); + else + output->push_back(static_cast(uch)); + } + new_component->len = output->length() - new_component->begin; + } else { + // Empty part. + new_component->reset(); + } + return success; +} + +template +bool DoCanonicalizePathURL(const URLComponentSource& source, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + // Scheme: this will append the colon. + bool success = CanonicalizeScheme(source.scheme, parsed.scheme, + output, &new_parsed->scheme); + + // We assume there's no authority for path URLs. Note that hosts should never + // have -1 length. + new_parsed->username.reset(); + new_parsed->password.reset(); + new_parsed->host.reset(); + new_parsed->port.reset(); + // We allow path URLs to have the path, query and fragment components, but we + // will canonicalize each of the via the weaker path URL rules. + success &= DoCanonicalizePathComponent( + source.path, parsed.path, '\0', output, &new_parsed->path); + success &= DoCanonicalizePathComponent( + source.query, parsed.query, '?', output, &new_parsed->query); + success &= DoCanonicalizePathComponent( + source.ref, parsed.ref, '#', output, &new_parsed->ref); + + return success; +} + +} // namespace + +bool CanonicalizePathURL(const char* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizePathURL( + URLComponentSource(spec), parsed, output, new_parsed); +} + +bool CanonicalizePathURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizePathURL( + URLComponentSource(spec), parsed, output, new_parsed); +} + +bool ReplacePathURL(const char* base, + const Parsed& base_parsed, + const Replacements& replacements, + CanonOutput* output, + Parsed* new_parsed) { + URLComponentSource source(base); + Parsed parsed(base_parsed); + SetupOverrideComponents(base, replacements, &source, &parsed); + return DoCanonicalizePathURL( + source, parsed, output, new_parsed); +} + +bool ReplacePathURL(const char* base, + const Parsed& base_parsed, + const Replacements& replacements, + CanonOutput* output, + Parsed* new_parsed) { + RawCanonOutput<1024> utf8; + URLComponentSource source(base); + Parsed parsed(base_parsed); + SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); + return DoCanonicalizePathURL( + source, parsed, output, new_parsed); +} + +} // namespace url diff --git a/src/url/url_canon_query.cc b/src/url/url_canon_query.cc new file mode 100644 index 00000000..bf59d104 --- /dev/null +++ b/src/url/url_canon_query.cc @@ -0,0 +1,164 @@ +// 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 "url/url_canon.h" +#include "url/url_canon_internal.h" + +// Query canonicalization in IE +// ---------------------------- +// IE is very permissive for query parameters specified in links on the page +// (in contrast to links that it constructs itself based on form data). It does +// not unescape any character. It does not reject any escape sequence (be they +// invalid like "%2y" or freaky like %00). +// +// IE only escapes spaces and nothing else. Embedded NULLs, tabs (0x09), +// LF (0x0a), and CR (0x0d) are removed (this probably happens at an earlier +// layer since they are removed from all portions of the URL). All other +// characters are passed unmodified. Invalid UTF-16 sequences are preserved as +// well, with each character in the input being converted to UTF-8. It is the +// server's job to make sense of this invalid query. +// +// Invalid multibyte sequences (for example, invalid UTF-8 on a UTF-8 page) +// are converted to the invalid character and sent as unescaped UTF-8 (0xef, +// 0xbf, 0xbd). This may not be canonicalization, the parser may generate these +// strings before the URL handler ever sees them. +// +// Our query canonicalization +// -------------------------- +// We escape all non-ASCII characters and control characters, like Firefox. +// This is more conformant to the URL spec, and there do not seem to be many +// problems relating to Firefox's behavior. +// +// Like IE, we will never unescape (although the application may want to try +// unescaping to present the user with a more understandable URL). We will +// replace all invalid sequences (including invalid UTF-16 sequences, which IE +// doesn't) with the "invalid character," and we will escape it. + +namespace url { + +namespace { + +// Returns true if the characters starting at |begin| and going until |end| +// (non-inclusive) are all representable in 7-bits. +template +bool IsAllASCII(const CHAR* spec, const Component& query) { + int end = query.end(); + for (int i = query.begin; i < end; i++) { + if (static_cast(spec[i]) >= 0x80) + return false; + } + return true; +} + +// Appends the given string to the output, escaping characters that do not +// match the given |type| in SharedCharTypes. This version will accept 8 or 16 +// bit characters, but assumes that they have only 7-bit values. It also assumes +// that all UTF-8 values are correct, so doesn't bother checking +template +void AppendRaw8BitQueryString(const CHAR* source, int length, + CanonOutput* output) { + for (int i = 0; i < length; i++) { + if (!IsQueryChar(static_cast(source[i]))) + AppendEscapedChar(static_cast(source[i]), output); + else // Doesn't need escaping. + output->push_back(static_cast(source[i])); + } +} + +// Runs the converter on the given UTF-8 input. Since the converter expects +// UTF-16, we have to convert first. The converter must be non-NULL. +void RunConverter(const char* spec, + const Component& query, + CharsetConverter* converter, + CanonOutput* output) { + // This function will replace any misencoded values with the invalid + // character. This is what we want so we don't have to check for error. + RawCanonOutputW<1024> utf16; + ConvertUTF8ToUTF16(&spec[query.begin], query.len, &utf16); + converter->ConvertFromUTF16(utf16.data(), utf16.length(), output); +} + +// Runs the converter with the given UTF-16 input. We don't have to do +// anything, but this overridden function allows us to use the same code +// for both UTF-8 and UTF-16 input. +void RunConverter(const base::char16* spec, + const Component& query, + CharsetConverter* converter, + CanonOutput* output) { + converter->ConvertFromUTF16(&spec[query.begin], query.len, output); +} + +template +void DoConvertToQueryEncoding(const CHAR* spec, + const Component& query, + CharsetConverter* converter, + CanonOutput* output) { + if (IsAllASCII(spec, query)) { + // Easy: the input can just appended with no character set conversions. + AppendRaw8BitQueryString(&spec[query.begin], query.len, output); + + } else { + // Harder: convert to the proper encoding first. + if (converter) { + // Run the converter to get an 8-bit string, then append it, escaping + // necessary values. + RawCanonOutput<1024> eight_bit; + RunConverter(spec, query, converter, &eight_bit); + AppendRaw8BitQueryString(eight_bit.data(), eight_bit.length(), output); + + } else { + // No converter, do our own UTF-8 conversion. + AppendStringOfType(&spec[query.begin], query.len, CHAR_QUERY, output); + } + } +} + +template +void DoCanonicalizeQuery(const CHAR* spec, + const Component& query, + CharsetConverter* converter, + CanonOutput* output, + Component* out_query) { + if (query.len < 0) { + *out_query = Component(); + return; + } + + output->push_back('?'); + out_query->begin = output->length(); + + DoConvertToQueryEncoding(spec, query, converter, output); + + out_query->len = output->length() - out_query->begin; +} + +} // namespace + +void CanonicalizeQuery(const char* spec, + const Component& query, + CharsetConverter* converter, + CanonOutput* output, + Component* out_query) { + DoCanonicalizeQuery(spec, query, converter, + output, out_query); +} + +void CanonicalizeQuery(const base::char16* spec, + const Component& query, + CharsetConverter* converter, + CanonOutput* output, + Component* out_query) { + DoCanonicalizeQuery(spec, query, converter, + output, out_query); +} + +void ConvertUTF16ToQueryEncoding(const base::char16* input, + const Component& query, + CharsetConverter* converter, + CanonOutput* output) { + DoConvertToQueryEncoding(input, query, + converter, output); +} + +} // namespace url diff --git a/src/url/url_canon_relative.cc b/src/url/url_canon_relative.cc new file mode 100644 index 00000000..e34ea2fa --- /dev/null +++ b/src/url/url_canon_relative.cc @@ -0,0 +1,564 @@ +// 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. + +// Canonicalizer functions for working with and resolving relative URLs. + +#include "base/logging.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_constants.h" +#include "url/url_file.h" +#include "url/url_parse_internal.h" +#include "url/url_util_internal.h" + +namespace url { + +namespace { + +// Firefox does a case-sensitive compare (which is probably wrong--Mozilla bug +// 379034), whereas IE is case-insensitive. +// +// We choose to be more permissive like IE. We don't need to worry about +// unescaping or anything here: neither IE or Firefox allow this. We also +// don't have to worry about invalid scheme characters since we are comparing +// against the canonical scheme of the base. +// +// The base URL should always be canonical, therefore it should be ASCII. +template +bool AreSchemesEqual(const char* base, + const Component& base_scheme, + const CHAR* cmp, + const Component& cmp_scheme) { + if (base_scheme.len != cmp_scheme.len) + return false; + for (int i = 0; i < base_scheme.len; i++) { + // We assume the base is already canonical, so we don't have to + // canonicalize it. + if (CanonicalSchemeChar(cmp[cmp_scheme.begin + i]) != + base[base_scheme.begin + i]) + return false; + } + return true; +} + +#ifdef WIN32 + +// Here, we also allow Windows paths to be represented as "/C:/" so we can be +// consistent about URL paths beginning with slashes. This function is like +// DoesBeginWindowsDrivePath except that it also requires a slash at the +// beginning. +template +bool DoesBeginSlashWindowsDriveSpec(const CHAR* spec, int start_offset, + int spec_len) { + if (start_offset >= spec_len) + return false; + return IsURLSlash(spec[start_offset]) && + DoesBeginWindowsDriveSpec(spec, start_offset + 1, spec_len); +} + +#endif // WIN32 + +// See IsRelativeURL in the header file for usage. +template +bool DoIsRelativeURL(const char* base, + const Parsed& base_parsed, + const CHAR* url, + int url_len, + bool is_base_hierarchical, + bool* is_relative, + Component* relative_component) { + *is_relative = false; // So we can default later to not relative. + + // Trim whitespace and construct a new range for the substring. + int begin = 0; + TrimURL(url, &begin, &url_len); + if (begin >= url_len) { + // Empty URLs are relative, but do nothing. + if (!is_base_hierarchical) { + // Don't allow relative URLs if the base scheme doesn't support it. + return false; + } + *relative_component = Component(begin, 0); + *is_relative = true; + return true; + } + +#ifdef WIN32 + // We special case paths like "C:\foo" so they can link directly to the + // file on Windows (IE compatibility). The security domain stuff should + // prevent a link like this from actually being followed if its on a + // web page. + // + // We treat "C:/foo" as an absolute URL. We can go ahead and treat "/c:/" + // as relative, as this will just replace the path when the base scheme + // is a file and the answer will still be correct. + // + // We require strict backslashes when detecting UNC since two forward + // slashes should be treated a a relative URL with a hostname. + if (DoesBeginWindowsDriveSpec(url, begin, url_len) || + DoesBeginUNCPath(url, begin, url_len, true)) + return true; +#endif // WIN32 + + // See if we've got a scheme, if not, we know this is a relative URL. + // BUT, just because we have a scheme, doesn't make it absolute. + // "http:foo.html" is a relative URL with path "foo.html". If the scheme is + // empty, we treat it as relative (":foo"), like IE does. + Component scheme; + const bool scheme_is_empty = + !ExtractScheme(url, url_len, &scheme) || scheme.len == 0; + if (scheme_is_empty) { + if (url[begin] == '#') { + // |url| is a bare fragment (e.g. "#foo"). This can be resolved against + // any base. Fall-through. + } else if (!is_base_hierarchical) { + // Don't allow relative URLs if the base scheme doesn't support it. + return false; + } + + *relative_component = MakeRange(begin, url_len); + *is_relative = true; + return true; + } + + // If the scheme isn't valid, then it's relative. + int scheme_end = scheme.end(); + for (int i = scheme.begin; i < scheme_end; i++) { + if (!CanonicalSchemeChar(url[i])) { + if (!is_base_hierarchical) { + // Don't allow relative URLs if the base scheme doesn't support it. + return false; + } + *relative_component = MakeRange(begin, url_len); + *is_relative = true; + return true; + } + } + + // If the scheme is not the same, then we can't count it as relative. + if (!AreSchemesEqual(base, base_parsed.scheme, url, scheme)) + return true; + + // When the scheme that they both share is not hierarchical, treat the + // incoming scheme as absolute (this way with the base of "data:foo", + // "data:bar" will be reported as absolute. + if (!is_base_hierarchical) + return true; + + int colon_offset = scheme.end(); + + // If it's a filesystem URL, the only valid way to make it relative is not to + // supply a scheme. There's no equivalent to e.g. http:index.html. + if (CompareSchemeComponent(url, scheme, kFileSystemScheme)) + return true; + + // ExtractScheme guarantees that the colon immediately follows what it + // considers to be the scheme. CountConsecutiveSlashes will handle the + // case where the begin offset is the end of the input. + int num_slashes = CountConsecutiveSlashes(url, colon_offset + 1, url_len); + + if (num_slashes == 0 || num_slashes == 1) { + // No slashes means it's a relative path like "http:foo.html". One slash + // is an absolute path. "http:/home/foo.html" + *is_relative = true; + *relative_component = MakeRange(colon_offset + 1, url_len); + return true; + } + + // Two or more slashes after the scheme we treat as absolute. + return true; +} + +// Copies all characters in the range [begin, end) of |spec| to the output, +// up until and including the last slash. There should be a slash in the +// range, if not, nothing will be copied. +// +// For stardard URLs the input should be canonical, but when resolving relative +// URLs on a non-standard base (like "data:") the input can be anything. +void CopyToLastSlash(const char* spec, + int begin, + int end, + CanonOutput* output) { + // Find the last slash. + int last_slash = -1; + for (int i = end - 1; i >= begin; i--) { + if (spec[i] == '/' || spec[i] == '\\') { + last_slash = i; + break; + } + } + if (last_slash < 0) + return; // No slash. + + // Copy. + for (int i = begin; i <= last_slash; i++) + output->push_back(spec[i]); +} + +// Copies a single component from the source to the output. This is used +// when resolving relative URLs and a given component is unchanged. Since the +// source should already be canonical, we don't have to do anything special, +// and the input is ASCII. +void CopyOneComponent(const char* source, + const Component& source_component, + CanonOutput* output, + Component* output_component) { + if (source_component.len < 0) { + // This component is not present. + *output_component = Component(); + return; + } + + output_component->begin = output->length(); + int source_end = source_component.end(); + for (int i = source_component.begin; i < source_end; i++) + output->push_back(source[i]); + output_component->len = output->length() - output_component->begin; +} + +#ifdef WIN32 + +// Called on Windows when the base URL is a file URL, this will copy the "C:" +// to the output, if there is a drive letter and if that drive letter is not +// being overridden by the relative URL. Otherwise, do nothing. +// +// It will return the index of the beginning of the next character in the +// base to be processed: if there is a "C:", the slash after it, or if +// there is no drive letter, the slash at the beginning of the path, or +// the end of the base. This can be used as the starting offset for further +// path processing. +template +int CopyBaseDriveSpecIfNecessary(const char* base_url, + int base_path_begin, + int base_path_end, + const CHAR* relative_url, + int path_start, + int relative_url_len, + CanonOutput* output) { + if (base_path_begin >= base_path_end) + return base_path_begin; // No path. + + // If the relative begins with a drive spec, don't do anything. The existing + // drive spec in the base will be replaced. + if (DoesBeginWindowsDriveSpec(relative_url, path_start, relative_url_len)) { + return base_path_begin; // Relative URL path is "C:/foo" + } + + // The path should begin with a slash (as all canonical paths do). We check + // if it is followed by a drive letter and copy it. + if (DoesBeginSlashWindowsDriveSpec(base_url, + base_path_begin, + base_path_end)) { + // Copy the two-character drive spec to the output. It will now look like + // "file:///C:" so the rest of it can be treated like a standard path. + output->push_back('/'); + output->push_back(base_url[base_path_begin + 1]); + output->push_back(base_url[base_path_begin + 2]); + return base_path_begin + 3; + } + + return base_path_begin; +} + +#endif // WIN32 + +// A subroutine of DoResolveRelativeURL, this resolves the URL knowning that +// the input is a relative path or less (qyuery or ref). +template +bool DoResolveRelativePath(const char* base_url, + const Parsed& base_parsed, + bool base_is_file, + const CHAR* relative_url, + const Component& relative_component, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* out_parsed) { + bool success = true; + + // We know the authority section didn't change, copy it to the output. We + // also know we have a path so can copy up to there. + Component path, query, ref; + ParsePathInternal(relative_url, relative_component, &path, &query, &ref); + // Canonical URLs always have a path, so we can use that offset. + output->Append(base_url, base_parsed.path.begin); + + if (path.len > 0) { + // The path is replaced or modified. + int true_path_begin = output->length(); + + // For file: URLs on Windows, we don't want to treat the drive letter and + // colon as part of the path for relative file resolution when the + // incoming URL does not provide a drive spec. We save the true path + // beginning so we can fix it up after we are done. + int base_path_begin = base_parsed.path.begin; +#ifdef WIN32 + if (base_is_file) { + base_path_begin = CopyBaseDriveSpecIfNecessary( + base_url, base_parsed.path.begin, base_parsed.path.end(), + relative_url, relative_component.begin, relative_component.end(), + output); + // Now the output looks like either "file://" or "file:///C:" + // and we can start appending the rest of the path. |base_path_begin| + // points to the character in the base that comes next. + } +#endif // WIN32 + + if (IsURLSlash(relative_url[path.begin])) { + // Easy case: the path is an absolute path on the server, so we can + // just replace everything from the path on with the new versions. + // Since the input should be canonical hierarchical URL, we should + // always have a path. + success &= CanonicalizePath(relative_url, path, + output, &out_parsed->path); + } else { + // Relative path, replace the query, and reference. We take the + // original path with the file part stripped, and append the new path. + // The canonicalizer will take care of resolving ".." and "." + int path_begin = output->length(); + CopyToLastSlash(base_url, base_path_begin, base_parsed.path.end(), + output); + success &= CanonicalizePartialPath(relative_url, path, path_begin, + output); + out_parsed->path = MakeRange(path_begin, output->length()); + + // Copy the rest of the stuff after the path from the relative path. + } + + // Finish with the query and reference part (these can't fail). + CanonicalizeQuery(relative_url, query, query_converter, + output, &out_parsed->query); + CanonicalizeRef(relative_url, ref, output, &out_parsed->ref); + + // Fix the path beginning to add back the "C:" we may have written above. + out_parsed->path = MakeRange(true_path_begin, out_parsed->path.end()); + return success; + } + + // If we get here, the path is unchanged: copy to output. + CopyOneComponent(base_url, base_parsed.path, output, &out_parsed->path); + + if (query.is_valid()) { + // Just the query specified, replace the query and reference (ignore + // failures for refs) + CanonicalizeQuery(relative_url, query, query_converter, + output, &out_parsed->query); + CanonicalizeRef(relative_url, ref, output, &out_parsed->ref); + return success; + } + + // If we get here, the query is unchanged: copy to output. Note that the + // range of the query parameter doesn't include the question mark, so we + // have to add it manually if there is a component. + if (base_parsed.query.is_valid()) + output->push_back('?'); + CopyOneComponent(base_url, base_parsed.query, output, &out_parsed->query); + + if (ref.is_valid()) { + // Just the reference specified: replace it (ignoring failures). + CanonicalizeRef(relative_url, ref, output, &out_parsed->ref); + return success; + } + + // We should always have something to do in this function, the caller checks + // that some component is being replaced. + DCHECK(false) << "Not reached"; + return success; +} + +// Resolves a relative URL that contains a host. Typically, these will +// be of the form "//www.google.com/foo/bar?baz#ref" and the only thing which +// should be kept from the original URL is the scheme. +template +bool DoResolveRelativeHost(const char* base_url, + const Parsed& base_parsed, + const CHAR* relative_url, + const Component& relative_component, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* out_parsed) { + // Parse the relative URL, just like we would for anything following a + // scheme. + Parsed relative_parsed; // Everything but the scheme is valid. + ParseAfterScheme(relative_url, relative_component.end(), + relative_component.begin, &relative_parsed); + + // Now we can just use the replacement function to replace all the necessary + // parts of the old URL with the new one. + Replacements replacements; + replacements.SetUsername(relative_url, relative_parsed.username); + replacements.SetPassword(relative_url, relative_parsed.password); + replacements.SetHost(relative_url, relative_parsed.host); + replacements.SetPort(relative_url, relative_parsed.port); + replacements.SetPath(relative_url, relative_parsed.path); + replacements.SetQuery(relative_url, relative_parsed.query); + replacements.SetRef(relative_url, relative_parsed.ref); + + return ReplaceStandardURL(base_url, base_parsed, replacements, + query_converter, output, out_parsed); +} + +// Resolves a relative URL that happens to be an absolute file path. Examples +// include: "//hostname/path", "/c:/foo", and "//hostname/c:/foo". +template +bool DoResolveAbsoluteFile(const CHAR* relative_url, + const Component& relative_component, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* out_parsed) { + // Parse the file URL. The file URl parsing function uses the same logic + // as we do for determining if the file is absolute, in which case it will + // not bother to look for a scheme. + Parsed relative_parsed; + ParseFileURL(&relative_url[relative_component.begin], relative_component.len, + &relative_parsed); + + return CanonicalizeFileURL(&relative_url[relative_component.begin], + relative_component.len, relative_parsed, + query_converter, output, out_parsed); +} + +// TODO(brettw) treat two slashes as root like Mozilla for FTP? +template +bool DoResolveRelativeURL(const char* base_url, + const Parsed& base_parsed, + bool base_is_file, + const CHAR* relative_url, + const Component& relative_component, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* out_parsed) { + // Starting point for our output parsed. We'll fix what we change. + *out_parsed = base_parsed; + + // Sanity check: the input should have a host or we'll break badly below. + // We can only resolve relative URLs with base URLs that have hosts and + // paths (even the default path of "/" is OK). + // + // We allow hosts with no length so we can handle file URLs, for example. + if (base_parsed.path.len <= 0) { + // On error, return the input (resolving a relative URL on a non-relative + // base = the base). + int base_len = base_parsed.Length(); + for (int i = 0; i < base_len; i++) + output->push_back(base_url[i]); + return false; + } + + if (relative_component.len <= 0) { + // Empty relative URL, leave unchanged, only removing the ref component. + int base_len = base_parsed.Length(); + base_len -= base_parsed.ref.len + 1; + out_parsed->ref.reset(); + output->Append(base_url, base_len); + return true; + } + + int num_slashes = CountConsecutiveSlashes( + relative_url, relative_component.begin, relative_component.end()); + +#ifdef WIN32 + // On Windows, two slashes for a file path (regardless of which direction + // they are) means that it's UNC. Two backslashes on any base scheme mean + // that it's an absolute UNC path (we use the base_is_file flag to control + // how strict the UNC finder is). + // + // We also allow Windows absolute drive specs on any scheme (for example + // "c:\foo") like IE does. There must be no preceding slashes in this + // case (we reject anything like "/c:/foo") because that should be treated + // as a path. For file URLs, we allow any number of slashes since that would + // be setting the path. + // + // This assumes the absolute path resolver handles absolute URLs like this + // properly. DoCanonicalize does this. + int after_slashes = relative_component.begin + num_slashes; + if (DoesBeginUNCPath(relative_url, relative_component.begin, + relative_component.end(), !base_is_file) || + ((num_slashes == 0 || base_is_file) && + DoesBeginWindowsDriveSpec( + relative_url, after_slashes, relative_component.end()))) { + return DoResolveAbsoluteFile(relative_url, relative_component, + query_converter, output, out_parsed); + } +#else + // Other platforms need explicit handling for file: URLs with multiple + // slashes because the generic scheme parsing always extracts a host, but a + // file: URL only has a host if it has exactly 2 slashes. Even if it does + // have a host, we want to use the special host detection logic for file + // URLs provided by DoResolveAbsoluteFile(), as opposed to the generic host + // detection logic, for consistency with parsing file URLs from scratch. + // This also handles the special case where the URL is only slashes, + // since that doesn't have a host part either. + if (base_is_file && + (num_slashes >= 2 || num_slashes == relative_component.len)) { + return DoResolveAbsoluteFile(relative_url, relative_component, + query_converter, output, out_parsed); + } +#endif + + // Any other double-slashes mean that this is relative to the scheme. + if (num_slashes >= 2) { + return DoResolveRelativeHost(base_url, base_parsed, + relative_url, relative_component, + query_converter, output, out_parsed); + } + + // When we get here, we know that the relative URL is on the same host. + return DoResolveRelativePath(base_url, base_parsed, base_is_file, + relative_url, relative_component, + query_converter, output, out_parsed); +} + +} // namespace + +bool IsRelativeURL(const char* base, + const Parsed& base_parsed, + const char* fragment, + int fragment_len, + bool is_base_hierarchical, + bool* is_relative, + Component* relative_component) { + return DoIsRelativeURL( + base, base_parsed, fragment, fragment_len, is_base_hierarchical, + is_relative, relative_component); +} + +bool IsRelativeURL(const char* base, + const Parsed& base_parsed, + const base::char16* fragment, + int fragment_len, + bool is_base_hierarchical, + bool* is_relative, + Component* relative_component) { + return DoIsRelativeURL( + base, base_parsed, fragment, fragment_len, is_base_hierarchical, + is_relative, relative_component); +} + +bool ResolveRelativeURL(const char* base_url, + const Parsed& base_parsed, + bool base_is_file, + const char* relative_url, + const Component& relative_component, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* out_parsed) { + return DoResolveRelativeURL( + base_url, base_parsed, base_is_file, relative_url, + relative_component, query_converter, output, out_parsed); +} + +bool ResolveRelativeURL(const char* base_url, + const Parsed& base_parsed, + bool base_is_file, + const base::char16* relative_url, + const Component& relative_component, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* out_parsed) { + return DoResolveRelativeURL( + base_url, base_parsed, base_is_file, relative_url, + relative_component, query_converter, output, out_parsed); +} + +} // namespace url diff --git a/src/url/url_canon_stdurl.cc b/src/url/url_canon_stdurl.cc new file mode 100644 index 00000000..7d1758b1 --- /dev/null +++ b/src/url/url_canon_stdurl.cc @@ -0,0 +1,187 @@ +// 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. + +// Functions to canonicalize "standard" URLs, which are ones that have an +// authority section including a host name. + +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_constants.h" + +namespace url { + +namespace { + +template +bool DoCanonicalizeStandardURL(const URLComponentSource& source, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + // Scheme: this will append the colon. + bool success = CanonicalizeScheme(source.scheme, parsed.scheme, + output, &new_parsed->scheme); + + // Authority (username, password, host, port) + bool have_authority; + if (parsed.username.is_valid() || parsed.password.is_valid() || + parsed.host.is_nonempty() || parsed.port.is_valid()) { + have_authority = true; + + // Only write the authority separators when we have a scheme. + if (parsed.scheme.is_valid()) { + output->push_back('/'); + output->push_back('/'); + } + + // User info: the canonicalizer will handle the : and @. + success &= CanonicalizeUserInfo(source.username, parsed.username, + source.password, parsed.password, + output, + &new_parsed->username, + &new_parsed->password); + + success &= CanonicalizeHost(source.host, parsed.host, + output, &new_parsed->host); + + // Host must not be empty for standard URLs. + if (!parsed.host.is_nonempty()) + success = false; + + // Port: the port canonicalizer will handle the colon. + int default_port = DefaultPortForScheme( + &output->data()[new_parsed->scheme.begin], new_parsed->scheme.len); + success &= CanonicalizePort(source.port, parsed.port, default_port, + output, &new_parsed->port); + } else { + // No authority, clear the components. + have_authority = false; + new_parsed->host.reset(); + new_parsed->username.reset(); + new_parsed->password.reset(); + new_parsed->port.reset(); + success = false; // Standard URLs must have an authority. + } + + // Path + if (parsed.path.is_valid()) { + success &= CanonicalizePath(source.path, parsed.path, + output, &new_parsed->path); + } else if (have_authority || + parsed.query.is_valid() || parsed.ref.is_valid()) { + // When we have an empty path, make up a path when we have an authority + // or something following the path. The only time we allow an empty + // output path is when there is nothing else. + new_parsed->path = Component(output->length(), 1); + output->push_back('/'); + } else { + // No path at all + new_parsed->path.reset(); + } + + // Query + CanonicalizeQuery(source.query, parsed.query, query_converter, + output, &new_parsed->query); + + // Ref: ignore failure for this, since the page can probably still be loaded. + CanonicalizeRef(source.ref, parsed.ref, output, &new_parsed->ref); + + return success; +} + +} // namespace + + +// Returns the default port for the given canonical scheme, or PORT_UNSPECIFIED +// if the scheme is unknown. +int DefaultPortForScheme(const char* scheme, int scheme_len) { + int default_port = PORT_UNSPECIFIED; + switch (scheme_len) { + case 4: + if (!strncmp(scheme, kHttpScheme, scheme_len)) + default_port = 80; + break; + case 5: + if (!strncmp(scheme, kHttpsScheme, scheme_len)) + default_port = 443; + break; + case 3: + if (!strncmp(scheme, kFtpScheme, scheme_len)) + default_port = 21; + else if (!strncmp(scheme, kWssScheme, scheme_len)) + default_port = 443; + break; + case 6: + if (!strncmp(scheme, kGopherScheme, scheme_len)) + default_port = 70; + break; + case 2: + if (!strncmp(scheme, kWsScheme, scheme_len)) + default_port = 80; + break; + } + return default_port; +} + +bool CanonicalizeStandardURL(const char* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeStandardURL( + URLComponentSource(spec), parsed, query_converter, + output, new_parsed); +} + +bool CanonicalizeStandardURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeStandardURL( + URLComponentSource(spec), parsed, query_converter, + output, new_parsed); +} + +// It might be nice in the future to optimize this so unchanged components don't +// need to be recanonicalized. This is especially true since the common case for +// ReplaceComponents is removing things we don't want, like reference fragments +// and usernames. These cases can become more efficient if we can assume the +// rest of the URL is OK with these removed (or only the modified parts +// recanonicalized). This would be much more complex to implement, however. +// +// You would also need to update DoReplaceComponents in url_util.cc which +// relies on this re-checking everything (see the comment there for why). +bool ReplaceStandardURL(const char* base, + const Parsed& base_parsed, + const Replacements& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + URLComponentSource source(base); + Parsed parsed(base_parsed); + SetupOverrideComponents(base, replacements, &source, &parsed); + return DoCanonicalizeStandardURL( + source, parsed, query_converter, output, new_parsed); +} + +// For 16-bit replacements, we turn all the replacements into UTF-8 so the +// regular code path can be used. +bool ReplaceStandardURL(const char* base, + const Parsed& base_parsed, + const Replacements& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + RawCanonOutput<1024> utf8; + URLComponentSource source(base); + Parsed parsed(base_parsed); + SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); + return DoCanonicalizeStandardURL( + source, parsed, query_converter, output, new_parsed); +} + +} // namespace url diff --git a/src/url/url_canon_unittest.cc b/src/url/url_canon_unittest.cc new file mode 100644 index 00000000..82edd0e6 --- /dev/null +++ b/src/url/url_canon_unittest.cc @@ -0,0 +1,2168 @@ +// 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 +#include + +#include "base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/third_party/mozilla/url_parse.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_canon_stdstring.h" +#include "url/url_test_utils.h" + +namespace url { + +using test_utils::WStringToUTF16; +using test_utils::ConvertUTF8ToUTF16; +using test_utils::ConvertUTF16ToUTF8; + +namespace { + +struct ComponentCase { + const char* input; + const char* expected; + Component expected_component; + bool expected_success; +}; + +// ComponentCase but with dual 8-bit/16-bit input. Generally, the unit tests +// treat each input as optional, and will only try processing if non-NULL. +// The output is always 8-bit. +struct DualComponentCase { + const char* input8; + const wchar_t* input16; + const char* expected; + Component expected_component; + bool expected_success; +}; + +// Test cases for CanonicalizeIPAddress(). The inputs are identical to +// DualComponentCase, but the output has extra CanonHostInfo fields. +struct IPAddressCase { + const char* input8; + const wchar_t* input16; + const char* expected; + Component expected_component; + + // CanonHostInfo fields, for verbose output. + CanonHostInfo::Family expected_family; + int expected_num_ipv4_components; + const char* expected_address_hex; // Two hex chars per IP address byte. +}; + +std::string BytesToHexString(unsigned char bytes[16], int length) { + EXPECT_TRUE(length == 0 || length == 4 || length == 16) + << "Bad IP address length: " << length; + std::string result; + for (int i = 0; i < length; ++i) { + result.push_back(kHexCharLookup[(bytes[i] >> 4) & 0xf]); + result.push_back(kHexCharLookup[bytes[i] & 0xf]); + } + return result; +} + +struct ReplaceCase { + const char* base; + const char* scheme; + const char* username; + const char* password; + const char* host; + const char* port; + const char* path; + const char* query; + const char* ref; + const char* expected; +}; + +// Magic string used in the replacements code that tells SetupReplComp to +// call the clear function. +const char kDeleteComp[] = "|"; + +// Sets up a replacement for a single component. This is given pointers to +// the set and clear function for the component being replaced, and will +// either set the component (if it exists) or clear it (if the replacement +// string matches kDeleteComp). +// +// This template is currently used only for the 8-bit case, and the strlen +// causes it to fail in other cases. It is left a template in case we have +// tests for wide replacements. +template +void SetupReplComp( + void (Replacements::*set)(const CHAR*, const Component&), + void (Replacements::*clear)(), + Replacements* rep, + const CHAR* str) { + if (str && str[0] == kDeleteComp[0]) { + (rep->*clear)(); + } else if (str) { + (rep->*set)(str, Component(0, static_cast(strlen(str)))); + } +} + +} // namespace + +TEST(URLCanonTest, DoAppendUTF8) { + struct UTF8Case { + unsigned input; + const char* output; + } utf_cases[] = { + // Valid code points. + {0x24, "\x24"}, + {0xA2, "\xC2\xA2"}, + {0x20AC, "\xE2\x82\xAC"}, + {0x24B62, "\xF0\xA4\xAD\xA2"}, + {0x10FFFF, "\xF4\x8F\xBF\xBF"}, + }; + std::string out_str; + for (size_t i = 0; i < arraysize(utf_cases); i++) { + out_str.clear(); + StdStringCanonOutput output(&out_str); + AppendUTF8Value(utf_cases[i].input, &output); + output.Complete(); + EXPECT_EQ(utf_cases[i].output, out_str); + } +} + +#if defined(GTEST_HAS_DEATH_TEST) +// TODO(mattm): Can't run this in debug mode for now, since the DCHECK will +// cause the Chromium stack trace dialog to appear and hang the test. +// See http://crbug.com/49580. +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +#define MAYBE_DoAppendUTF8Invalid DoAppendUTF8Invalid +#else +#define MAYBE_DoAppendUTF8Invalid DISABLED_DoAppendUTF8Invalid +#endif +TEST(URLCanonTest, MAYBE_DoAppendUTF8Invalid) { + std::string out_str; + StdStringCanonOutput output(&out_str); + // Invalid code point (too large). + ASSERT_DEBUG_DEATH({ + AppendUTF8Value(0x110000, &output); + output.Complete(); + EXPECT_EQ("", out_str); + }, ""); +} +#endif // defined(GTEST_HAS_DEATH_TEST) + +TEST(URLCanonTest, UTF) { + // Low-level test that we handle reading, canonicalization, and writing + // UTF-8/UTF-16 strings properly. + struct UTFCase { + const char* input8; + const wchar_t* input16; + bool expected_success; + const char* output; + } utf_cases[] = { + // Valid canonical input should get passed through & escaped. + {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true, "%E4%BD%A0%E5%A5%BD"}, + // Test a character that takes > 16 bits (U+10300 = old italic letter A) + {"\xF0\x90\x8C\x80", L"\xd800\xdf00", true, "%F0%90%8C%80"}, + // Non-shortest-form UTF-8 characters are invalid. The bad character + // should be replaced with the invalid character (EF BF DB in UTF-8). + {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", NULL, false, "%EF%BF%BD%E5%A5%BD"}, + // Invalid UTF-8 sequences should be marked as invalid (the first + // sequence is truncated). + {"\xe4\xa0\xe5\xa5\xbd", L"\xd800\x597d", false, "%EF%BF%BD%E5%A5%BD"}, + // Character going off the end. + {"\xe4\xbd\xa0\xe5\xa5", L"\x4f60\xd800", false, "%E4%BD%A0%EF%BF%BD"}, + // ...same with low surrogates with no high surrogate. + {"\xed\xb0\x80", L"\xdc00", false, "%EF%BF%BD"}, + // Test a UTF-8 encoded surrogate value is marked as invalid. + // ED A0 80 = U+D800 + {"\xed\xa0\x80", NULL, false, "%EF%BF%BD"}, + }; + + std::string out_str; + for (size_t i = 0; i < arraysize(utf_cases); i++) { + if (utf_cases[i].input8) { + out_str.clear(); + StdStringCanonOutput output(&out_str); + + int input_len = static_cast(strlen(utf_cases[i].input8)); + bool success = true; + for (int ch = 0; ch < input_len; ch++) { + success &= AppendUTF8EscapedChar(utf_cases[i].input8, &ch, input_len, + &output); + } + output.Complete(); + EXPECT_EQ(utf_cases[i].expected_success, success); + EXPECT_EQ(std::string(utf_cases[i].output), out_str); + } + if (utf_cases[i].input16) { + out_str.clear(); + StdStringCanonOutput output(&out_str); + + base::string16 input_str(WStringToUTF16(utf_cases[i].input16)); + int input_len = static_cast(input_str.length()); + bool success = true; + for (int ch = 0; ch < input_len; ch++) { + success &= AppendUTF8EscapedChar(input_str.c_str(), &ch, input_len, + &output); + } + output.Complete(); + EXPECT_EQ(utf_cases[i].expected_success, success); + EXPECT_EQ(std::string(utf_cases[i].output), out_str); + } + + if (utf_cases[i].input8 && utf_cases[i].input16 && + utf_cases[i].expected_success) { + // Check that the UTF-8 and UTF-16 inputs are equivalent. + + // UTF-16 -> UTF-8 + std::string input8_str(utf_cases[i].input8); + base::string16 input16_str(WStringToUTF16(utf_cases[i].input16)); + EXPECT_EQ(input8_str, ConvertUTF16ToUTF8(input16_str)); + + // UTF-8 -> UTF-16 + EXPECT_EQ(input16_str, ConvertUTF8ToUTF16(input8_str)); + } + } +} + +TEST(URLCanonTest, Scheme) { + // Here, we're mostly testing that unusual characters are handled properly. + // The canonicalizer doesn't do any parsing or whitespace detection. It will + // also do its best on error, and will escape funny sequences (these won't be + // valid schemes and it will return error). + // + // Note that the canonicalizer will append a colon to the output to separate + // out the rest of the URL, which is not present in the input. We check, + // however, that the output range includes everything but the colon. + ComponentCase scheme_cases[] = { + {"http", "http:", Component(0, 4), true}, + {"HTTP", "http:", Component(0, 4), true}, + {" HTTP ", "%20http%20:", Component(0, 10), false}, + {"htt: ", "htt%3A%20:", Component(0, 9), false}, + {"\xe4\xbd\xa0\xe5\xa5\xbdhttp", "%E4%BD%A0%E5%A5%BDhttp:", Component(0, 22), false}, + // Don't re-escape something already escaped. Note that it will + // "canonicalize" the 'A' to 'a', but that's OK. + {"ht%3Atp", "ht%3atp:", Component(0, 7), false}, + }; + + std::string out_str; + + for (size_t i = 0; i < arraysize(scheme_cases); i++) { + int url_len = static_cast(strlen(scheme_cases[i].input)); + Component in_comp(0, url_len); + Component out_comp; + + out_str.clear(); + StdStringCanonOutput output1(&out_str); + bool success = CanonicalizeScheme(scheme_cases[i].input, in_comp, &output1, + &out_comp); + output1.Complete(); + + EXPECT_EQ(scheme_cases[i].expected_success, success); + EXPECT_EQ(std::string(scheme_cases[i].expected), out_str); + EXPECT_EQ(scheme_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(scheme_cases[i].expected_component.len, out_comp.len); + + // Now try the wide version. + out_str.clear(); + StdStringCanonOutput output2(&out_str); + + base::string16 wide_input(ConvertUTF8ToUTF16(scheme_cases[i].input)); + in_comp.len = static_cast(wide_input.length()); + success = CanonicalizeScheme(wide_input.c_str(), in_comp, &output2, + &out_comp); + output2.Complete(); + + EXPECT_EQ(scheme_cases[i].expected_success, success); + EXPECT_EQ(std::string(scheme_cases[i].expected), out_str); + EXPECT_EQ(scheme_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(scheme_cases[i].expected_component.len, out_comp.len); + } + + // Test the case where the scheme is declared nonexistent, it should be + // converted into an empty scheme. + Component out_comp; + out_str.clear(); + StdStringCanonOutput output(&out_str); + + EXPECT_TRUE(CanonicalizeScheme("", Component(0, -1), &output, &out_comp)); + output.Complete(); + + EXPECT_EQ(std::string(":"), out_str); + EXPECT_EQ(0, out_comp.begin); + EXPECT_EQ(0, out_comp.len); +} + +TEST(URLCanonTest, Host) { + IPAddressCase host_cases[] = { + // Basic canonicalization, uppercase should be converted to lowercase. + {"GoOgLe.CoM", L"GoOgLe.CoM", "google.com", Component(0, 10), CanonHostInfo::NEUTRAL, -1, ""}, + // Spaces and some other characters should be escaped. + {"Goo%20 goo%7C|.com", L"Goo%20 goo%7C|.com", "goo%20%20goo%7C%7C.com", Component(0, 22), CanonHostInfo::NEUTRAL, -1, ""}, + // Exciting different types of spaces! + {NULL, L"GOO\x00a0\x3000goo.com", "goo%20%20goo.com", Component(0, 16), CanonHostInfo::NEUTRAL, -1, ""}, + // Other types of space (no-break, zero-width, zero-width-no-break) are + // name-prepped away to nothing. + {NULL, L"GOO\x200b\x2060\xfeffgoo.com", "googoo.com", Component(0, 10), CanonHostInfo::NEUTRAL, -1, ""}, + // Ideographic full stop (full-width period for Chinese, etc.) should be + // treated as a dot. + {NULL, L"www.foo\x3002" L"bar.com", "www.foo.bar.com", Component(0, 15), CanonHostInfo::NEUTRAL, -1, ""}, + // Invalid unicode characters should fail... + // ...In wide input, ICU will barf and we'll end up with the input as + // escaped UTF-8 (the invalid character should be replaced with the + // replacement character). + {"\xef\xb7\x90zyx.com", L"\xfdd0zyx.com", "%EF%BF%BDzyx.com", Component(0, 16), CanonHostInfo::BROKEN, -1, ""}, + // ...This is the same as previous but with with escaped. + {"%ef%b7%90zyx.com", L"%ef%b7%90zyx.com", "%EF%BF%BDzyx.com", Component(0, 16), CanonHostInfo::BROKEN, -1, ""}, + // Test name prepping, fullwidth input should be converted to ASCII and NOT + // IDN-ized. This is "Go" in fullwidth UTF-8/UTF-16. + {"\xef\xbc\xa7\xef\xbd\x8f.com", L"\xff27\xff4f.com", "go.com", Component(0, 6), CanonHostInfo::NEUTRAL, -1, ""}, + // Test that fullwidth escaped values are properly name-prepped, + // then converted or rejected. + // ...%41 in fullwidth = 'A' (also as escaped UTF-8 input) + {"\xef\xbc\x85\xef\xbc\x94\xef\xbc\x91.com", L"\xff05\xff14\xff11.com", "a.com", Component(0, 5), CanonHostInfo::NEUTRAL, -1, ""}, + {"%ef%bc%85%ef%bc%94%ef%bc%91.com", L"%ef%bc%85%ef%bc%94%ef%bc%91.com", "a.com", Component(0, 5), CanonHostInfo::NEUTRAL, -1, ""}, + // ...%00 in fullwidth should fail (also as escaped UTF-8 input) + {"\xef\xbc\x85\xef\xbc\x90\xef\xbc\x90.com", L"\xff05\xff10\xff10.com", "%00.com", Component(0, 7), CanonHostInfo::BROKEN, -1, ""}, + {"%ef%bc%85%ef%bc%90%ef%bc%90.com", L"%ef%bc%85%ef%bc%90%ef%bc%90.com", "%00.com", Component(0, 7), CanonHostInfo::BROKEN, -1, ""}, + // ICU will convert weird percents into ASCII percents, but not unescape + // further. A weird percent is U+FE6A (EF B9 AA in UTF-8) which is a + // "small percent". At this point we should be within our rights to mark + // anything as invalid since the URL is corrupt or malicious. The code + // happens to allow ASCII characters (%41 = "A" -> 'a') to be unescaped + // and kept as valid, so we validate that behavior here, but this level + // of fixing the input shouldn't be seen as required. "%81" is invalid. + {"\xef\xb9\xaa" "41.com", L"\xfe6a" L"41.com", "a.com", Component(0, 5), CanonHostInfo::NEUTRAL, -1, ""}, + {"%ef%b9%aa" "41.com", L"\xfe6a" L"41.com", "a.com", Component(0, 5), CanonHostInfo::NEUTRAL, -1, ""}, + {"\xef\xb9\xaa" "81.com", L"\xfe6a" L"81.com", "%81.com", Component(0, 7), CanonHostInfo::BROKEN, -1, ""}, + {"%ef%b9%aa" "81.com", L"\xfe6a" L"81.com", "%81.com", Component(0, 7), CanonHostInfo::BROKEN, -1, ""}, + // Basic IDN support, UTF-8 and UTF-16 input should be converted to IDN + {"\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d\x4f60\x597d", "xn--6qqa088eba", Component(0, 14), CanonHostInfo::NEUTRAL, -1, ""}, + // See http://unicode.org/cldr/utility/idna.jsp for other + // examples/experiments and http://goo.gl/7yG11o + // for the full list of characters handled differently by + // IDNA 2003, UTS 46 (http://unicode.org/reports/tr46/ ) and IDNA 2008. + + // 4 Deviation characters are mapped/ignored in UTS 46 transitional + // mechansm. UTS 46, table 4 row (g). + // Sharp-s is mapped to 'ss' in UTS 46 and IDNA 2003. + // Otherwise, it'd be "xn--fuball-cta.de". + {"fu\xc3\x9f" "ball.de", L"fu\x00df" L"ball.de", "fussball.de", + Component(0, 11), CanonHostInfo::NEUTRAL, -1, ""}, + // Final-sigma (U+03C3) is mapped to regular sigma (U+03C2). + // Otherwise, it'd be "xn--wxaijb9b". + {"\xcf\x83\xcf\x8c\xce\xbb\xce\xbf\xcf\x82", L"\x3c3\x3cc\x3bb\x3bf\x3c2", + "xn--wxaikc6b", Component(0, 12), + CanonHostInfo::NEUTRAL, -1, ""}, + // ZWNJ (U+200C) and ZWJ (U+200D) are mapped away in UTS 46 transitional + // handling as well as in IDNA 2003. + {"a\xe2\x80\x8c" "b\xe2\x80\x8d" "c", L"a\x200c" L"b\x200d" L"c", "abc", + Component(0, 3), CanonHostInfo::NEUTRAL, -1, ""}, + // ZWJ between Devanagari characters is still mapped away in UTS 46 + // transitional handling. IDNA 2008 would give xn--11bo0mv54g. + {"\xe0\xa4\x95\xe0\xa5\x8d\xe2\x80\x8d\xe0\xa4\x9c", + L"\x915\x94d\x200d\x91c", "xn--11bo0m", + Component(0, 10), CanonHostInfo::NEUTRAL, -1, ""}, + // Fullwidth exclamation mark is disallowed. UTS 46, table 4, row (b) + // However, we do allow this at the moment because we don't use + // STD3 rules and canonicalize full-width ASCII to ASCII. + {"wow\xef\xbc\x81", L"wow\xff01", "wow%21", + Component(0, 6), CanonHostInfo::NEUTRAL, -1, ""}, + // U+2132 (turned capital F) is disallowed. UTS 46, table 4, row (c) + // Allowed in IDNA 2003, but the mapping changed after Unicode 3.2 + {"\xe2\x84\xb2oo", L"\x2132oo", "%E2%84%B2oo", + Component(0, 11), CanonHostInfo::BROKEN, -1, ""}, + // U+2F868 (CJK Comp) is disallowed. UTS 46, table 4, row (d) + // Allowed in IDNA 2003, but the mapping changed after Unicode 3.2 + {"\xf0\xaf\xa1\xa8\xe5\xa7\xbb.cn", L"\xd87e\xdc68\x59fb.cn", + "%F0%AF%A1%A8%E5%A7%BB.cn", + Component(0, 24), CanonHostInfo::BROKEN, -1, ""}, + // Maps uppercase letters to lower case letters. UTS 46 table 4 row (e) + {"M\xc3\x9cNCHEN", L"M\xdcNCHEN", "xn--mnchen-3ya", + Component(0, 14), CanonHostInfo::NEUTRAL, -1, ""}, + // Symbol/punctuations are allowed in IDNA 2003/UTS46. + // Not allowed in IDNA 2008. UTS 46 table 4 row (f). + {"\xe2\x99\xa5ny.us", L"\x2665ny.us", "xn--ny-s0x.us", + Component(0, 13), CanonHostInfo::NEUTRAL, -1, ""}, + // U+11013 is new in Unicode 6.0 and is allowed. UTS 46 table 4, row (h) + // We used to allow it because we passed through unassigned code points. + {"\xf0\x91\x80\x93.com", L"\xd804\xdc13.com", "xn--n00d.com", + Component(0, 12), CanonHostInfo::NEUTRAL, -1, ""}, + // U+0602 is disallowed in UTS46/IDNA 2008. UTS 46 table 4, row(i) + // Used to be allowed in INDA 2003. + {"\xd8\x82.eg", L"\x602.eg", "%D8%82.eg", + Component(0, 9), CanonHostInfo::BROKEN, -1, ""}, + // U+20B7 is new in Unicode 5.2 (not a part of IDNA 2003 based + // on Unicode 3.2). We did allow it in the past because we let unassigned + // code point pass. We continue to allow it even though it's a + // "punctuation and symbol" blocked in IDNA 2008. + // UTS 46 table 4, row (j) + {"\xe2\x82\xb7.com", L"\x20b7.com", "xn--wzg.com", + Component(0, 11), CanonHostInfo::NEUTRAL, -1, ""}, + // Maps uppercase letters to lower case letters. + // In IDNA 2003, it's allowed without case-folding + // ( xn--bc-7cb.com ) because it's not defined in Unicode 3.2 + // (added in Unicode 4.1). UTS 46 table 4 row (k) + {"bc\xc8\xba.com", L"bc\x23a.com", "xn--bc-is1a.com", + Component(0, 15), CanonHostInfo::NEUTRAL, -1, ""}, + // BiDi check test + // "Divehi" in Divehi (Thaana script) ends with BidiClass=NSM. + // Disallowed in IDNA 2003 but now allowed in UTS 46/IDNA 2008. + {"\xde\x8b\xde\xa8\xde\x88\xde\xac\xde\x80\xde\xa8", + L"\x78b\x7a8\x788\x7ac\x780\x7a8", "xn--hqbpi0jcw", + Component(0, 13), CanonHostInfo::NEUTRAL, -1, ""}, + // Disallowed in both IDNA 2003 and 2008 with BiDi check. + // Labels starting with a RTL character cannot end with a LTR character. + {"\xd8\xac\xd8\xa7\xd8\xb1xyz", L"\x62c\x627\x631xyz", + "%D8%AC%D8%A7%D8%B1xyz", Component(0, 21), + CanonHostInfo::BROKEN, -1, ""}, + // Labels starting with a RTL character can end with BC=EN (European + // number). Disallowed in IDNA 2003 but now allowed. + {"\xd8\xac\xd8\xa7\xd8\xb1" "2", L"\x62c\x627\x631" L"2", + "xn--2-ymcov", Component(0, 11), + CanonHostInfo::NEUTRAL, -1, ""}, + // Labels starting with a RTL character cannot have "L" characters + // even if it ends with an BC=EN. Disallowed in both IDNA 2003/2008. + {"\xd8\xac\xd8\xa7\xd8\xb1xy2", L"\x62c\x627\x631xy2", + "%D8%AC%D8%A7%D8%B1xy2", Component(0, 21), + CanonHostInfo::BROKEN, -1, ""}, + // Labels starting with a RTL character can end with BC=AN (Arabic number) + // Disallowed in IDNA 2003, but now allowed. + {"\xd8\xac\xd8\xa7\xd8\xb1\xd9\xa2", L"\x62c\x627\x631\x662", + "xn--mgbjq0r", Component(0, 11), + CanonHostInfo::NEUTRAL, -1, ""}, + // Labels starting with a RTL character cannot have "L" characters + // even if it ends with an BC=AN (Arabic number). + // Disallowed in both IDNA 2003/2008. + {"\xd8\xac\xd8\xa7\xd8\xb1xy\xd9\xa2", L"\x62c\x627\x631xy\x662", + "%D8%AC%D8%A7%D8%B1xy%D9%A2", Component(0, 26), + CanonHostInfo::BROKEN, -1, ""}, + // Labels starting with a RTL character cannot mix BC=EN and BC=AN + {"\xd8\xac\xd8\xa7\xd8\xb1xy2\xd9\xa2", L"\x62c\x627\x631xy2\x662", + "%D8%AC%D8%A7%D8%B1xy2%D9%A2", Component(0, 27), + CanonHostInfo::BROKEN, -1, ""}, + // As of Unicode 6.2, U+20CF is not assigned. We do not allow it. + {"\xe2\x83\x8f.com", L"\x20cf.com", "%E2%83%8F.com", + Component(0, 13), CanonHostInfo::BROKEN, -1, ""}, + // U+0080 is not allowed. + {"\xc2\x80.com", L"\x80.com", "%C2%80.com", + Component(0, 10), CanonHostInfo::BROKEN, -1, ""}, + // Mixed UTF-8 and escaped UTF-8 (narrow case) and UTF-16 and escaped + // Mixed UTF-8 and escaped UTF-8 (narrow case) and UTF-16 and escaped + // UTF-8 (wide case). The output should be equivalent to the true wide + // character input above). + {"%E4%BD%A0%E5%A5%BD\xe4\xbd\xa0\xe5\xa5\xbd", + L"%E4%BD%A0%E5%A5%BD\x4f60\x597d", "xn--6qqa088eba", + Component(0, 14), CanonHostInfo::NEUTRAL, -1, ""}, + // Invalid escaped characters should fail and the percents should be + // escaped. + {"%zz%66%a", L"%zz%66%a", "%25zzf%25a", Component(0, 10), + CanonHostInfo::BROKEN, -1, ""}, + // If we get an invalid character that has been escaped. + {"%25", L"%25", "%25", Component(0, 3), + CanonHostInfo::BROKEN, -1, ""}, + {"hello%00", L"hello%00", "hello%00", Component(0, 8), + CanonHostInfo::BROKEN, -1, ""}, + // Escaped numbers should be treated like IP addresses if they are. + {"%30%78%63%30%2e%30%32%35%30.01", L"%30%78%63%30%2e%30%32%35%30.01", + "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 3, + "C0A80001"}, + {"%30%78%63%30%2e%30%32%35%30.01%2e", L"%30%78%63%30%2e%30%32%35%30.01%2e", + "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 3, + "C0A80001"}, + // Invalid escaping should trigger the regular host error handling. + {"%3g%78%63%30%2e%30%32%35%30%2E.01", L"%3g%78%63%30%2e%30%32%35%30%2E.01", "%253gxc0.0250..01", Component(0, 17), CanonHostInfo::BROKEN, -1, ""}, + // Something that isn't exactly an IP should get treated as a host and + // spaces escaped. + {"192.168.0.1 hello", L"192.168.0.1 hello", "192.168.0.1%20hello", Component(0, 19), CanonHostInfo::NEUTRAL, -1, ""}, + // Fullwidth and escaped UTF-8 fullwidth should still be treated as IP. + // These are "0Xc0.0250.01" in fullwidth. + {"\xef\xbc\x90%Ef%bc\xb8%ef%Bd%83\xef\xbc\x90%EF%BC%8E\xef\xbc\x90\xef\xbc\x92\xef\xbc\x95\xef\xbc\x90\xef\xbc%8E\xef\xbc\x90\xef\xbc\x91", L"\xff10\xff38\xff43\xff10\xff0e\xff10\xff12\xff15\xff10\xff0e\xff10\xff11", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 3, "C0A80001"}, + // Broken IP addresses get marked as such. + {"192.168.0.257", L"192.168.0.257", "192.168.0.257", Component(0, 13), CanonHostInfo::BROKEN, -1, ""}, + {"[google.com]", L"[google.com]", "[google.com]", Component(0, 12), CanonHostInfo::BROKEN, -1, ""}, + // Cyrillic letter followed by '(' should return punycode for '(' escaped + // before punycode string was created. I.e. + // if '(' is escaped after punycode is created we would get xn--%28-8tb + // (incorrect). + {"\xd1\x82(", L"\x0442(", "xn--%28-7ed", Component(0, 11), + CanonHostInfo::NEUTRAL, -1, ""}, + // Address with all hexidecimal characters with leading number of 1<<32 + // or greater and should return NEUTRAL rather than BROKEN if not all + // components are numbers. + {"12345678912345.de", L"12345678912345.de", "12345678912345.de", Component(0, 17), CanonHostInfo::NEUTRAL, -1, ""}, + {"1.12345678912345.de", L"1.12345678912345.de", "1.12345678912345.de", Component(0, 19), CanonHostInfo::NEUTRAL, -1, ""}, + {"12345678912345.12345678912345.de", L"12345678912345.12345678912345.de", "12345678912345.12345678912345.de", Component(0, 32), CanonHostInfo::NEUTRAL, -1, ""}, + {"1.2.0xB3A73CE5B59.de", L"1.2.0xB3A73CE5B59.de", "1.2.0xb3a73ce5b59.de", Component(0, 20), CanonHostInfo::NEUTRAL, -1, ""}, + {"12345678912345.0xde", L"12345678912345.0xde", "12345678912345.0xde", Component(0, 19), CanonHostInfo::BROKEN, -1, ""}, + }; + + // CanonicalizeHost() non-verbose. + std::string out_str; + for (size_t i = 0; i < arraysize(host_cases); i++) { + // Narrow version. + if (host_cases[i].input8) { + int host_len = static_cast(strlen(host_cases[i].input8)); + Component in_comp(0, host_len); + Component out_comp; + + out_str.clear(); + StdStringCanonOutput output(&out_str); + + bool success = CanonicalizeHost(host_cases[i].input8, in_comp, &output, + &out_comp); + output.Complete(); + + EXPECT_EQ(host_cases[i].expected_family != CanonHostInfo::BROKEN, + success) << "for input: " << host_cases[i].input8; + EXPECT_EQ(std::string(host_cases[i].expected), out_str) << + "for input: " << host_cases[i].input8; + EXPECT_EQ(host_cases[i].expected_component.begin, out_comp.begin) << + "for input: " << host_cases[i].input8; + EXPECT_EQ(host_cases[i].expected_component.len, out_comp.len) << + "for input: " << host_cases[i].input8; + } + + // Wide version. + if (host_cases[i].input16) { + base::string16 input16(WStringToUTF16(host_cases[i].input16)); + int host_len = static_cast(input16.length()); + Component in_comp(0, host_len); + Component out_comp; + + out_str.clear(); + StdStringCanonOutput output(&out_str); + + bool success = CanonicalizeHost(input16.c_str(), in_comp, &output, + &out_comp); + output.Complete(); + + EXPECT_EQ(host_cases[i].expected_family != CanonHostInfo::BROKEN, + success); + EXPECT_EQ(std::string(host_cases[i].expected), out_str); + EXPECT_EQ(host_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(host_cases[i].expected_component.len, out_comp.len); + } + } + + // CanonicalizeHostVerbose() + for (size_t i = 0; i < arraysize(host_cases); i++) { + // Narrow version. + if (host_cases[i].input8) { + int host_len = static_cast(strlen(host_cases[i].input8)); + Component in_comp(0, host_len); + + out_str.clear(); + StdStringCanonOutput output(&out_str); + CanonHostInfo host_info; + + CanonicalizeHostVerbose(host_cases[i].input8, in_comp, &output, + &host_info); + output.Complete(); + + EXPECT_EQ(host_cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(host_cases[i].expected), out_str); + EXPECT_EQ(host_cases[i].expected_component.begin, + host_info.out_host.begin); + EXPECT_EQ(host_cases[i].expected_component.len, host_info.out_host.len); + EXPECT_EQ(std::string(host_cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())); + if (host_cases[i].expected_family == CanonHostInfo::IPV4) { + EXPECT_EQ(host_cases[i].expected_num_ipv4_components, + host_info.num_ipv4_components); + } + } + + // Wide version. + if (host_cases[i].input16) { + base::string16 input16(WStringToUTF16(host_cases[i].input16)); + int host_len = static_cast(input16.length()); + Component in_comp(0, host_len); + + out_str.clear(); + StdStringCanonOutput output(&out_str); + CanonHostInfo host_info; + + CanonicalizeHostVerbose(input16.c_str(), in_comp, &output, &host_info); + output.Complete(); + + EXPECT_EQ(host_cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(host_cases[i].expected), out_str); + EXPECT_EQ(host_cases[i].expected_component.begin, + host_info.out_host.begin); + EXPECT_EQ(host_cases[i].expected_component.len, host_info.out_host.len); + EXPECT_EQ(std::string(host_cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())); + if (host_cases[i].expected_family == CanonHostInfo::IPV4) { + EXPECT_EQ(host_cases[i].expected_num_ipv4_components, + host_info.num_ipv4_components); + } + } + } +} + +TEST(URLCanonTest, IPv4) { + IPAddressCase cases[] = { + // Empty is not an IP address. + {"", L"", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + {".", L".", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Regular IP addresses in different bases. + {"192.168.0.1", L"192.168.0.1", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 4, "C0A80001"}, + {"0300.0250.00.01", L"0300.0250.00.01", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 4, "C0A80001"}, + {"0xC0.0Xa8.0x0.0x1", L"0xC0.0Xa8.0x0.0x1", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 4, "C0A80001"}, + // Non-IP addresses due to invalid characters. + {"192.168.9.com", L"192.168.9.com", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Invalid characters for the base should be rejected. + {"19a.168.0.1", L"19a.168.0.1", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + {"0308.0250.00.01", L"0308.0250.00.01", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + {"0xCG.0xA8.0x0.0x1", L"0xCG.0xA8.0x0.0x1", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // If there are not enough components, the last one should fill them out. + {"192", L"192", "0.0.0.192", Component(0, 9), CanonHostInfo::IPV4, 1, "000000C0"}, + {"0xC0a80001", L"0xC0a80001", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 1, "C0A80001"}, + {"030052000001", L"030052000001", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 1, "C0A80001"}, + {"000030052000001", L"000030052000001", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 1, "C0A80001"}, + {"192.168", L"192.168", "192.0.0.168", Component(0, 11), CanonHostInfo::IPV4, 2, "C00000A8"}, + {"192.0x00A80001", L"192.0x000A80001", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 2, "C0A80001"}, + {"0xc0.052000001", L"0xc0.052000001", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 2, "C0A80001"}, + {"192.168.1", L"192.168.1", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 3, "C0A80001"}, + // Too many components means not an IP address. + {"192.168.0.0.1", L"192.168.0.0.1", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // We allow a single trailing dot. + {"192.168.0.1.", L"192.168.0.1.", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 4, "C0A80001"}, + {"192.168.0.1. hello", L"192.168.0.1. hello", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + {"192.168.0.1..", L"192.168.0.1..", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Two dots in a row means not an IP address. + {"192.168..1", L"192.168..1", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Any numerical overflow should be marked as BROKEN. + {"0x100.0", L"0x100.0", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0x100.0.0", L"0x100.0.0", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0x100.0.0.0", L"0x100.0.0.0", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0.0x100.0.0", L"0.0x100.0.0", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0.0.0x100.0", L"0.0.0x100.0", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0.0.0.0x100", L"0.0.0.0x100", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0.0.0x10000", L"0.0.0x10000", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0.0x1000000", L"0.0x1000000", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0x100000000", L"0x100000000", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Repeat the previous tests, minus 1, to verify boundaries. + {"0xFF.0", L"0xFF.0", "255.0.0.0", Component(0, 9), CanonHostInfo::IPV4, 2, "FF000000"}, + {"0xFF.0.0", L"0xFF.0.0", "255.0.0.0", Component(0, 9), CanonHostInfo::IPV4, 3, "FF000000"}, + {"0xFF.0.0.0", L"0xFF.0.0.0", "255.0.0.0", Component(0, 9), CanonHostInfo::IPV4, 4, "FF000000"}, + {"0.0xFF.0.0", L"0.0xFF.0.0", "0.255.0.0", Component(0, 9), CanonHostInfo::IPV4, 4, "00FF0000"}, + {"0.0.0xFF.0", L"0.0.0xFF.0", "0.0.255.0", Component(0, 9), CanonHostInfo::IPV4, 4, "0000FF00"}, + {"0.0.0.0xFF", L"0.0.0.0xFF", "0.0.0.255", Component(0, 9), CanonHostInfo::IPV4, 4, "000000FF"}, + {"0.0.0xFFFF", L"0.0.0xFFFF", "0.0.255.255", Component(0, 11), CanonHostInfo::IPV4, 3, "0000FFFF"}, + {"0.0xFFFFFF", L"0.0xFFFFFF", "0.255.255.255", Component(0, 13), CanonHostInfo::IPV4, 2, "00FFFFFF"}, + {"0xFFFFFFFF", L"0xFFFFFFFF", "255.255.255.255", Component(0, 15), CanonHostInfo::IPV4, 1, "FFFFFFFF"}, + // Old trunctations tests. They're all "BROKEN" now. + {"276.256.0xf1a2.077777", L"276.256.0xf1a2.077777", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"192.168.0.257", L"192.168.0.257", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"192.168.0xa20001", L"192.168.0xa20001", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"192.015052000001", L"192.015052000001", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0X12C0a80001", L"0X12C0a80001", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"276.1.2", L"276.1.2", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Spaces should be rejected. + {"192.168.0.1 hello", L"192.168.0.1 hello", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Very large numbers. + {"0000000000000300.0x00000000000000fF.00000000000000001", L"0000000000000300.0x00000000000000fF.00000000000000001", "192.255.0.1", Component(0, 11), CanonHostInfo::IPV4, 3, "C0FF0001"}, + {"0000000000000300.0xffffffffFFFFFFFF.3022415481470977", L"0000000000000300.0xffffffffFFFFFFFF.3022415481470977", "", Component(0, 11), CanonHostInfo::BROKEN, -1, ""}, + // A number has no length limit, but long numbers can still overflow. + {"00000000000000000001", L"00000000000000000001", "0.0.0.1", Component(0, 7), CanonHostInfo::IPV4, 1, "00000001"}, + {"0000000000000000100000000000000001", L"0000000000000000100000000000000001", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // If a long component is non-numeric, it's a hostname, *not* a broken IP. + {"0.0.0.000000000000000000z", L"0.0.0.000000000000000000z", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + {"0.0.0.100000000000000000z", L"0.0.0.100000000000000000z", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Truncation of all zeros should still result in 0. + {"0.00.0x.0x0", L"0.00.0x.0x0", "0.0.0.0", Component(0, 7), CanonHostInfo::IPV4, 4, "00000000"}, + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + // 8-bit version. + Component component(0, static_cast(strlen(cases[i].input8))); + + std::string out_str1; + StdStringCanonOutput output1(&out_str1); + CanonHostInfo host_info; + CanonicalizeIPAddress(cases[i].input8, component, &output1, &host_info); + output1.Complete(); + + EXPECT_EQ(cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())); + if (host_info.family == CanonHostInfo::IPV4) { + EXPECT_STREQ(cases[i].expected, out_str1.c_str()); + EXPECT_EQ(cases[i].expected_component.begin, host_info.out_host.begin); + EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); + EXPECT_EQ(cases[i].expected_num_ipv4_components, + host_info.num_ipv4_components); + } + + // 16-bit version. + base::string16 input16(WStringToUTF16(cases[i].input16)); + component = Component(0, static_cast(input16.length())); + + std::string out_str2; + StdStringCanonOutput output2(&out_str2); + CanonicalizeIPAddress(input16.c_str(), component, &output2, &host_info); + output2.Complete(); + + EXPECT_EQ(cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())); + if (host_info.family == CanonHostInfo::IPV4) { + EXPECT_STREQ(cases[i].expected, out_str2.c_str()); + EXPECT_EQ(cases[i].expected_component.begin, host_info.out_host.begin); + EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); + EXPECT_EQ(cases[i].expected_num_ipv4_components, + host_info.num_ipv4_components); + } + } +} + +TEST(URLCanonTest, IPv6) { + IPAddressCase cases[] = { + // Empty is not an IP address. + {"", L"", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Non-IPs with [:] characters are marked BROKEN. + {":", L":", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[", L"[", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[:", L"[:", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"]", L"]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {":]", L":]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[]", L"[]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[:]", L"[:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Regular IP address is invalid without bounding '[' and ']'. + {"2001:db8::1", L"2001:db8::1", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[2001:db8::1", L"[2001:db8::1", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"2001:db8::1]", L"2001:db8::1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Regular IP addresses. + {"[::]", L"[::]", "[::]", Component(0,4), CanonHostInfo::IPV6, -1, "00000000000000000000000000000000"}, + {"[::1]", L"[::1]", "[::1]", Component(0,5), CanonHostInfo::IPV6, -1, "00000000000000000000000000000001"}, + {"[1::]", L"[1::]", "[1::]", Component(0,5), CanonHostInfo::IPV6, -1, "00010000000000000000000000000000"}, + + // Leading zeros should be stripped. + {"[000:01:02:003:004:5:6:007]", L"[000:01:02:003:004:5:6:007]", "[0:1:2:3:4:5:6:7]", Component(0,17), CanonHostInfo::IPV6, -1, "00000001000200030004000500060007"}, + + // Upper case letters should be lowercased. + {"[A:b:c:DE:fF:0:1:aC]", L"[A:b:c:DE:fF:0:1:aC]", "[a:b:c:de:ff:0:1:ac]", Component(0,20), CanonHostInfo::IPV6, -1, "000A000B000C00DE00FF0000000100AC"}, + + // The same address can be written with different contractions, but should + // get canonicalized to the same thing. + {"[1:0:0:2::3:0]", L"[1:0:0:2::3:0]", "[1::2:0:0:3:0]", Component(0,14), CanonHostInfo::IPV6, -1, "00010000000000020000000000030000"}, + {"[1::2:0:0:3:0]", L"[1::2:0:0:3:0]", "[1::2:0:0:3:0]", Component(0,14), CanonHostInfo::IPV6, -1, "00010000000000020000000000030000"}, + + // Addresses with embedded IPv4. + {"[::192.168.0.1]", L"[::192.168.0.1]", "[::c0a8:1]", Component(0,10), CanonHostInfo::IPV6, -1, "000000000000000000000000C0A80001"}, + {"[::ffff:192.168.0.1]", L"[::ffff:192.168.0.1]", "[::ffff:c0a8:1]", Component(0,15), CanonHostInfo::IPV6, -1, "00000000000000000000FFFFC0A80001"}, + {"[::eeee:192.168.0.1]", L"[::eeee:192.168.0.1]", "[::eeee:c0a8:1]", Component(0, 15), CanonHostInfo::IPV6, -1, "00000000000000000000EEEEC0A80001"}, + {"[2001::192.168.0.1]", L"[2001::192.168.0.1]", "[2001::c0a8:1]", Component(0, 14), CanonHostInfo::IPV6, -1, "200100000000000000000000C0A80001"}, + {"[1:2:192.168.0.1:5:6]", L"[1:2:192.168.0.1:5:6]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // IPv4 with last component missing. + {"[::ffff:192.1.2]", L"[::ffff:192.1.2]", "[::ffff:c001:2]", Component(0,15), CanonHostInfo::IPV6, -1, "00000000000000000000FFFFC0010002"}, + + // IPv4 using hex. + // TODO(eroman): Should this format be disallowed? + {"[::ffff:0xC0.0Xa8.0x0.0x1]", L"[::ffff:0xC0.0Xa8.0x0.0x1]", "[::ffff:c0a8:1]", Component(0,15), CanonHostInfo::IPV6, -1, "00000000000000000000FFFFC0A80001"}, + + // There may be zeros surrounding the "::" contraction. + {"[0:0::0:0:8]", L"[0:0::0:0:8]", "[::8]", Component(0,5), CanonHostInfo::IPV6, -1, "00000000000000000000000000000008"}, + + {"[2001:db8::1]", L"[2001:db8::1]", "[2001:db8::1]", Component(0,13), CanonHostInfo::IPV6, -1, "20010DB8000000000000000000000001"}, + + // Can only have one "::" contraction in an IPv6 string literal. + {"[2001::db8::1]", L"[2001::db8::1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // No more than 2 consecutive ':'s. + {"[2001:db8:::1]", L"[2001:db8:::1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[:::]", L"[:::]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Non-IP addresses due to invalid characters. + {"[2001::.com]", L"[2001::.com]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // If there are not enough components, the last one should fill them out. + // ... omitted at this time ... + // Too many components means not an IP address. Similarly, with too few + // if using IPv4 compat or mapped addresses. + {"[::192.168.0.0.1]", L"[::192.168.0.0.1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[::ffff:192.168.0.0.1]", L"[::ffff:192.168.0.0.1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[1:2:3:4:5:6:7:8:9]", L"[1:2:3:4:5:6:7:8:9]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Too many bits (even though 8 comonents, the last one holds 32 bits). + {"[0:0:0:0:0:0:0:192.168.0.1]", L"[0:0:0:0:0:0:0:192.168.0.1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // Too many bits specified -- the contraction would have to be zero-length + // to not exceed 128 bits. + {"[1:2:3:4:5:6::192.168.0.1]", L"[1:2:3:4:5:6::192.168.0.1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // The contraction is for 16 bits of zero. + {"[1:2:3:4:5:6::8]", L"[1:2:3:4:5:6::8]", "[1:2:3:4:5:6:0:8]", Component(0,17), CanonHostInfo::IPV6, -1, "00010002000300040005000600000008"}, + + // Cannot have a trailing colon. + {"[1:2:3:4:5:6:7:8:]", L"[1:2:3:4:5:6:7:8:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[1:2:3:4:5:6:192.168.0.1:]", L"[1:2:3:4:5:6:192.168.0.1:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // Cannot have negative numbers. + {"[-1:2:3:4:5:6:7:8]", L"[-1:2:3:4:5:6:7:8]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // Scope ID -- the URL may contain an optional ["%" ] section. + // The scope_id should be included in the canonicalized URL, and is an + // unsigned decimal number. + + // Invalid because no ID was given after the percent. + + // Don't allow scope-id + {"[1::%1]", L"[1::%1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[1::%eth0]", L"[1::%eth0]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[1::%]", L"[1::%]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[%]", L"[%]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[::%:]", L"[::%:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // Don't allow leading or trailing colons. + {"[:0:0::0:0:8]", L"[:0:0::0:0:8]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[0:0::0:0:8:]", L"[0:0::0:0:8:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[:0:0::0:0:8:]", L"[:0:0::0:0:8:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // We allow a single trailing dot. + // ... omitted at this time ... + // Two dots in a row means not an IP address. + {"[::192.168..1]", L"[::192.168..1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Any non-first components get truncated to one byte. + // ... omitted at this time ... + // Spaces should be rejected. + {"[::1 hello]", L"[::1 hello]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + // 8-bit version. + Component component(0, static_cast(strlen(cases[i].input8))); + + std::string out_str1; + StdStringCanonOutput output1(&out_str1); + CanonHostInfo host_info; + CanonicalizeIPAddress(cases[i].input8, component, &output1, &host_info); + output1.Complete(); + + EXPECT_EQ(cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())) << "iter " << i << " host " << cases[i].input8; + if (host_info.family == CanonHostInfo::IPV6) { + EXPECT_STREQ(cases[i].expected, out_str1.c_str()); + EXPECT_EQ(cases[i].expected_component.begin, + host_info.out_host.begin); + EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); + } + + // 16-bit version. + base::string16 input16(WStringToUTF16(cases[i].input16)); + component = Component(0, static_cast(input16.length())); + + std::string out_str2; + StdStringCanonOutput output2(&out_str2); + CanonicalizeIPAddress(input16.c_str(), component, &output2, &host_info); + output2.Complete(); + + EXPECT_EQ(cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())); + if (host_info.family == CanonHostInfo::IPV6) { + EXPECT_STREQ(cases[i].expected, out_str2.c_str()); + EXPECT_EQ(cases[i].expected_component.begin, host_info.out_host.begin); + EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); + } + } +} + +TEST(URLCanonTest, IPEmpty) { + std::string out_str1; + StdStringCanonOutput output1(&out_str1); + CanonHostInfo host_info; + + // This tests tests. + const char spec[] = "192.168.0.1"; + CanonicalizeIPAddress(spec, Component(), &output1, &host_info); + EXPECT_FALSE(host_info.IsIPAddress()); + + CanonicalizeIPAddress(spec, Component(0, 0), &output1, &host_info); + EXPECT_FALSE(host_info.IsIPAddress()); +} + +TEST(URLCanonTest, UserInfo) { + // Note that the canonicalizer should escape and treat empty components as + // not being there. + + // We actually parse a full input URL so we can get the initial components. + struct UserComponentCase { + const char* input; + const char* expected; + Component expected_username; + Component expected_password; + bool expected_success; + } user_info_cases[] = { + {"http://user:pass@host.com/", "user:pass@", Component(0, 4), Component(5, 4), true}, + {"http://@host.com/", "", Component(0, -1), Component(0, -1), true}, + {"http://:@host.com/", "", Component(0, -1), Component(0, -1), true}, + {"http://foo:@host.com/", "foo@", Component(0, 3), Component(0, -1), true}, + {"http://:foo@host.com/", ":foo@", Component(0, 0), Component(1, 3), true}, + {"http://^ :$\t@host.com/", "%5E%20:$%09@", Component(0, 6), Component(7, 4), true}, + {"http://user:pass@/", "user:pass@", Component(0, 4), Component(5, 4), true}, + {"http://%2540:bar@domain.com/", "%2540:bar@", Component(0, 5), Component(6, 3), true }, + + // IE7 compatibility: old versions allowed backslashes in usernames, but + // IE7 does not. We disallow it as well. + {"ftp://me\\mydomain:pass@foo.com/", "", Component(0, -1), Component(0, -1), true}, + }; + + for (size_t i = 0; i < arraysize(user_info_cases); i++) { + int url_len = static_cast(strlen(user_info_cases[i].input)); + Parsed parsed; + ParseStandardURL(user_info_cases[i].input, url_len, &parsed); + Component out_user, out_pass; + std::string out_str; + StdStringCanonOutput output1(&out_str); + + bool success = CanonicalizeUserInfo(user_info_cases[i].input, + parsed.username, + user_info_cases[i].input, + parsed.password, + &output1, + &out_user, + &out_pass); + output1.Complete(); + + EXPECT_EQ(user_info_cases[i].expected_success, success); + EXPECT_EQ(std::string(user_info_cases[i].expected), out_str); + EXPECT_EQ(user_info_cases[i].expected_username.begin, out_user.begin); + EXPECT_EQ(user_info_cases[i].expected_username.len, out_user.len); + EXPECT_EQ(user_info_cases[i].expected_password.begin, out_pass.begin); + EXPECT_EQ(user_info_cases[i].expected_password.len, out_pass.len); + + // Now try the wide version + out_str.clear(); + StdStringCanonOutput output2(&out_str); + base::string16 wide_input(ConvertUTF8ToUTF16(user_info_cases[i].input)); + success = CanonicalizeUserInfo(wide_input.c_str(), + parsed.username, + wide_input.c_str(), + parsed.password, + &output2, + &out_user, + &out_pass); + output2.Complete(); + + EXPECT_EQ(user_info_cases[i].expected_success, success); + EXPECT_EQ(std::string(user_info_cases[i].expected), out_str); + EXPECT_EQ(user_info_cases[i].expected_username.begin, out_user.begin); + EXPECT_EQ(user_info_cases[i].expected_username.len, out_user.len); + EXPECT_EQ(user_info_cases[i].expected_password.begin, out_pass.begin); + EXPECT_EQ(user_info_cases[i].expected_password.len, out_pass.len); + } +} + +TEST(URLCanonTest, Port) { + // We only need to test that the number gets properly put into the output + // buffer. The parser unit tests will test scanning the number correctly. + // + // Note that the CanonicalizePort will always prepend a colon to the output + // to separate it from the colon that it assumes precedes it. + struct PortCase { + const char* input; + int default_port; + const char* expected; + Component expected_component; + bool expected_success; + } port_cases[] = { + // Invalid input should be copied w/ failure. + {"as df", 80, ":as%20df", Component(1, 7), false}, + {"-2", 80, ":-2", Component(1, 2), false}, + // Default port should be omitted. + {"80", 80, "", Component(0, -1), true}, + {"8080", 80, ":8080", Component(1, 4), true}, + // PORT_UNSPECIFIED should mean always keep the port. + {"80", PORT_UNSPECIFIED, ":80", Component(1, 2), true}, + }; + + for (size_t i = 0; i < arraysize(port_cases); i++) { + int url_len = static_cast(strlen(port_cases[i].input)); + Component in_comp(0, url_len); + Component out_comp; + std::string out_str; + StdStringCanonOutput output1(&out_str); + bool success = CanonicalizePort(port_cases[i].input, + in_comp, + port_cases[i].default_port, + &output1, + &out_comp); + output1.Complete(); + + EXPECT_EQ(port_cases[i].expected_success, success); + EXPECT_EQ(std::string(port_cases[i].expected), out_str); + EXPECT_EQ(port_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(port_cases[i].expected_component.len, out_comp.len); + + // Now try the wide version + out_str.clear(); + StdStringCanonOutput output2(&out_str); + base::string16 wide_input(ConvertUTF8ToUTF16(port_cases[i].input)); + success = CanonicalizePort(wide_input.c_str(), + in_comp, + port_cases[i].default_port, + &output2, + &out_comp); + output2.Complete(); + + EXPECT_EQ(port_cases[i].expected_success, success); + EXPECT_EQ(std::string(port_cases[i].expected), out_str); + EXPECT_EQ(port_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(port_cases[i].expected_component.len, out_comp.len); + } +} + +TEST(URLCanonTest, Path) { + DualComponentCase path_cases[] = { + // ----- path collapsing tests ----- + {"/././foo", L"/././foo", "/foo", Component(0, 4), true}, + {"/./.foo", L"/./.foo", "/.foo", Component(0, 5), true}, + {"/foo/.", L"/foo/.", "/foo/", Component(0, 5), true}, + {"/foo/./", L"/foo/./", "/foo/", Component(0, 5), true}, + // double dots followed by a slash or the end of the string count + {"/foo/bar/..", L"/foo/bar/..", "/foo/", Component(0, 5), true}, + {"/foo/bar/../", L"/foo/bar/../", "/foo/", Component(0, 5), true}, + // don't count double dots when they aren't followed by a slash + {"/foo/..bar", L"/foo/..bar", "/foo/..bar", Component(0, 10), true}, + // some in the middle + {"/foo/bar/../ton", L"/foo/bar/../ton", "/foo/ton", Component(0, 8), true}, + {"/foo/bar/../ton/../../a", L"/foo/bar/../ton/../../a", "/a", Component(0, 2), true}, + // we should not be able to go above the root + {"/foo/../../..", L"/foo/../../..", "/", Component(0, 1), true}, + {"/foo/../../../ton", L"/foo/../../../ton", "/ton", Component(0, 4), true}, + // escaped dots should be unescaped and treated the same as dots + {"/foo/%2e", L"/foo/%2e", "/foo/", Component(0, 5), true}, + {"/foo/%2e%2", L"/foo/%2e%2", "/foo/.%2", Component(0, 8), true}, + {"/foo/%2e./%2e%2e/.%2e/%2e.bar", L"/foo/%2e./%2e%2e/.%2e/%2e.bar", "/..bar", Component(0, 6), true}, + // Multiple slashes in a row should be preserved and treated like empty + // directory names. + {"////../..", L"////../..", "//", Component(0, 2), true}, + + // ----- escaping tests ----- + {"/foo", L"/foo", "/foo", Component(0, 4), true}, + // Valid escape sequence + {"/%20foo", L"/%20foo", "/%20foo", Component(0, 7), true}, + // Invalid escape sequence we should pass through unchanged. + {"/foo%", L"/foo%", "/foo%", Component(0, 5), true}, + {"/foo%2", L"/foo%2", "/foo%2", Component(0, 6), true}, + // Invalid escape sequence: bad characters should be treated the same as + // the sourrounding text, not as escaped (in this case, UTF-8). + {"/foo%2zbar", L"/foo%2zbar", "/foo%2zbar", Component(0, 10), true}, + {"/foo%2\xc2\xa9zbar", NULL, "/foo%2%C2%A9zbar", Component(0, 16), true}, + {NULL, L"/foo%2\xc2\xa9zbar", "/foo%2%C3%82%C2%A9zbar", Component(0, 22), true}, + // Regular characters that are escaped should be unescaped + {"/foo%41%7a", L"/foo%41%7a", "/fooAz", Component(0, 6), true}, + // Funny characters that are unescaped should be escaped + {"/foo\x09\x91%91", NULL, "/foo%09%91%91", Component(0, 13), true}, + {NULL, L"/foo\x09\x91%91", "/foo%09%C2%91%91", Component(0, 16), true}, + // Invalid characters that are escaped should cause a failure. + {"/foo%00%51", L"/foo%00%51", "/foo%00Q", Component(0, 8), false}, + // Some characters should be passed through unchanged regardless of esc. + {"/(%28:%3A%29)", L"/(%28:%3A%29)", "/(%28:%3A%29)", Component(0, 13), true}, + // Characters that are properly escaped should not have the case changed + // of hex letters. + {"/%3A%3a%3C%3c", L"/%3A%3a%3C%3c", "/%3A%3a%3C%3c", Component(0, 13), true}, + // Funny characters that are unescaped should be escaped + {"/foo\tbar", L"/foo\tbar", "/foo%09bar", Component(0, 10), true}, + // Backslashes should get converted to forward slashes + {"\\foo\\bar", L"\\foo\\bar", "/foo/bar", Component(0, 8), true}, + // Hashes found in paths (possibly only when the caller explicitly sets + // the path on an already-parsed URL) should be escaped. + {"/foo#bar", L"/foo#bar", "/foo%23bar", Component(0, 10), true}, + // %7f should be allowed and %3D should not be unescaped (these were wrong + // in a previous version). + {"/%7Ffp3%3Eju%3Dduvgw%3Dd", L"/%7Ffp3%3Eju%3Dduvgw%3Dd", "/%7Ffp3%3Eju%3Dduvgw%3Dd", Component(0, 24), true}, + // @ should be passed through unchanged (escaped or unescaped). + {"/@asdf%40", L"/@asdf%40", "/@asdf%40", Component(0, 9), true}, + // Nested escape sequences should result in escaping the leading '%' if + // unescaping would result in a new escape sequence. + {"/%A%42", L"/%A%42", "/%25AB", Component(0, 6), true}, + {"/%%41B", L"/%%41B", "/%25AB", Component(0, 6), true}, + {"/%%41%42", L"/%%41%42", "/%25AB", Component(0, 6), true}, + // Make sure truncated "nested" escapes don't result in reading off the + // string end. + {"/%%41", L"/%%41", "/%A", Component(0, 3), true}, + // Don't unescape the leading '%' if unescaping doesn't result in a valid + // new escape sequence. + {"/%%470", L"/%%470", "/%G0", Component(0, 4), true}, + {"/%%2D%41", L"/%%2D%41", "/%-A", Component(0, 4), true}, + // Don't erroneously downcast a UTF-16 charater in a way that makes it + // look like part of an escape sequence. + {NULL, L"/%%41\x0130", "/%A%C4%B0", Component(0, 9), true}, + + // ----- encoding tests ----- + // Basic conversions + {"/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", L"/\x4f60\x597d\x4f60\x597d", "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD", Component(0, 37), true}, + // Invalid unicode characters should fail. We only do validation on + // UTF-16 input, so this doesn't happen on 8-bit. + {"/\xef\xb7\x90zyx", NULL, "/%EF%B7%90zyx", Component(0, 13), true}, + {NULL, L"/\xfdd0zyx", "/%EF%BF%BDzyx", Component(0, 13), false}, + }; + + for (size_t i = 0; i < arraysize(path_cases); i++) { + if (path_cases[i].input8) { + int len = static_cast(strlen(path_cases[i].input8)); + Component in_comp(0, len); + Component out_comp; + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = + CanonicalizePath(path_cases[i].input8, in_comp, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(path_cases[i].expected_success, success); + EXPECT_EQ(path_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(path_cases[i].expected_component.len, out_comp.len); + EXPECT_EQ(path_cases[i].expected, out_str); + } + + if (path_cases[i].input16) { + base::string16 input16(WStringToUTF16(path_cases[i].input16)); + int len = static_cast(input16.length()); + Component in_comp(0, len); + Component out_comp; + std::string out_str; + StdStringCanonOutput output(&out_str); + + bool success = + CanonicalizePath(input16.c_str(), in_comp, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(path_cases[i].expected_success, success); + EXPECT_EQ(path_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(path_cases[i].expected_component.len, out_comp.len); + EXPECT_EQ(path_cases[i].expected, out_str); + } + } + + // Manual test: embedded NULLs should be escaped and the URL should be marked + // as invalid. + const char path_with_null[] = "/ab\0c"; + Component in_comp(0, 5); + Component out_comp; + + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizePath(path_with_null, in_comp, &output, &out_comp); + output.Complete(); + EXPECT_FALSE(success); + EXPECT_EQ("/ab%00c", out_str); +} + +TEST(URLCanonTest, Query) { + struct QueryCase { + const char* input8; + const wchar_t* input16; + const char* expected; + } query_cases[] = { + // Regular ASCII case. + {"foo=bar", L"foo=bar", "?foo=bar"}, + // Allow question marks in the query without escaping + {"as?df", L"as?df", "?as?df"}, + // Always escape '#' since it would mark the ref. + {"as#df", L"as#df", "?as%23df"}, + // Escape some questionable 8-bit characters, but never unescape. + {"\x02hello\x7f bye", L"\x02hello\x7f bye", "?%02hello%7F%20bye"}, + {"%40%41123", L"%40%41123", "?%40%41123"}, + // Chinese input/output + {"q=\xe4\xbd\xa0\xe5\xa5\xbd", L"q=\x4f60\x597d", "?q=%E4%BD%A0%E5%A5%BD"}, + // Invalid UTF-8/16 input should be replaced with invalid characters. + {"q=\xed\xed", L"q=\xd800\xd800", "?q=%EF%BF%BD%EF%BF%BD"}, + // Don't allow < or > because sometimes they are used for XSS if the + // URL is echoed in content. Firefox does this, IE doesn't. + {"q=", L"q=", "?q=%3Casdf%3E"}, + // Escape double quotemarks in the query. + {"q=\"asdf\"", L"q=\"asdf\"", "?q=%22asdf%22"}, + }; + + for (size_t i = 0; i < arraysize(query_cases); i++) { + Component out_comp; + + if (query_cases[i].input8) { + int len = static_cast(strlen(query_cases[i].input8)); + Component in_comp(0, len); + std::string out_str; + + StdStringCanonOutput output(&out_str); + CanonicalizeQuery(query_cases[i].input8, in_comp, NULL, &output, + &out_comp); + output.Complete(); + + EXPECT_EQ(query_cases[i].expected, out_str); + } + + if (query_cases[i].input16) { + base::string16 input16(WStringToUTF16(query_cases[i].input16)); + int len = static_cast(input16.length()); + Component in_comp(0, len); + std::string out_str; + + StdStringCanonOutput output(&out_str); + CanonicalizeQuery(input16.c_str(), in_comp, NULL, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(query_cases[i].expected, out_str); + } + } + + // Extra test for input with embedded NULL; + std::string out_str; + StdStringCanonOutput output(&out_str); + Component out_comp; + CanonicalizeQuery("a \x00z\x01", Component(0, 5), NULL, &output, &out_comp); + output.Complete(); + EXPECT_EQ("?a%20%00z%01", out_str); +} + +TEST(URLCanonTest, Ref) { + // Refs are trivial, it just checks the encoding. + DualComponentCase ref_cases[] = { + // Regular one, we shouldn't escape spaces, et al. + {"hello, world", L"hello, world", "#hello, world", Component(1, 12), true}, + // UTF-8/wide input should be preserved + {"\xc2\xa9", L"\xa9", "#\xc2\xa9", Component(1, 2), true}, + // Test a characer that takes > 16 bits (U+10300 = old italic letter A) + {"\xF0\x90\x8C\x80ss", L"\xd800\xdf00ss", "#\xF0\x90\x8C\x80ss", Component(1, 6), true}, + // Escaping should be preserved unchanged, even invalid ones + {"%41%a", L"%41%a", "#%41%a", Component(1, 5), true}, + // Invalid UTF-8/16 input should be flagged and the input made valid + {"\xc2", NULL, "#\xef\xbf\xbd", Component(1, 3), true}, + {NULL, L"\xd800\x597d", "#\xef\xbf\xbd\xe5\xa5\xbd", Component(1, 6), true}, + // Test a Unicode invalid character. + {"a\xef\xb7\x90", L"a\xfdd0", "#a\xef\xbf\xbd", Component(1, 4), true}, + // Refs can have # signs and we should preserve them. + {"asdf#qwer", L"asdf#qwer", "#asdf#qwer", Component(1, 9), true}, + {"#asdf", L"#asdf", "##asdf", Component(1, 5), true}, + }; + + for (size_t i = 0; i < arraysize(ref_cases); i++) { + // 8-bit input + if (ref_cases[i].input8) { + int len = static_cast(strlen(ref_cases[i].input8)); + Component in_comp(0, len); + Component out_comp; + + std::string out_str; + StdStringCanonOutput output(&out_str); + CanonicalizeRef(ref_cases[i].input8, in_comp, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(ref_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(ref_cases[i].expected_component.len, out_comp.len); + EXPECT_EQ(ref_cases[i].expected, out_str); + } + + // 16-bit input + if (ref_cases[i].input16) { + base::string16 input16(WStringToUTF16(ref_cases[i].input16)); + int len = static_cast(input16.length()); + Component in_comp(0, len); + Component out_comp; + + std::string out_str; + StdStringCanonOutput output(&out_str); + CanonicalizeRef(input16.c_str(), in_comp, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(ref_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(ref_cases[i].expected_component.len, out_comp.len); + EXPECT_EQ(ref_cases[i].expected, out_str); + } + } + + // Try one with an embedded NULL. It should be stripped. + const char null_input[5] = "ab\x00z"; + Component null_input_component(0, 4); + Component out_comp; + + std::string out_str; + StdStringCanonOutput output(&out_str); + CanonicalizeRef(null_input, null_input_component, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(1, out_comp.begin); + EXPECT_EQ(3, out_comp.len); + EXPECT_EQ("#abz", out_str); +} + +TEST(URLCanonTest, CanonicalizeStandardURL) { + // The individual component canonicalize tests should have caught the cases + // for each of those components. Here, we just need to test that the various + // parts are included or excluded properly, and have the correct separators. + struct URLCase { + const char* input; + const char* expected; + bool expected_success; + } cases[] = { + {"http://www.google.com/foo?bar=baz#", "http://www.google.com/foo?bar=baz#", true}, + {"http://[www.google.com]/", "http://[www.google.com]/", false}, + {"ht\ttp:@www.google.com:80/;p?#", "ht%09tp://www.google.com:80/;p?#", false}, + {"http:////////user:@google.com:99?foo", "http://user@google.com:99/?foo", true}, + {"www.google.com", ":www.google.com/", true}, + {"http://192.0x00A80001", "http://192.168.0.1/", true}, + {"http://www/foo%2Ehtml", "http://www/foo.html", true}, + {"http://user:pass@/", "http://user:pass@/", false}, + {"http://%25DOMAIN:foobar@foodomain.com/", "http://%25DOMAIN:foobar@foodomain.com/", true}, + + // Backslashes should get converted to forward slashes. + {"http:\\\\www.google.com\\foo", "http://www.google.com/foo", true}, + + // Busted refs shouldn't make the whole thing fail. + {"http://www.google.com/asdf#\xc2", "http://www.google.com/asdf#\xef\xbf\xbd", true}, + + // Basic port tests. + {"http://foo:80/", "http://foo/", true}, + {"http://foo:81/", "http://foo:81/", true}, + {"httpa://foo:80/", "httpa://foo:80/", true}, + {"http://foo:-80/", "http://foo:-80/", false}, + + {"https://foo:443/", "https://foo/", true}, + {"https://foo:80/", "https://foo:80/", true}, + {"ftp://foo:21/", "ftp://foo/", true}, + {"ftp://foo:80/", "ftp://foo:80/", true}, + {"gopher://foo:70/", "gopher://foo/", true}, + {"gopher://foo:443/", "gopher://foo:443/", true}, + {"ws://foo:80/", "ws://foo/", true}, + {"ws://foo:81/", "ws://foo:81/", true}, + {"ws://foo:443/", "ws://foo:443/", true}, + {"ws://foo:815/", "ws://foo:815/", true}, + {"wss://foo:80/", "wss://foo:80/", true}, + {"wss://foo:81/", "wss://foo:81/", true}, + {"wss://foo:443/", "wss://foo/", true}, + {"wss://foo:815/", "wss://foo:815/", true}, + + // This particular code path ends up "backing up" to replace an invalid + // host ICU generated with an escaped version. Test that in the context + // of a full URL to make sure the backing up doesn't mess up the non-host + // parts of the URL. "EF B9 AA" is U+FE6A which is a type of percent that + // ICU will convert to an ASCII one, generating "%81". + {"ws:)W\x1eW\xef\xb9\xaa""81:80/", "ws://%29w%1ew%81/", false}, + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + int url_len = static_cast(strlen(cases[i].input)); + Parsed parsed; + ParseStandardURL(cases[i].input, url_len, &parsed); + + Parsed out_parsed; + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizeStandardURL( + cases[i].input, url_len, parsed, NULL, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(cases[i].expected_success, success); + EXPECT_EQ(cases[i].expected, out_str); + } +} + +// The codepath here is the same as for regular canonicalization, so we just +// need to test that things are replaced or not correctly. +TEST(URLCanonTest, ReplaceStandardURL) { + ReplaceCase replace_cases[] = { + // Common case of truncating the path. + {"http://www.google.com/foo?bar=baz#ref", NULL, NULL, NULL, NULL, NULL, "/", kDeleteComp, kDeleteComp, "http://www.google.com/"}, + // Replace everything + {"http://a:b@google.com:22/foo;bar?baz@cat", "https", "me", "pw", "host.com", "99", "/path", "query", "ref", "https://me:pw@host.com:99/path?query#ref"}, + // Replace nothing + {"http://a:b@google.com:22/foo?baz@cat", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "http://a:b@google.com:22/foo?baz@cat"}, + // Replace scheme with filesystem. The result is garbage, but you asked + // for it. + {"http://a:b@google.com:22/foo?baz@cat", "filesystem", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "filesystem://a:b@google.com:22/foo?baz@cat"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + int base_len = static_cast(strlen(cur.base)); + Parsed parsed; + ParseStandardURL(cur.base, base_len, &parsed); + + Replacements r; + typedef Replacements R; // Clean up syntax. + + // Note that for the scheme we pass in a different clear function since + // there is no function to clear the scheme. + SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); + SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); + SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); + SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); + SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); + SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); + SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); + SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); + + std::string out_str; + StdStringCanonOutput output(&out_str); + Parsed out_parsed; + ReplaceStandardURL(replace_cases[i].base, parsed, r, NULL, &output, + &out_parsed); + output.Complete(); + + EXPECT_EQ(replace_cases[i].expected, out_str); + } + + // The path pointer should be ignored if the address is invalid. + { + const char src[] = "http://www.google.com/here_is_the_path"; + int src_len = static_cast(strlen(src)); + + Parsed parsed; + ParseStandardURL(src, src_len, &parsed); + + // Replace the path to 0 length string. By using 1 as the string address, + // the test should get an access violation if it tries to dereference it. + Replacements r; + r.SetPath(reinterpret_cast(0x00000001), Component(0, 0)); + std::string out_str1; + StdStringCanonOutput output1(&out_str1); + Parsed new_parsed; + ReplaceStandardURL(src, parsed, r, NULL, &output1, &new_parsed); + output1.Complete(); + EXPECT_STREQ("http://www.google.com/", out_str1.c_str()); + + // Same with an "invalid" path. + r.SetPath(reinterpret_cast(0x00000001), Component()); + std::string out_str2; + StdStringCanonOutput output2(&out_str2); + ReplaceStandardURL(src, parsed, r, NULL, &output2, &new_parsed); + output2.Complete(); + EXPECT_STREQ("http://www.google.com/", out_str2.c_str()); + } +} + +TEST(URLCanonTest, ReplaceFileURL) { + ReplaceCase replace_cases[] = { + // Replace everything + {"file:///C:/gaba?query#ref", NULL, NULL, NULL, "filer", NULL, "/foo", "b", "c", "file://filer/foo?b#c"}, + // Replace nothing + {"file:///C:/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///C:/gaba?query#ref"}, + // Clear non-path components (common) + {"file:///C:/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, kDeleteComp, kDeleteComp, "file:///C:/gaba"}, + // Replace path with something that doesn't begin with a slash and make + // sure it gets added properly. + {"file:///C:/gaba", NULL, NULL, NULL, NULL, NULL, "interesting/", NULL, NULL, "file:///interesting/"}, + {"file:///home/gaba?query#ref", NULL, NULL, NULL, "filer", NULL, "/foo", "b", "c", "file://filer/foo?b#c"}, + {"file:///home/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///home/gaba?query#ref"}, + {"file:///home/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, kDeleteComp, kDeleteComp, "file:///home/gaba"}, + {"file:///home/gaba", NULL, NULL, NULL, NULL, NULL, "interesting/", NULL, NULL, "file:///interesting/"}, + // Replace scheme -- shouldn't do anything. + {"file:///C:/gaba?query#ref", "http", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///C:/gaba?query#ref"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + int base_len = static_cast(strlen(cur.base)); + Parsed parsed; + ParseFileURL(cur.base, base_len, &parsed); + + Replacements r; + typedef Replacements R; // Clean up syntax. + SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); + SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); + SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); + SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); + SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); + SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); + SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); + SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); + + std::string out_str; + StdStringCanonOutput output(&out_str); + Parsed out_parsed; + ReplaceFileURL(cur.base, parsed, r, NULL, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(replace_cases[i].expected, out_str); + } +} + +TEST(URLCanonTest, ReplaceFileSystemURL) { + ReplaceCase replace_cases[] = { + // Replace everything in the outer URL. + {"filesystem:file:///temporary/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, "/foo", "b", "c", "filesystem:file:///temporary/foo?b#c"}, + // Replace nothing + {"filesystem:file:///temporary/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "filesystem:file:///temporary/gaba?query#ref"}, + // Clear non-path components (common) + {"filesystem:file:///temporary/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, kDeleteComp, kDeleteComp, "filesystem:file:///temporary/gaba"}, + // Replace path with something that doesn't begin with a slash and make + // sure it gets added properly. + {"filesystem:file:///temporary/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, "interesting/", NULL, NULL, "filesystem:file:///temporary/interesting/?query#ref"}, + // Replace scheme -- shouldn't do anything. + {"filesystem:http://u:p@bar.com/t/gaba?query#ref", "http", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "filesystem:http://u:p@bar.com/t/gaba?query#ref"}, + // Replace username -- shouldn't do anything. + {"filesystem:http://u:p@bar.com/t/gaba?query#ref", NULL, "u2", NULL, NULL, NULL, NULL, NULL, NULL, "filesystem:http://u:p@bar.com/t/gaba?query#ref"}, + // Replace password -- shouldn't do anything. + {"filesystem:http://u:p@bar.com/t/gaba?query#ref", NULL, NULL, "pw2", NULL, NULL, NULL, NULL, NULL, "filesystem:http://u:p@bar.com/t/gaba?query#ref"}, + // Replace host -- shouldn't do anything. + {"filesystem:http://u:p@bar.com/t/gaba?query#ref", NULL, NULL, NULL, "foo.com", NULL, NULL, NULL, NULL, "filesystem:http://u:p@bar.com/t/gaba?query#ref"}, + // Replace port -- shouldn't do anything. + {"filesystem:http://u:p@bar.com:40/t/gaba?query#ref", NULL, NULL, NULL, NULL, "41", NULL, NULL, NULL, "filesystem:http://u:p@bar.com:40/t/gaba?query#ref"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + int base_len = static_cast(strlen(cur.base)); + Parsed parsed; + ParseFileSystemURL(cur.base, base_len, &parsed); + + Replacements r; + typedef Replacements R; // Clean up syntax. + SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); + SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); + SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); + SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); + SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); + SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); + SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); + SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); + + std::string out_str; + StdStringCanonOutput output(&out_str); + Parsed out_parsed; + ReplaceFileSystemURL(cur.base, parsed, r, NULL, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(replace_cases[i].expected, out_str); + } +} + +TEST(URLCanonTest, ReplacePathURL) { + ReplaceCase replace_cases[] = { + // Replace everything + {"data:foo", "javascript", NULL, NULL, NULL, NULL, "alert('foo?');", NULL, NULL, "javascript:alert('foo?');"}, + // Replace nothing + {"data:foo", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "data:foo"}, + // Replace one or the other + {"data:foo", "javascript", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "javascript:foo"}, + {"data:foo", NULL, NULL, NULL, NULL, NULL, "bar", NULL, NULL, "data:bar"}, + {"data:foo", NULL, NULL, NULL, NULL, NULL, kDeleteComp, NULL, NULL, "data:"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + int base_len = static_cast(strlen(cur.base)); + Parsed parsed; + ParsePathURL(cur.base, base_len, false, &parsed); + + Replacements r; + typedef Replacements R; // Clean up syntax. + SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); + SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); + SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); + SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); + SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); + SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); + SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); + SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); + + std::string out_str; + StdStringCanonOutput output(&out_str); + Parsed out_parsed; + ReplacePathURL(cur.base, parsed, r, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(replace_cases[i].expected, out_str); + } +} + +TEST(URLCanonTest, ReplaceMailtoURL) { + ReplaceCase replace_cases[] = { + // Replace everything + {"mailto:jon@foo.com?body=sup", "mailto", NULL, NULL, NULL, NULL, "addr1", "to=tony", NULL, "mailto:addr1?to=tony"}, + // Replace nothing + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "mailto:jon@foo.com?body=sup"}, + // Replace the path + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "jason", NULL, NULL, "mailto:jason?body=sup"}, + // Replace the query + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "custom=1", NULL, "mailto:jon@foo.com?custom=1"}, + // Replace the path and query + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "jason", "custom=1", NULL, "mailto:jason?custom=1"}, + // Set the query to empty (should leave trailing question mark) + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "", NULL, "mailto:jon@foo.com?"}, + // Clear the query + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "|", NULL, "mailto:jon@foo.com"}, + // Clear the path + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "|", NULL, NULL, "mailto:?body=sup"}, + // Clear the path + query + {"mailto:", NULL, NULL, NULL, NULL, NULL, "|", "|", NULL, "mailto:"}, + // Setting the ref should have no effect + {"mailto:addr1", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "BLAH", "mailto:addr1"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + int base_len = static_cast(strlen(cur.base)); + Parsed parsed; + ParseMailtoURL(cur.base, base_len, &parsed); + + Replacements r; + typedef Replacements R; + SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); + SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); + SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); + SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); + SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); + SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); + SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); + SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); + + std::string out_str; + StdStringCanonOutput output(&out_str); + Parsed out_parsed; + ReplaceMailtoURL(cur.base, parsed, r, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(replace_cases[i].expected, out_str); + } +} + +TEST(URLCanonTest, CanonicalizeFileURL) { + struct URLCase { + const char* input; + const char* expected; + bool expected_success; + Component expected_host; + Component expected_path; + } cases[] = { +#ifdef _WIN32 + // Windows-style paths + {"file:c:\\foo\\bar.html", "file:///C:/foo/bar.html", true, Component(), Component(7, 16)}, + {" File:c|////foo\\bar.html", "file:///C:////foo/bar.html", true, Component(), Component(7, 19)}, + {"file:", "file:///", true, Component(), Component(7, 1)}, + {"file:UNChost/path", "file://unchost/path", true, Component(7, 7), Component(14, 5)}, + // CanonicalizeFileURL supports absolute Windows style paths for IE + // compatibility. Note that the caller must decide that this is a file + // URL itself so it can call the file canonicalizer. This is usually + // done automatically as part of relative URL resolving. + {"c:\\foo\\bar", "file:///C:/foo/bar", true, Component(), Component(7, 11)}, + {"C|/foo/bar", "file:///C:/foo/bar", true, Component(), Component(7, 11)}, + {"/C|\\foo\\bar", "file:///C:/foo/bar", true, Component(), Component(7, 11)}, + {"//C|/foo/bar", "file:///C:/foo/bar", true, Component(), Component(7, 11)}, + {"//server/file", "file://server/file", true, Component(7, 6), Component(13, 5)}, + {"\\\\server\\file", "file://server/file", true, Component(7, 6), Component(13, 5)}, + {"/\\server/file", "file://server/file", true, Component(7, 6), Component(13, 5)}, + // We should preserve the number of slashes after the colon for IE + // compatibility, except when there is none, in which case we should + // add one. + {"file:c:foo/bar.html", "file:///C:/foo/bar.html", true, Component(), Component(7, 16)}, + {"file:/\\/\\C:\\\\//foo\\bar.html", "file:///C:////foo/bar.html", true, Component(), Component(7, 19)}, + // Three slashes should be non-UNC, even if there is no drive spec (IE + // does this, which makes the resulting request invalid). + {"file:///foo/bar.txt", "file:///foo/bar.txt", true, Component(), Component(7, 12)}, + // TODO(brettw) we should probably fail for invalid host names, which + // would change the expected result on this test. We also currently allow + // colon even though it's probably invalid, because its currently the + // "natural" result of the way the canonicalizer is written. There doesn't + // seem to be a strong argument for why allowing it here would be bad, so + // we just tolerate it and the load will fail later. + {"FILE:/\\/\\7:\\\\//foo\\bar.html", "file://7:////foo/bar.html", false, Component(7, 2), Component(9, 16)}, + {"file:filer/home\\me", "file://filer/home/me", true, Component(7, 5), Component(12, 8)}, + // Make sure relative paths can't go above the "C:" + {"file:///C:/foo/../../../bar.html", "file:///C:/bar.html", true, Component(), Component(7, 12)}, + // Busted refs shouldn't make the whole thing fail. + {"file:///C:/asdf#\xc2", "file:///C:/asdf#\xef\xbf\xbd", true, Component(), Component(7, 8)}, +#else + // Unix-style paths + {"file:///home/me", "file:///home/me", true, Component(), Component(7, 8)}, + // Windowsy ones should get still treated as Unix-style. + {"file:c:\\foo\\bar.html", "file:///c:/foo/bar.html", true, Component(), Component(7, 16)}, + {"file:c|//foo\\bar.html", "file:///c%7C//foo/bar.html", true, Component(), Component(7, 19)}, + // file: tests from WebKit (LayoutTests/fast/loader/url-parse-1.html) + {"//", "file:///", true, Component(), Component(7, 1)}, + {"///", "file:///", true, Component(), Component(7, 1)}, + {"///test", "file:///test", true, Component(), Component(7, 5)}, + {"file://test", "file://test/", true, Component(7, 4), Component(11, 1)}, + {"file://localhost", "file://localhost/", true, Component(7, 9), Component(16, 1)}, + {"file://localhost/", "file://localhost/", true, Component(7, 9), Component(16, 1)}, + {"file://localhost/test", "file://localhost/test", true, Component(7, 9), Component(16, 5)}, +#endif // _WIN32 + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + int url_len = static_cast(strlen(cases[i].input)); + Parsed parsed; + ParseFileURL(cases[i].input, url_len, &parsed); + + Parsed out_parsed; + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizeFileURL(cases[i].input, url_len, parsed, NULL, + &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(cases[i].expected_success, success); + EXPECT_EQ(cases[i].expected, out_str); + + // Make sure the spec was properly identified, the file canonicalizer has + // different code for writing the spec. + EXPECT_EQ(0, out_parsed.scheme.begin); + EXPECT_EQ(4, out_parsed.scheme.len); + + EXPECT_EQ(cases[i].expected_host.begin, out_parsed.host.begin); + EXPECT_EQ(cases[i].expected_host.len, out_parsed.host.len); + + EXPECT_EQ(cases[i].expected_path.begin, out_parsed.path.begin); + EXPECT_EQ(cases[i].expected_path.len, out_parsed.path.len); + } +} + +TEST(URLCanonTest, CanonicalizeFileSystemURL) { + struct URLCase { + const char* input; + const char* expected; + bool expected_success; + } cases[] = { + {"Filesystem:htTp://www.Foo.com:80/tempoRary", "filesystem:http://www.foo.com/tempoRary/", true}, + {"filesystem:httpS://www.foo.com/temporary/", "filesystem:https://www.foo.com/temporary/", true}, + {"filesystem:http://www.foo.com//", "filesystem:http://www.foo.com//", false}, + {"filesystem:http://www.foo.com/persistent/bob?query#ref", "filesystem:http://www.foo.com/persistent/bob?query#ref", true}, + {"filesystem:fIle://\\temporary/", "filesystem:file:///temporary/", true}, + {"filesystem:fiLe:///temporary", "filesystem:file:///temporary/", true}, + {"filesystem:File:///temporary/Bob?qUery#reF", "filesystem:file:///temporary/Bob?qUery#reF", true}, + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + int url_len = static_cast(strlen(cases[i].input)); + Parsed parsed; + ParseFileSystemURL(cases[i].input, url_len, &parsed); + + Parsed out_parsed; + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizeFileSystemURL(cases[i].input, url_len, parsed, + NULL, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(cases[i].expected_success, success); + EXPECT_EQ(cases[i].expected, out_str); + + // Make sure the spec was properly identified, the filesystem canonicalizer + // has different code for writing the spec. + EXPECT_EQ(0, out_parsed.scheme.begin); + EXPECT_EQ(10, out_parsed.scheme.len); + if (success) + EXPECT_GT(out_parsed.path.len, 0); + } +} + +TEST(URLCanonTest, CanonicalizePathURL) { + // Path URLs should get canonicalized schemes but nothing else. + struct PathCase { + const char* input; + const char* expected; + } path_cases[] = { + {"javascript:", "javascript:"}, + {"JavaScript:Foo", "javascript:Foo"}, + {":\":This /is interesting;?#", ":\":This /is interesting;?#"}, + }; + + for (size_t i = 0; i < arraysize(path_cases); i++) { + int url_len = static_cast(strlen(path_cases[i].input)); + Parsed parsed; + ParsePathURL(path_cases[i].input, url_len, true, &parsed); + + Parsed out_parsed; + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizePathURL(path_cases[i].input, url_len, parsed, + &output, &out_parsed); + output.Complete(); + + EXPECT_TRUE(success); + EXPECT_EQ(path_cases[i].expected, out_str); + + EXPECT_EQ(0, out_parsed.host.begin); + EXPECT_EQ(-1, out_parsed.host.len); + + // When we end with a colon at the end, there should be no path. + if (path_cases[i].input[url_len - 1] == ':') { + EXPECT_EQ(0, out_parsed.GetContent().begin); + EXPECT_EQ(-1, out_parsed.GetContent().len); + } + } +} + +TEST(URLCanonTest, CanonicalizeMailtoURL) { + struct URLCase { + const char* input; + const char* expected; + bool expected_success; + Component expected_path; + Component expected_query; + } cases[] = { + {"mailto:addr1", "mailto:addr1", true, Component(7, 5), Component()}, + {"mailto:addr1@foo.com", "mailto:addr1@foo.com", true, Component(7, 13), Component()}, + // Trailing whitespace is stripped. + {"MaIlTo:addr1 \t ", "mailto:addr1", true, Component(7, 5), Component()}, + {"MaIlTo:addr1?to=jon", "mailto:addr1?to=jon", true, Component(7, 5), Component(13,6)}, + {"mailto:addr1,addr2", "mailto:addr1,addr2", true, Component(7, 11), Component()}, + {"mailto:addr1, addr2", "mailto:addr1, addr2", true, Component(7, 12), Component()}, + {"mailto:addr1%2caddr2", "mailto:addr1%2caddr2", true, Component(7, 13), Component()}, + {"mailto:\xF0\x90\x8C\x80", "mailto:%F0%90%8C%80", true, Component(7, 12), Component()}, + // Null character should be escaped to %00 + {"mailto:addr1\0addr2?foo", "mailto:addr1%00addr2?foo", true, Component(7, 13), Component(21, 3)}, + // Invalid -- UTF-8 encoded surrogate value. + {"mailto:\xed\xa0\x80", "mailto:%EF%BF%BD", false, Component(7, 9), Component()}, + {"mailto:addr1?", "mailto:addr1?", true, Component(7, 5), Component(13, 0)}, + }; + + // Define outside of loop to catch bugs where components aren't reset + Parsed parsed; + Parsed out_parsed; + + for (size_t i = 0; i < arraysize(cases); i++) { + int url_len = static_cast(strlen(cases[i].input)); + if (i == 8) { + // The 9th test case purposely has a '\0' in it -- don't count it + // as the string terminator. + url_len = 22; + } + ParseMailtoURL(cases[i].input, url_len, &parsed); + + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizeMailtoURL(cases[i].input, url_len, parsed, + &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(cases[i].expected_success, success); + EXPECT_EQ(cases[i].expected, out_str); + + // Make sure the spec was properly identified + EXPECT_EQ(0, out_parsed.scheme.begin); + EXPECT_EQ(6, out_parsed.scheme.len); + + EXPECT_EQ(cases[i].expected_path.begin, out_parsed.path.begin); + EXPECT_EQ(cases[i].expected_path.len, out_parsed.path.len); + + EXPECT_EQ(cases[i].expected_query.begin, out_parsed.query.begin); + EXPECT_EQ(cases[i].expected_query.len, out_parsed.query.len); + } +} + +#ifndef WIN32 + +TEST(URLCanonTest, _itoa_s) { + // We fill the buffer with 0xff to ensure that it's getting properly + // null-terminated. We also allocate one byte more than what we tell + // _itoa_s about, and ensure that the extra byte is untouched. + char buf[6]; + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(0, _itoa_s(12, buf, sizeof(buf) - 1, 10)); + EXPECT_STREQ("12", buf); + EXPECT_EQ('\xFF', buf[3]); + + // Test the edge cases - exactly the buffer size and one over + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(0, _itoa_s(1234, buf, sizeof(buf) - 1, 10)); + EXPECT_STREQ("1234", buf); + EXPECT_EQ('\xFF', buf[5]); + + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(EINVAL, _itoa_s(12345, buf, sizeof(buf) - 1, 10)); + EXPECT_EQ('\xFF', buf[5]); // should never write to this location + + // Test the template overload (note that this will see the full buffer) + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(0, _itoa_s(12, buf, 10)); + EXPECT_STREQ("12", buf); + EXPECT_EQ('\xFF', buf[3]); + + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(0, _itoa_s(12345, buf, 10)); + EXPECT_STREQ("12345", buf); + + EXPECT_EQ(EINVAL, _itoa_s(123456, buf, 10)); + + // Test that radix 16 is supported. + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(0, _itoa_s(1234, buf, sizeof(buf) - 1, 16)); + EXPECT_STREQ("4d2", buf); + EXPECT_EQ('\xFF', buf[5]); +} + +TEST(URLCanonTest, _itow_s) { + // We fill the buffer with 0xff to ensure that it's getting properly + // null-terminated. We also allocate one byte more than what we tell + // _itoa_s about, and ensure that the extra byte is untouched. + base::char16 buf[6]; + const char fill_mem = 0xff; + const base::char16 fill_char = 0xffff; + memset(buf, fill_mem, sizeof(buf)); + EXPECT_EQ(0, _itow_s(12, buf, sizeof(buf) / 2 - 1, 10)); + EXPECT_EQ(WStringToUTF16(L"12"), base::string16(buf)); + EXPECT_EQ(fill_char, buf[3]); + + // Test the edge cases - exactly the buffer size and one over + EXPECT_EQ(0, _itow_s(1234, buf, sizeof(buf) / 2 - 1, 10)); + EXPECT_EQ(WStringToUTF16(L"1234"), base::string16(buf)); + EXPECT_EQ(fill_char, buf[5]); + + memset(buf, fill_mem, sizeof(buf)); + EXPECT_EQ(EINVAL, _itow_s(12345, buf, sizeof(buf) / 2 - 1, 10)); + EXPECT_EQ(fill_char, buf[5]); // should never write to this location + + // Test the template overload (note that this will see the full buffer) + memset(buf, fill_mem, sizeof(buf)); + EXPECT_EQ(0, _itow_s(12, buf, 10)); + EXPECT_EQ(WStringToUTF16(L"12"), base::string16(buf)); + EXPECT_EQ(fill_char, buf[3]); + + memset(buf, fill_mem, sizeof(buf)); + EXPECT_EQ(0, _itow_s(12345, buf, 10)); + EXPECT_EQ(WStringToUTF16(L"12345"), base::string16(buf)); + + EXPECT_EQ(EINVAL, _itow_s(123456, buf, 10)); +} + +#endif // !WIN32 + +// Returns true if the given two structures are the same. +static bool ParsedIsEqual(const Parsed& a, const Parsed& b) { + return a.scheme.begin == b.scheme.begin && a.scheme.len == b.scheme.len && + a.username.begin == b.username.begin && a.username.len == b.username.len && + a.password.begin == b.password.begin && a.password.len == b.password.len && + a.host.begin == b.host.begin && a.host.len == b.host.len && + a.port.begin == b.port.begin && a.port.len == b.port.len && + a.path.begin == b.path.begin && a.path.len == b.path.len && + a.query.begin == b.query.begin && a.query.len == b.query.len && + a.ref.begin == b.ref.begin && a.ref.len == b.ref.len; +} + +TEST(URLCanonTest, ResolveRelativeURL) { + struct RelativeCase { + const char* base; // Input base URL: MUST BE CANONICAL + bool is_base_hier; // Is the base URL hierarchical + bool is_base_file; // Tells us if the base is a file URL. + const char* test; // Input URL to test against. + bool succeed_relative; // Whether we expect IsRelativeURL to succeed + bool is_rel; // Whether we expect |test| to be relative or not. + bool succeed_resolve; // Whether we expect ResolveRelativeURL to succeed. + const char* resolved; // What we expect in the result when resolving. + } rel_cases[] = { + // Basic absolute input. + {"http://host/a", true, false, "http://another/", true, false, false, NULL}, + {"http://host/a", true, false, "http:////another/", true, false, false, NULL}, + // Empty relative URLs should only remove the ref part of the URL, + // leaving the rest unchanged. + {"http://foo/bar", true, false, "", true, true, true, "http://foo/bar"}, + {"http://foo/bar#ref", true, false, "", true, true, true, "http://foo/bar"}, + {"http://foo/bar#", true, false, "", true, true, true, "http://foo/bar"}, + // Spaces at the ends of the relative path should be ignored. + {"http://foo/bar", true, false, " another ", true, true, true, "http://foo/another"}, + {"http://foo/bar", true, false, " . ", true, true, true, "http://foo/"}, + {"http://foo/bar", true, false, " \t ", true, true, true, "http://foo/bar"}, + // Matching schemes without two slashes are treated as relative. + {"http://host/a", true, false, "http:path", true, true, true, "http://host/path"}, + {"http://host/a/", true, false, "http:path", true, true, true, "http://host/a/path"}, + {"http://host/a", true, false, "http:/path", true, true, true, "http://host/path"}, + {"http://host/a", true, false, "HTTP:/path", true, true, true, "http://host/path"}, + // Nonmatching schemes are absolute. + {"http://host/a", true, false, "https:host2", true, false, false, NULL}, + {"http://host/a", true, false, "htto:/host2", true, false, false, NULL}, + // Absolute path input + {"http://host/a", true, false, "/b/c/d", true, true, true, "http://host/b/c/d"}, + {"http://host/a", true, false, "\\b\\c\\d", true, true, true, "http://host/b/c/d"}, + {"http://host/a", true, false, "/b/../c", true, true, true, "http://host/c"}, + {"http://host/a?b#c", true, false, "/b/../c", true, true, true, "http://host/c"}, + {"http://host/a", true, false, "\\b/../c?x#y", true, true, true, "http://host/c?x#y"}, + {"http://host/a?b#c", true, false, "/b/../c?x#y", true, true, true, "http://host/c?x#y"}, + // Relative path input + {"http://host/a", true, false, "b", true, true, true, "http://host/b"}, + {"http://host/a", true, false, "bc/de", true, true, true, "http://host/bc/de"}, + {"http://host/a/", true, false, "bc/de?query#ref", true, true, true, "http://host/a/bc/de?query#ref"}, + {"http://host/a/", true, false, ".", true, true, true, "http://host/a/"}, + {"http://host/a/", true, false, "..", true, true, true, "http://host/"}, + {"http://host/a/", true, false, "./..", true, true, true, "http://host/"}, + {"http://host/a/", true, false, "../.", true, true, true, "http://host/"}, + {"http://host/a/", true, false, "././.", true, true, true, "http://host/a/"}, + {"http://host/a?query#ref", true, false, "../../../foo", true, true, true, "http://host/foo"}, + // Query input + {"http://host/a", true, false, "?foo=bar", true, true, true, "http://host/a?foo=bar"}, + {"http://host/a?x=y#z", true, false, "?", true, true, true, "http://host/a?"}, + {"http://host/a?x=y#z", true, false, "?foo=bar#com", true, true, true, "http://host/a?foo=bar#com"}, + // Ref input + {"http://host/a", true, false, "#ref", true, true, true, "http://host/a#ref"}, + {"http://host/a#b", true, false, "#", true, true, true, "http://host/a#"}, + {"http://host/a?foo=bar#hello", true, false, "#bye", true, true, true, "http://host/a?foo=bar#bye"}, + // Non-hierarchical base: no relative handling. Relative input should + // error, and if a scheme is present, it should be treated as absolute. + {"data:foobar", false, false, "baz.html", false, false, false, NULL}, + {"data:foobar", false, false, "data:baz", true, false, false, NULL}, + {"data:foobar", false, false, "data:/base", true, false, false, NULL}, + // Non-hierarchical base: absolute input should succeed. + {"data:foobar", false, false, "http://host/", true, false, false, NULL}, + {"data:foobar", false, false, "http:host", true, false, false, NULL}, + // Non-hierarchical base: empty URL should give error. + {"data:foobar", false, false, "", false, false, false, NULL}, + // Invalid schemes should be treated as relative. + {"http://foo/bar", true, false, "./asd:fgh", true, true, true, "http://foo/asd:fgh"}, + {"http://foo/bar", true, false, ":foo", true, true, true, "http://foo/:foo"}, + {"http://foo/bar", true, false, " hello world", true, true, true, "http://foo/hello%20world"}, + {"data:asdf", false, false, ":foo", false, false, false, NULL}, + {"data:asdf", false, false, "bad(':foo')", false, false, false, NULL}, + // We should treat semicolons like any other character in URL resolving + {"http://host/a", true, false, ";foo", true, true, true, "http://host/;foo"}, + {"http://host/a;", true, false, ";foo", true, true, true, "http://host/;foo"}, + {"http://host/a", true, false, ";/../bar", true, true, true, "http://host/bar"}, + // Relative URLs can also be written as "//foo/bar" which is relative to + // the scheme. In this case, it would take the old scheme, so for http + // the example would resolve to "http://foo/bar". + {"http://host/a", true, false, "//another", true, true, true, "http://another/"}, + {"http://host/a", true, false, "//another/path?query#ref", true, true, true, "http://another/path?query#ref"}, + {"http://host/a", true, false, "///another/path", true, true, true, "http://another/path"}, + {"http://host/a", true, false, "//Another\\path", true, true, true, "http://another/path"}, + {"http://host/a", true, false, "//", true, true, false, "http:"}, + // IE will also allow one or the other to be a backslash to get the same + // behavior. + {"http://host/a", true, false, "\\/another/path", true, true, true, "http://another/path"}, + {"http://host/a", true, false, "/\\Another\\path", true, true, true, "http://another/path"}, +#ifdef WIN32 + // Resolving against Windows file base URLs. + {"file:///C:/foo", true, true, "http://host/", true, false, false, NULL}, + {"file:///C:/foo", true, true, "bar", true, true, true, "file:///C:/bar"}, + {"file:///C:/foo", true, true, "../../../bar.html", true, true, true, "file:///C:/bar.html"}, + {"file:///C:/foo", true, true, "/../bar.html", true, true, true, "file:///C:/bar.html"}, + // But two backslashes on Windows should be UNC so should be treated + // as absolute. + {"http://host/a", true, false, "\\\\another\\path", true, false, false, NULL}, + // IE doesn't support drive specs starting with two slashes. It fails + // immediately and doesn't even try to load. We fix it up to either + // an absolute path or UNC depending on what it looks like. + {"file:///C:/something", true, true, "//c:/foo", true, true, true, "file:///C:/foo"}, + {"file:///C:/something", true, true, "//localhost/c:/foo", true, true, true, "file:///C:/foo"}, + // Windows drive specs should be allowed and treated as absolute. + {"file:///C:/foo", true, true, "c:", true, false, false, NULL}, + {"file:///C:/foo", true, true, "c:/foo", true, false, false, NULL}, + {"http://host/a", true, false, "c:\\foo", true, false, false, NULL}, + // Relative paths with drive letters should be allowed when the base is + // also a file. + {"file:///C:/foo", true, true, "/z:/bar", true, true, true, "file:///Z:/bar"}, + // Treat absolute paths as being off of the drive. + {"file:///C:/foo", true, true, "/bar", true, true, true, "file:///C:/bar"}, + {"file://localhost/C:/foo", true, true, "/bar", true, true, true, "file://localhost/C:/bar"}, + {"file:///C:/foo/com/", true, true, "/bar", true, true, true, "file:///C:/bar"}, + // On Windows, two slashes without a drive letter when the base is a file + // means that the path is UNC. + {"file:///C:/something", true, true, "//somehost/path", true, true, true, "file://somehost/path"}, + {"file:///C:/something", true, true, "/\\//somehost/path", true, true, true, "file://somehost/path"}, +#else + // On Unix we fall back to relative behavior since there's nothing else + // reasonable to do. + {"http://host/a", true, false, "\\\\Another\\path", true, true, true, "http://another/path"}, +#endif + // Even on Windows, we don't allow relative drive specs when the base + // is not file. + {"http://host/a", true, false, "/c:\\foo", true, true, true, "http://host/c:/foo"}, + {"http://host/a", true, false, "//c:\\foo", true, true, true, "http://c/foo"}, + // Ensure that ports aren't allowed for hosts relative to a file url. + // Although the result string shows a host:port portion, the call to + // resolve the relative URL returns false, indicating parse failure, + // which is what is required. + {"file:///foo.txt", true, true, "//host:80/bar.txt", true, true, false, "file://host:80/bar.txt"}, + // Filesystem URL tests; filesystem URLs are only valid and relative if + // they have no scheme, e.g. "./index.html". There's no valid equivalent + // to http:index.html. + {"filesystem:http://host/t/path", true, false, "filesystem:http://host/t/path2", true, false, false, NULL}, + {"filesystem:http://host/t/path", true, false, "filesystem:https://host/t/path2", true, false, false, NULL}, + {"filesystem:http://host/t/path", true, false, "http://host/t/path2", true, false, false, NULL}, + {"http://host/t/path", true, false, "filesystem:http://host/t/path2", true, false, false, NULL}, + {"filesystem:http://host/t/path", true, false, "./path2", true, true, true, "filesystem:http://host/t/path2"}, + {"filesystem:http://host/t/path/", true, false, "path2", true, true, true, "filesystem:http://host/t/path/path2"}, + {"filesystem:http://host/t/path", true, false, "filesystem:http:path2", true, false, false, NULL}, + // Absolute URLs are still not relative to a non-standard base URL. + {"about:blank", false, false, "http://X/A", true, false, true, ""}, + {"about:blank", false, false, "content://content.Provider/", true, false, true, ""}, + }; + + for (size_t i = 0; i < arraysize(rel_cases); i++) { + const RelativeCase& cur_case = rel_cases[i]; + + Parsed parsed; + int base_len = static_cast(strlen(cur_case.base)); + if (cur_case.is_base_file) + ParseFileURL(cur_case.base, base_len, &parsed); + else if (cur_case.is_base_hier) + ParseStandardURL(cur_case.base, base_len, &parsed); + else + ParsePathURL(cur_case.base, base_len, false, &parsed); + + // First see if it is relative. + int test_len = static_cast(strlen(cur_case.test)); + bool is_relative; + Component relative_component; + bool succeed_is_rel = IsRelativeURL( + cur_case.base, parsed, cur_case.test, test_len, cur_case.is_base_hier, + &is_relative, &relative_component); + + EXPECT_EQ(cur_case.succeed_relative, succeed_is_rel) << + "succeed is rel failure on " << cur_case.test; + EXPECT_EQ(cur_case.is_rel, is_relative) << + "is rel failure on " << cur_case.test; + // Now resolve it. + if (succeed_is_rel && is_relative && cur_case.is_rel) { + std::string resolved; + StdStringCanonOutput output(&resolved); + Parsed resolved_parsed; + + bool succeed_resolve = ResolveRelativeURL( + cur_case.base, parsed, cur_case.is_base_file, cur_case.test, + relative_component, NULL, &output, &resolved_parsed); + output.Complete(); + + EXPECT_EQ(cur_case.succeed_resolve, succeed_resolve); + EXPECT_EQ(cur_case.resolved, resolved) << " on " << cur_case.test; + + // Verify that the output parsed structure is the same as parsing a + // the URL freshly. + Parsed ref_parsed; + int resolved_len = static_cast(resolved.size()); + if (cur_case.is_base_file) { + ParseFileURL(resolved.c_str(), resolved_len, &ref_parsed); + } else if (cur_case.is_base_hier) { + ParseStandardURL(resolved.c_str(), resolved_len, &ref_parsed); + } else { + ParsePathURL(resolved.c_str(), resolved_len, false, &ref_parsed); + } + EXPECT_TRUE(ParsedIsEqual(ref_parsed, resolved_parsed)); + } + } +} + +// It used to be the case that when we did a replacement with a long buffer of +// UTF-16 characters, we would get invalid data in the URL. This is because the +// buffer that it used to hold the UTF-8 data was resized, while some pointers +// were still kept to the old buffer that was removed. +TEST(URLCanonTest, ReplacementOverflow) { + const char src[] = "file:///C:/foo/bar"; + int src_len = static_cast(strlen(src)); + Parsed parsed; + ParseFileURL(src, src_len, &parsed); + + // Override two components, the path with something short, and the query with + // something long enough to trigger the bug. + Replacements repl; + base::string16 new_query; + for (int i = 0; i < 4800; i++) + new_query.push_back('a'); + + base::string16 new_path(WStringToUTF16(L"/foo")); + repl.SetPath(new_path.c_str(), Component(0, 4)); + repl.SetQuery(new_query.c_str(), + Component(0, static_cast(new_query.length()))); + + // Call ReplaceComponents on the string. It doesn't matter if we call it for + // standard URLs, file URLs, etc, since they will go to the same replacement + // function that was buggy. + Parsed repl_parsed; + std::string repl_str; + StdStringCanonOutput repl_output(&repl_str); + ReplaceFileURL(src, parsed, repl, NULL, &repl_output, &repl_parsed); + repl_output.Complete(); + + // Generate the expected string and check. + std::string expected("file:///foo?"); + for (size_t i = 0; i < new_query.length(); i++) + expected.push_back('a'); + EXPECT_TRUE(expected == repl_str); +} + +} // namespace url diff --git a/src/url/url_constants.cc b/src/url/url_constants.cc index 2dc1478a..0388fbc2 100644 --- a/src/url/url_constants.cc +++ b/src/url/url_constants.cc @@ -25,4 +25,6 @@ const char kWssScheme[] = "wss"; const char kStandardSchemeSeparator[] = "://"; +const size_t kMaxURLChars = 2 * 1024 * 1024; + } // namespace url diff --git a/src/url/url_constants.h b/src/url/url_constants.h index c48dafcd..fa71164c 100644 --- a/src/url/url_constants.h +++ b/src/url/url_constants.h @@ -5,6 +5,8 @@ #ifndef URL_URL_CONSTANTS_H_ #define URL_URL_CONSTANTS_H_ +#include + #include "url/url_export.h" namespace url { @@ -30,6 +32,8 @@ URL_EXPORT extern const char kWssScheme[]; // Used to separate a standard scheme and the hostname: "://". URL_EXPORT extern const char kStandardSchemeSeparator[]; +URL_EXPORT extern const size_t kMaxURLChars; + } // namespace url #endif // URL_URL_CONSTANTS_H_ diff --git a/src/url/url_parse_file.cc b/src/url/url_parse_file.cc new file mode 100644 index 00000000..fcbb12dc --- /dev/null +++ b/src/url/url_parse_file.cc @@ -0,0 +1,222 @@ +// 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 "base/logging.h" +#include "url/third_party/mozilla/url_parse.h" +#include "url/url_file.h" +#include "url/url_parse_internal.h" + +// Interesting IE file:isms... +// +// INPUT OUTPUT +// ========================= ============================== +// file:/foo/bar file:///foo/bar +// The result here seems totally invalid!?!? This isn't UNC. +// +// file:/ +// file:// or any other number of slashes +// IE6 doesn't do anything at all if you click on this link. No error: +// nothing. IE6's history system seems to always color this link, so I'm +// guessing that it maps internally to the empty URL. +// +// C:\ file:///C:/ +// When on a file: URL source page, this link will work. When over HTTP, +// the file: URL will appear in the status bar but the link will not work +// (security restriction for all file URLs). +// +// file:foo/ file:foo/ (invalid?!?!?) +// file:/foo/ file:///foo/ (invalid?!?!?) +// file://foo/ file://foo/ (UNC to server "foo") +// file:///foo/ file:///foo/ (invalid, seems to be a file) +// file:////foo/ file://foo/ (UNC to server "foo") +// Any more than four slashes is also treated as UNC. +// +// file:C:/ file://C:/ +// file:/C:/ file://C:/ +// The number of slashes after "file:" don't matter if the thing following +// it looks like an absolute drive path. Also, slashes and backslashes are +// equally valid here. + +namespace url { + +namespace { + +// A subcomponent of DoInitFileURL, the input of this function should be a UNC +// path name, with the index of the first character after the slashes following +// the scheme given in |after_slashes|. This will initialize the host, path, +// query, and ref, and leave the other output components untouched +// (DoInitFileURL handles these for us). +template +void DoParseUNC(const CHAR* spec, + int after_slashes, + int spec_len, + Parsed* parsed) { + int next_slash = FindNextSlash(spec, after_slashes, spec_len); + if (next_slash == spec_len) { + // No additional slash found, as in "file://foo", treat the text as the + // host with no path (this will end up being UNC to server "foo"). + int host_len = spec_len - after_slashes; + if (host_len) + parsed->host = Component(after_slashes, host_len); + else + parsed->host.reset(); + parsed->path.reset(); + return; + } + +#ifdef WIN32 + // See if we have something that looks like a path following the first + // component. As in "file://localhost/c:/", we get "c:/" out. We want to + // treat this as a having no host but the path given. Works on Windows only. + if (DoesBeginWindowsDriveSpec(spec, next_slash + 1, spec_len)) { + parsed->host.reset(); + ParsePathInternal(spec, MakeRange(next_slash, spec_len), + &parsed->path, &parsed->query, &parsed->ref); + return; + } +#endif + + // Otherwise, everything up until that first slash we found is the host name, + // which will end up being the UNC host. For example "file://foo/bar.txt" + // will get a server name of "foo" and a path of "/bar". Later, on Windows, + // this should be treated as the filename "\\foo\bar.txt" in proper UNC + // notation. + int host_len = next_slash - after_slashes; + if (host_len) + parsed->host = MakeRange(after_slashes, next_slash); + else + parsed->host.reset(); + if (next_slash < spec_len) { + ParsePathInternal(spec, MakeRange(next_slash, spec_len), + &parsed->path, &parsed->query, &parsed->ref); + } else { + parsed->path.reset(); + } +} + +// A subcomponent of DoParseFileURL, the input should be a local file, with the +// beginning of the path indicated by the index in |path_begin|. This will +// initialize the host, path, query, and ref, and leave the other output +// components untouched (DoInitFileURL handles these for us). +template +void DoParseLocalFile(const CHAR* spec, + int path_begin, + int spec_len, + Parsed* parsed) { + parsed->host.reset(); + ParsePathInternal(spec, MakeRange(path_begin, spec_len), + &parsed->path, &parsed->query, &parsed->ref); +} + +// Backend for the external functions that operates on either char type. +// Handles cases where there is a scheme, but also when handed the first +// character following the "file:" at the beginning of the spec. If so, +// this is usually a slash, but needn't be; we allow paths like "file:c:\foo". +template +void DoParseFileURL(const CHAR* spec, int spec_len, Parsed* parsed) { + DCHECK(spec_len >= 0); + + // Get the parts we never use for file URLs out of the way. + parsed->username.reset(); + parsed->password.reset(); + parsed->port.reset(); + + // Many of the code paths don't set these, so it's convenient to just clear + // them. We'll write them in those cases we need them. + parsed->query.reset(); + parsed->ref.reset(); + + // Strip leading & trailing spaces and control characters. + int begin = 0; + TrimURL(spec, &begin, &spec_len); + + // Find the scheme, if any. + int num_slashes = CountConsecutiveSlashes(spec, begin, spec_len); + int after_scheme; + int after_slashes; +#ifdef WIN32 + // See how many slashes there are. We want to handle cases like UNC but also + // "/c:/foo". This is when there is no scheme, so we can allow pages to do + // links like "c:/foo/bar" or "//foo/bar". This is also called by the + // relative URL resolver when it determines there is an absolute URL, which + // may give us input like "/c:/foo". + after_slashes = begin + num_slashes; + if (DoesBeginWindowsDriveSpec(spec, after_slashes, spec_len)) { + // Windows path, don't try to extract the scheme (for example, "c:\foo"). + parsed->scheme.reset(); + after_scheme = after_slashes; + } else if (DoesBeginUNCPath(spec, begin, spec_len, false)) { + // Windows UNC path: don't try to extract the scheme, but keep the slashes. + parsed->scheme.reset(); + after_scheme = begin; + } else +#endif + { + // ExtractScheme doesn't understand the possibility of filenames with + // colons in them, in which case it returns the entire spec up to the + // colon as the scheme. So handle /foo.c:5 as a file but foo.c:5 as + // the foo.c: scheme. + if (!num_slashes && + ExtractScheme(&spec[begin], spec_len - begin, &parsed->scheme)) { + // Offset the results since we gave ExtractScheme a substring. + parsed->scheme.begin += begin; + after_scheme = parsed->scheme.end() + 1; + } else { + // No scheme found, remember that. + parsed->scheme.reset(); + after_scheme = begin; + } + } + + // Handle empty specs ones that contain only whitespace or control chars, + // or that are just the scheme (for example "file:"). + if (after_scheme == spec_len) { + parsed->host.reset(); + parsed->path.reset(); + return; + } + + num_slashes = CountConsecutiveSlashes(spec, after_scheme, spec_len); + after_slashes = after_scheme + num_slashes; +#ifdef WIN32 + // Check whether the input is a drive again. We checked above for windows + // drive specs, but that's only at the very beginning to see if we have a + // scheme at all. This test will be duplicated in that case, but will + // additionally handle all cases with a real scheme such as "file:///C:/". + if (!DoesBeginWindowsDriveSpec(spec, after_slashes, spec_len) && + num_slashes != 3) { + // Anything not beginning with a drive spec ("c:\") on Windows is treated + // as UNC, with the exception of three slashes which always means a file. + // Even IE7 treats file:///foo/bar as "/foo/bar", which then fails. + DoParseUNC(spec, after_slashes, spec_len, parsed); + return; + } +#else + // file: URL with exactly 2 slashes is considered to have a host component. + if (num_slashes == 2) { + DoParseUNC(spec, after_slashes, spec_len, parsed); + return; + } +#endif // WIN32 + + // Easy and common case, the full path immediately follows the scheme + // (modulo slashes), as in "file://c:/foo". Just treat everything from + // there to the end as the path. Empty hosts have 0 length instead of -1. + // We include the last slash as part of the path if there is one. + DoParseLocalFile(spec, + num_slashes > 0 ? after_scheme + num_slashes - 1 : after_scheme, + spec_len, parsed); +} + +} // namespace + +void ParseFileURL(const char* url, int url_len, Parsed* parsed) { + DoParseFileURL(url, url_len, parsed); +} + +void ParseFileURL(const base::char16* url, int url_len, Parsed* parsed) { + DoParseFileURL(url, url_len, parsed); +} + +} // namespace url diff --git a/src/url/url_util.cc b/src/url/url_util.cc index cbb252f4..c1e99799 100644 --- a/src/url/url_util.cc +++ b/src/url/url_util.cc @@ -34,13 +34,21 @@ const SchemeWithType kStandardURLSchemes[kNumStandardURLSchemes] = { {kFileSystemScheme, SCHEME_WITHOUT_AUTHORITY}, }; -// List of the currently installed standard schemes. This list is lazily -// initialized by InitStandardSchemes and is leaked on shutdown to prevent -// any destructors from being called that will slow us down or cause problems. -std::vector* standard_schemes = NULL; +const int kNumReferrerURLSchemes = 2; +const SchemeWithType kReferrerURLSchemes[kNumReferrerURLSchemes] = { + {kHttpScheme, SCHEME_WITH_PORT}, + {kHttpsScheme, SCHEME_WITH_PORT}, +}; + +// Lists of the currently installed standard and referrer schemes. These lists +// are lazily initialized by InitStandardSchemes and InitReferrerSchemes and are +// leaked on shutdown to prevent any destructors from being called that will +// slow us down or cause problems. +std::vector* standard_schemes = nullptr; +std::vector* referrer_schemes = nullptr; -// See the LockStandardSchemes declaration in the header. -bool standard_schemes_locked = false; +// See the LockSchemeRegistries declaration in the header. +bool scheme_registries_locked = false; // This template converts a given character type to the corresponding // StringPiece type. @@ -53,14 +61,27 @@ template<> struct CharToStringPiece { typedef base::StringPiece16 Piece; }; -// Ensures that the standard_schemes list is initialized, does nothing if it -// already has values. -void InitStandardSchemes() { - if (standard_schemes) +void InitSchemes(std::vector** schemes, + const SchemeWithType* initial_schemes, + size_t size) { + if (*schemes) return; - standard_schemes = new std::vector; - for (int i = 0; i < kNumStandardURLSchemes; i++) - standard_schemes->push_back(kStandardURLSchemes[i]); + *schemes = new std::vector(size); + for (size_t i = 0; i < size; i++) { + (*schemes)->push_back(initial_schemes[i]); + } +} + +// Ensures that the standard_schemes list is initialized, does nothing if +// it already has values. +void InitStandardSchemes() { + InitSchemes(&standard_schemes, kStandardURLSchemes, kNumStandardURLSchemes); +} + +// Ensures that the referrer_schemes list is initialized, does nothing if +// it already has values. +void InitReferrerSchemes() { + InitSchemes(&referrer_schemes, kReferrerURLSchemes, kNumReferrerURLSchemes); } // Given a string and a range inside the string, compares it to the given @@ -78,28 +99,33 @@ inline bool DoCompareSchemeComponent(const CHAR* spec, } // Returns true and sets |type| to the SchemeType of the given scheme -// identified by |scheme| within |spec| if the scheme is one of the registered -// "standard" schemes. +// identified by |scheme| within |spec| if in |schemes|. template -bool DoIsStandard(const CHAR* spec, - const Component& scheme, - SchemeType* type) { +bool DoIsInSchemes(const CHAR* spec, + const Component& scheme, + SchemeType* type, + const std::vector& schemes) { if (!scheme.is_nonempty()) return false; // Empty or invalid schemes are non-standard. - InitStandardSchemes(); - for (size_t i = 0; i < standard_schemes->size(); i++) { - if (base::LowerCaseEqualsASCII( - typename CharToStringPiece::Piece( - &spec[scheme.begin], scheme.len), - standard_schemes->at(i).scheme)) { - *type = standard_schemes->at(i).type; + for (const SchemeWithType& scheme_with_type : schemes) { + if (base::LowerCaseEqualsASCII(typename CharToStringPiece::Piece( + &spec[scheme.begin], scheme.len), + scheme_with_type.scheme)) { + *type = scheme_with_type.type; return true; } } return false; } +template +bool DoIsStandard(const CHAR* spec, const Component& scheme, SchemeType* type) { + InitStandardSchemes(); + return DoIsInSchemes(spec, scheme, type, *standard_schemes); +} + + template bool DoFindAndCompareScheme(const CHAR* str, int str_len, @@ -364,31 +390,19 @@ bool DoReplaceComponents(const char* spec, return ReplacePathURL(spec, parsed, replacements, output, out_parsed); } -} // namespace - -void Initialize() { - InitStandardSchemes(); -} - -void Shutdown() { - if (standard_schemes) { - delete standard_schemes; - standard_schemes = NULL; - } -} - -void AddStandardScheme(const char* new_scheme, - SchemeType type) { - // If this assert triggers, it means you've called AddStandardScheme after - // LockStandardSchemes have been called (see the header file for - // LockStandardSchemes for more). +void DoAddScheme(const char* new_scheme, + SchemeType type, + std::vector* schemes) { + DCHECK(schemes); + // If this assert triggers, it means you've called Add*Scheme after + // LockSchemeRegistries has been called (see the header file for + // LockSchemeRegistries for more). // - // This normally means you're trying to set up a new standard scheme too late - // in your application's init process. Locate where your app does this - // initialization and calls LockStandardSchemes, and add your new standard - // scheme there. - DCHECK(!standard_schemes_locked) << - "Trying to add a standard scheme after the list has been locked."; + // This normally means you're trying to set up a new scheme too late in your + // application's init process. Locate where your app does this initialization + // and calls LockSchemeRegistries, and add your new scheme there. + DCHECK(!scheme_registries_locked) + << "Trying to add a scheme after the lists have been locked."; size_t scheme_len = strlen(new_scheme); if (scheme_len == 0) @@ -400,15 +414,42 @@ void AddStandardScheme(const char* new_scheme, ANNOTATE_LEAKING_OBJECT_PTR(dup_scheme); memcpy(dup_scheme, new_scheme, scheme_len + 1); - InitStandardSchemes(); SchemeWithType scheme_with_type; scheme_with_type.scheme = dup_scheme; scheme_with_type.type = type; - standard_schemes->push_back(scheme_with_type); + schemes->push_back(scheme_with_type); +} + +} // namespace + +void Initialize() { + InitStandardSchemes(); + InitReferrerSchemes(); +} + +void Shutdown() { + if (standard_schemes) { + delete standard_schemes; + standard_schemes = NULL; + } + if (referrer_schemes) { + delete referrer_schemes; + referrer_schemes = NULL; + } } -void LockStandardSchemes() { - standard_schemes_locked = true; +void AddStandardScheme(const char* new_scheme, SchemeType type) { + InitStandardSchemes(); + DoAddScheme(new_scheme, type, standard_schemes); +} + +void AddReferrerScheme(const char* new_scheme, SchemeType type) { + InitReferrerSchemes(); + DoAddScheme(new_scheme, type, referrer_schemes); +} + +void LockSchemeRegistries() { + scheme_registries_locked = true; } bool IsStandard(const char* spec, const Component& scheme) { @@ -427,6 +468,12 @@ bool IsStandard(const base::char16* spec, const Component& scheme) { return DoIsStandard(spec, scheme, &unused_scheme_type); } +bool IsReferrerScheme(const char* spec, const Component& scheme) { + InitReferrerSchemes(); + SchemeType unused_scheme_type; + return DoIsInSchemes(spec, scheme, &unused_scheme_type, *referrer_schemes); +} + bool FindAndCompareScheme(const char* str, int str_len, const char* compare, diff --git a/src/url/url_util.h b/src/url/url_util.h index 04a6ef43..a209a61c 100644 --- a/src/url/url_util.h +++ b/src/url/url_util.h @@ -20,7 +20,7 @@ namespace url { // Initialization is NOT required, it will be implicitly initialized when first // used. However, this implicit initialization is NOT threadsafe. If you are // using this library in a threaded environment and don't have a consistent -// "first call" (an example might be calling AddStandardScheme with your special +// "first call" (an example might be calling Add*Scheme with your special // application-specific schemes) then you will want to call initialize before // spawning any threads. // @@ -61,24 +61,33 @@ struct URL_EXPORT SchemeWithType { // URI syntax" (https://tools.ietf.org/html/rfc3986#section-3). // // This function is not threadsafe and can not be called concurrently with any -// other url_util function. It will assert if the list of standard schemes has -// been locked (see LockStandardSchemes). +// other url_util function. It will assert if the lists of schemes have +// been locked (see LockSchemeRegistries). URL_EXPORT void AddStandardScheme(const char* new_scheme, SchemeType scheme_type); -// Sets a flag to prevent future calls to AddStandardScheme from succeeding. +// Adds an application-defined scheme to the internal list of schemes allowed +// for referrers. +// +// This function is not threadsafe and can not be called concurrently with any +// other url_util function. It will assert if the lists of schemes have +// been locked (see LockSchemeRegistries). +URL_EXPORT void AddReferrerScheme(const char* new_scheme, + SchemeType scheme_type); + +// Sets a flag to prevent future calls to Add*Scheme from succeeding. // // This is designed to help prevent errors for multithreaded applications. -// Normal usage would be to call AddStandardScheme for your custom schemes at -// the beginning of program initialization, and then LockStandardSchemes. This -// prevents future callers from mistakenly calling AddStandardScheme when the +// Normal usage would be to call Add*Scheme for your custom schemes at +// the beginning of program initialization, and then LockSchemeRegistries. This +// prevents future callers from mistakenly calling Add*Scheme when the // program is running with multiple threads, where such usage would be // dangerous. // -// We could have had AddStandardScheme use a lock instead, but that would add +// We could have had Add*Scheme use a lock instead, but that would add // some platform-specific dependencies we don't otherwise have now, and is // overkill considering the normal usage is so simple. -URL_EXPORT void LockStandardSchemes(); +URL_EXPORT void LockSchemeRegistries(); // Locates the scheme in the given string and places it into |found_scheme|, // which may be NULL to indicate the caller does not care about the range. @@ -112,6 +121,10 @@ inline bool FindAndCompareScheme(const base::string16& str, URL_EXPORT bool IsStandard(const char* spec, const Component& scheme); URL_EXPORT bool IsStandard(const base::char16* spec, const Component& scheme); +// Returns true if the given scheme identified by |scheme| within |spec| is in +// the list of allowed schemes for referrers (see AddReferrerScheme). +URL_EXPORT bool IsReferrerScheme(const char* spec, const Component& scheme); + // Returns true and sets |type| to the SchemeType of the given scheme // identified by |scheme| within |spec| if the scheme is in the list of known // standard-format schemes (see AddStandardScheme).