summaryrefslogtreecommitdiffstats
path: root/chromium/net/quic
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/net/quic
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/net/quic')
-rw-r--r--chromium/net/quic/congestion_control/available_channel_estimator.cc78
-rw-r--r--chromium/net/quic/congestion_control/available_channel_estimator.h64
-rw-r--r--chromium/net/quic/congestion_control/available_channel_estimator_test.cc109
-rw-r--r--chromium/net/quic/congestion_control/channel_estimator.cc110
-rw-r--r--chromium/net/quic/congestion_control/channel_estimator.h61
-rw-r--r--chromium/net/quic/congestion_control/channel_estimator_test.cc224
-rw-r--r--chromium/net/quic/congestion_control/cube_root.h3
-rw-r--r--chromium/net/quic/congestion_control/cubic.cc163
-rw-r--r--chromium/net/quic/congestion_control/cubic.h15
-rw-r--r--chromium/net/quic/congestion_control/cubic_test.cc162
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_receiver.cc3
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_receiver.h3
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_sender.cc86
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_sender.h36
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_test.cc96
-rw-r--r--chromium/net/quic/congestion_control/hybrid_slow_start.cc133
-rw-r--r--chromium/net/quic/congestion_control/hybrid_slow_start.h61
-rw-r--r--chromium/net/quic/congestion_control/hybrid_slow_start_test.cc59
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc176
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h65
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc404
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc256
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h173
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc1114
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_probe.cc122
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_probe.h61
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_probe_test.cc84
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_receiver.cc48
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_receiver.h46
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc55
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_sender.cc529
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_sender.h102
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_sender_test.cc569
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_state_machine.cc163
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_state_machine.h94
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc126
-rw-r--r--chromium/net/quic/congestion_control/leaky_bucket.cc10
-rw-r--r--chromium/net/quic/congestion_control/leaky_bucket.h3
-rw-r--r--chromium/net/quic/congestion_control/loss_detection_interface.cc25
-rw-r--r--chromium/net/quic/congestion_control/loss_detection_interface.h42
-rw-r--r--chromium/net/quic/congestion_control/paced_sender.cc57
-rw-r--r--chromium/net/quic/congestion_control/paced_sender.h44
-rw-r--r--chromium/net/quic/congestion_control/paced_sender_test.cc78
-rw-r--r--chromium/net/quic/congestion_control/pacing_sender.cc108
-rw-r--r--chromium/net/quic/congestion_control/pacing_sender.h34
-rw-r--r--chromium/net/quic/congestion_control/pacing_sender_test.cc143
-rw-r--r--chromium/net/quic/congestion_control/quic_max_sized_map.h77
-rw-r--r--chromium/net/quic/congestion_control/quic_max_sized_map_test.cc66
-rw-r--r--chromium/net/quic/congestion_control/receive_algorithm_interface.cc6
-rw-r--r--chromium/net/quic/congestion_control/receive_algorithm_interface.h5
-rw-r--r--chromium/net/quic/congestion_control/rtt_stats.cc129
-rw-r--r--chromium/net/quic/congestion_control/rtt_stats.h112
-rw-r--r--chromium/net/quic/congestion_control/rtt_stats_test.cc160
-rw-r--r--chromium/net/quic/congestion_control/send_algorithm_interface.cc17
-rw-r--r--chromium/net/quic/congestion_control/send_algorithm_interface.h93
-rw-r--r--chromium/net/quic/congestion_control/send_algorithm_simulator.cc273
-rw-r--r--chromium/net/quic/congestion_control/send_algorithm_simulator.h133
-rw-r--r--chromium/net/quic/congestion_control/tcp_cubic_sender.cc343
-rw-r--r--chromium/net/quic/congestion_control/tcp_cubic_sender.h92
-rw-r--r--chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc527
-rw-r--r--chromium/net/quic/congestion_control/tcp_loss_algorithm.cc79
-rw-r--r--chromium/net/quic/congestion_control/tcp_loss_algorithm.h46
-rw-r--r--chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc183
-rw-r--r--chromium/net/quic/congestion_control/tcp_receiver.cc13
-rw-r--r--chromium/net/quic/congestion_control/tcp_receiver.h5
-rw-r--r--chromium/net/quic/congestion_control/tcp_receiver_test.cc6
-rw-r--r--chromium/net/quic/congestion_control/time_loss_algorithm.cc69
-rw-r--r--chromium/net/quic/congestion_control/time_loss_algorithm.h52
-rw-r--r--chromium/net/quic/congestion_control/time_loss_algorithm_test.cc138
-rw-r--r--chromium/net/quic/crypto/aead_base_decrypter.h107
-rw-r--r--chromium/net/quic/crypto/aead_base_decrypter_nss.cc154
-rw-r--r--chromium/net/quic/crypto/aead_base_decrypter_openssl.cc143
-rw-r--r--chromium/net/quic/crypto/aead_base_encrypter.h110
-rw-r--r--chromium/net/quic/crypto/aead_base_encrypter_nss.cc162
-rw-r--r--chromium/net/quic/crypto/aead_base_encrypter_openssl.cc148
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h50
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc185
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc141
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc70
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h50
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc188
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc164
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc60
-rw-r--r--chromium/net/quic/crypto/cert_compressor.h3
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_decrypter.h47
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc80
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc31
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc132
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_encrypter.h47
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc80
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc31
-rw-r--r--chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc108
-rw-r--r--chromium/net/quic/crypto/channel_id.h66
-rw-r--r--chromium/net/quic/crypto/channel_id_test.cc14
-rw-r--r--chromium/net/quic/crypto/crypto_framer.cc18
-rw-r--r--chromium/net/quic/crypto/crypto_framer.h4
-rw-r--r--chromium/net/quic/crypto/crypto_handshake.cc310
-rw-r--r--chromium/net/quic/crypto/crypto_handshake.h132
-rw-r--r--chromium/net/quic/crypto/crypto_handshake_message.cc324
-rw-r--r--chromium/net/quic/crypto/crypto_handshake_message.h135
-rw-r--r--chromium/net/quic/crypto/crypto_protocol.h131
-rw-r--r--chromium/net/quic/crypto/crypto_secret_boxer.cc78
-rw-r--r--chromium/net/quic/crypto/crypto_secret_boxer.h4
-rw-r--r--chromium/net/quic/crypto/crypto_server_config_protobuf.cc3
-rw-r--r--chromium/net/quic/crypto/crypto_server_config_protobuf.h40
-rw-r--r--chromium/net/quic/crypto/crypto_server_test.cc323
-rw-r--r--chromium/net/quic/crypto/crypto_utils.cc4
-rw-r--r--chromium/net/quic/crypto/crypto_utils.h3
-rw-r--r--chromium/net/quic/crypto/curve25519_key_exchange.cc1
-rw-r--r--chromium/net/quic/crypto/local_strike_register_client.cc8
-rw-r--r--chromium/net/quic/crypto/local_strike_register_client.h5
-rw-r--r--chromium/net/quic/crypto/local_strike_register_client_test.cc15
-rw-r--r--chromium/net/quic/crypto/null_decrypter.h2
-rw-r--r--chromium/net/quic/crypto/null_encrypter.h2
-rw-r--r--chromium/net/quic/crypto/p256_key_exchange.h2
-rw-r--r--chromium/net/quic/crypto/proof_test.cc169
-rw-r--r--chromium/net/quic/crypto/proof_verifier.cc15
-rw-r--r--chromium/net/quic/crypto/proof_verifier.h64
-rw-r--r--chromium/net/quic/crypto/proof_verifier_chromium.cc167
-rw-r--r--chromium/net/quic/crypto/proof_verifier_chromium.h71
-rw-r--r--chromium/net/quic/crypto/quic_crypto_client_config.cc268
-rw-r--r--chromium/net/quic/crypto/quic_crypto_client_config.h126
-rw-r--r--chromium/net/quic/crypto/quic_crypto_client_config_test.cc210
-rw-r--r--chromium/net/quic/crypto/quic_crypto_server_config.cc531
-rw-r--r--chromium/net/quic/crypto/quic_crypto_server_config.h124
-rw-r--r--chromium/net/quic/crypto/quic_crypto_server_config_test.cc419
-rw-r--r--chromium/net/quic/crypto/quic_decrypter.cc4
-rw-r--r--chromium/net/quic/crypto/quic_decrypter.h1
-rw-r--r--chromium/net/quic/crypto/quic_encrypter.cc4
-rw-r--r--chromium/net/quic/crypto/quic_encrypter.h1
-rw-r--r--chromium/net/quic/crypto/quic_random.cc5
-rw-r--r--chromium/net/quic/crypto/quic_server_info.cc141
-rw-r--r--chromium/net/quic/crypto/quic_server_info.h118
-rw-r--r--chromium/net/quic/crypto/scoped_evp_aead_ctx.cc23
-rw-r--r--chromium/net/quic/crypto/scoped_evp_aead_ctx.h31
-rw-r--r--chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc22
-rw-r--r--chromium/net/quic/crypto/scoped_evp_cipher_ctx.h30
-rw-r--r--chromium/net/quic/crypto/source_address_token.h2
-rw-r--r--chromium/net/quic/crypto/strike_register.cc12
-rw-r--r--chromium/net/quic/crypto/strike_register.h5
-rw-r--r--chromium/net/quic/crypto/strike_register_client.h5
-rw-r--r--chromium/net/quic/iovector.h1
-rw-r--r--chromium/net/quic/quic_ack_notifier.cc62
-rw-r--r--chromium/net/quic/quic_ack_notifier.h58
-rw-r--r--chromium/net/quic/quic_ack_notifier_manager.cc8
-rw-r--r--chromium/net/quic/quic_ack_notifier_manager.h6
-rw-r--r--chromium/net/quic/quic_ack_notifier_test.cc54
-rw-r--r--chromium/net/quic/quic_address_mismatch.cc52
-rw-r--r--chromium/net/quic/quic_address_mismatch.h44
-rw-r--r--chromium/net/quic/quic_address_mismatch_test.cc113
-rw-r--r--chromium/net/quic/quic_bandwidth.h1
-rw-r--r--chromium/net/quic/quic_blocked_writer_interface.h5
-rw-r--r--chromium/net/quic/quic_client_session.cc423
-rw-r--r--chromium/net/quic/quic_client_session.h48
-rw-r--r--chromium/net/quic/quic_client_session_base.cc16
-rw-r--r--chromium/net/quic/quic_client_session_base.h41
-rw-r--r--chromium/net/quic/quic_client_session_test.cc40
-rw-r--r--chromium/net/quic/quic_clock.cc3
-rw-r--r--chromium/net/quic/quic_clock.h3
-rw-r--r--chromium/net/quic/quic_config.cc632
-rw-r--r--chromium/net/quic/quic_config.h311
-rw-r--r--chromium/net/quic/quic_config_test.cc158
-rw-r--r--chromium/net/quic/quic_connection.cc1231
-rw-r--r--chromium/net/quic/quic_connection.h475
-rw-r--r--chromium/net/quic/quic_connection_helper.h2
-rw-r--r--chromium/net/quic/quic_connection_logger.cc448
-rw-r--r--chromium/net/quic/quic_connection_logger.h87
-rw-r--r--chromium/net/quic/quic_connection_stats.cc45
-rw-r--r--chromium/net/quic/quic_connection_stats.h67
-rw-r--r--chromium/net/quic/quic_connection_test.cc2592
-rw-r--r--chromium/net/quic/quic_crypto_client_stream.cc174
-rw-r--r--chromium/net/quic/quic_crypto_client_stream.h32
-rw-r--r--chromium/net/quic/quic_crypto_client_stream_factory.h7
-rw-r--r--chromium/net/quic/quic_crypto_client_stream_test.cc27
-rw-r--r--chromium/net/quic/quic_crypto_server_stream.cc23
-rw-r--r--chromium/net/quic/quic_crypto_server_stream.h14
-rw-r--r--chromium/net/quic/quic_crypto_server_stream_test.cc20
-rw-r--r--chromium/net/quic/quic_crypto_stream.cc8
-rw-r--r--chromium/net/quic/quic_crypto_stream.h5
-rw-r--r--chromium/net/quic/quic_crypto_stream_test.cc6
-rw-r--r--chromium/net/quic/quic_data_reader.h2
-rw-r--r--chromium/net/quic/quic_data_stream.cc226
-rw-r--r--chromium/net/quic/quic_data_stream.h51
-rw-r--r--chromium/net/quic/quic_data_stream_test.cc615
-rw-r--r--chromium/net/quic/quic_data_writer.cc5
-rw-r--r--chromium/net/quic/quic_data_writer.h2
-rw-r--r--chromium/net/quic/quic_data_writer_test.cc10
-rw-r--r--chromium/net/quic/quic_default_packet_writer.cc17
-rw-r--r--chromium/net/quic/quic_default_packet_writer.h22
-rw-r--r--chromium/net/quic/quic_dispatcher.cc426
-rw-r--r--chromium/net/quic/quic_dispatcher.h230
-rw-r--r--chromium/net/quic/quic_end_to_end_unittest.cc29
-rw-r--r--chromium/net/quic/quic_fec_group.cc30
-rw-r--r--chromium/net/quic/quic_fec_group.h37
-rw-r--r--chromium/net/quic/quic_fec_group_test.cc59
-rw-r--r--chromium/net/quic/quic_flags.cc48
-rw-r--r--chromium/net/quic/quic_flags.h20
-rw-r--r--chromium/net/quic/quic_flow_controller.cc203
-rw-r--r--chromium/net/quic/quic_flow_controller.h130
-rw-r--r--chromium/net/quic/quic_flow_controller_test.cc224
-rw-r--r--chromium/net/quic/quic_framer.cc800
-rw-r--r--chromium/net/quic/quic_framer.h166
-rw-r--r--chromium/net/quic/quic_framer_test.cc2172
-rw-r--r--chromium/net/quic/quic_headers_stream.cc266
-rw-r--r--chromium/net/quic/quic_headers_stream.h82
-rw-r--r--chromium/net/quic/quic_headers_stream_test.cc334
-rw-r--r--chromium/net/quic/quic_http_stream.cc58
-rw-r--r--chromium/net/quic/quic_http_stream.h8
-rw-r--r--chromium/net/quic/quic_http_stream_test.cc436
-rw-r--r--chromium/net/quic/quic_http_utils.cc12
-rw-r--r--chromium/net/quic/quic_http_utils.h10
-rw-r--r--chromium/net/quic/quic_in_memory_cache.cc242
-rw-r--r--chromium/net/quic/quic_in_memory_cache.h116
-rw-r--r--chromium/net/quic/quic_network_transaction_unittest.cc691
-rw-r--r--chromium/net/quic/quic_packet_creator.cc362
-rw-r--r--chromium/net/quic/quic_packet_creator.h145
-rw-r--r--chromium/net/quic/quic_packet_creator_test.cc572
-rw-r--r--chromium/net/quic/quic_packet_generator.cc204
-rw-r--r--chromium/net/quic/quic_packet_generator.h93
-rw-r--r--chromium/net/quic/quic_packet_generator_test.cc379
-rw-r--r--chromium/net/quic/quic_packet_writer.h13
-rw-r--r--chromium/net/quic/quic_protocol.cc471
-rw-r--r--chromium/net/quic/quic_protocol.h424
-rw-r--r--chromium/net/quic/quic_protocol_test.cc31
-rw-r--r--chromium/net/quic/quic_received_packet_manager.cc249
-rw-r--r--chromium/net/quic/quic_received_packet_manager.h125
-rw-r--r--chromium/net/quic/quic_received_packet_manager_test.cc238
-rw-r--r--chromium/net/quic/quic_reliable_client_stream.cc10
-rw-r--r--chromium/net/quic/quic_reliable_client_stream_test.cc67
-rw-r--r--chromium/net/quic/quic_sent_entropy_manager.h2
-rw-r--r--chromium/net/quic/quic_sent_packet_manager.cc1044
-rw-r--r--chromium/net/quic/quic_sent_packet_manager.h245
-rw-r--r--chromium/net/quic/quic_sent_packet_manager_test.cc1473
-rw-r--r--chromium/net/quic/quic_server_id.cc58
-rw-r--r--chromium/net/quic/quic_server_id.h60
-rw-r--r--chromium/net/quic/quic_server_id_test.cc319
-rw-r--r--chromium/net/quic/quic_server_packet_writer.cc74
-rw-r--r--chromium/net/quic/quic_server_packet_writer.h53
-rw-r--r--chromium/net/quic/quic_server_session.cc81
-rw-r--r--chromium/net/quic/quic_server_session.h87
-rw-r--r--chromium/net/quic/quic_session.cc553
-rw-r--r--chromium/net/quic/quic_session.h146
-rw-r--r--chromium/net/quic/quic_session_test.cc791
-rw-r--r--chromium/net/quic/quic_socket_address_coder.cc89
-rw-r--r--chromium/net/quic/quic_socket_address_coder.h45
-rw-r--r--chromium/net/quic/quic_socket_address_coder_test.cc117
-rw-r--r--chromium/net/quic/quic_spdy_compressor.cc71
-rw-r--r--chromium/net/quic/quic_spdy_compressor.h49
-rw-r--r--chromium/net/quic/quic_spdy_compressor_test.cc46
-rw-r--r--chromium/net/quic/quic_spdy_decompressor.cc140
-rw-r--r--chromium/net/quic/quic_spdy_decompressor.h65
-rw-r--r--chromium/net/quic/quic_spdy_decompressor_test.cc103
-rw-r--r--chromium/net/quic/quic_spdy_server_stream.cc143
-rw-r--r--chromium/net/quic/quic_spdy_server_stream.h64
-rw-r--r--chromium/net/quic/quic_stream_factory.cc744
-rw-r--r--chromium/net/quic/quic_stream_factory.h148
-rw-r--r--chromium/net/quic/quic_stream_factory_test.cc893
-rw-r--r--chromium/net/quic/quic_stream_sequencer.cc205
-rw-r--r--chromium/net/quic/quic_stream_sequencer.h77
-rw-r--r--chromium/net/quic/quic_stream_sequencer_test.cc486
-rw-r--r--chromium/net/quic/quic_time.cc19
-rw-r--r--chromium/net/quic/quic_time.h9
-rw-r--r--chromium/net/quic/quic_time_test.cc24
-rw-r--r--chromium/net/quic/quic_time_wait_list_manager.cc283
-rw-r--r--chromium/net/quic/quic_time_wait_list_manager.h174
-rw-r--r--chromium/net/quic/quic_types.cc34
-rw-r--r--chromium/net/quic/quic_types.h69
-rw-r--r--chromium/net/quic/quic_unacked_packet_map.cc323
-rw-r--r--chromium/net/quic/quic_unacked_packet_map.h152
-rw-r--r--chromium/net/quic/quic_unacked_packet_map_test.cc166
-rw-r--r--chromium/net/quic/quic_utils.cc31
-rw-r--r--chromium/net/quic/quic_utils.h10
-rw-r--r--chromium/net/quic/quic_utils_chromium.h80
-rw-r--r--chromium/net/quic/quic_utils_chromium_test.cc50
-rw-r--r--chromium/net/quic/quic_write_blocked_list.cc20
-rw-r--r--chromium/net/quic/quic_write_blocked_list.h113
-rw-r--r--chromium/net/quic/quic_write_blocked_list_test.cc117
-rw-r--r--chromium/net/quic/reliable_quic_stream.cc380
-rw-r--r--chromium/net/quic/reliable_quic_stream.h108
-rw-r--r--chromium/net/quic/reliable_quic_stream_test.cc569
-rw-r--r--chromium/net/quic/spdy_utils.cc2
-rw-r--r--chromium/net/quic/spdy_utils.h3
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils.cc33
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils.h21
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils_chromium.cc9
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils_nss.cc91
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils_openssl.cc83
-rw-r--r--chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc2
-rw-r--r--chromium/net/quic/test_tools/delayed_verify_strike_register_client.h4
-rw-r--r--chromium/net/quic/test_tools/mock_clock.h3
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream.cc35
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream.h15
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc14
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h15
-rw-r--r--chromium/net/quic/test_tools/mock_quic_dispatcher.cc23
-rw-r--r--chromium/net/quic/test_tools/mock_quic_dispatcher.h37
-rw-r--r--chromium/net/quic/test_tools/mock_random.h4
-rw-r--r--chromium/net/quic/test_tools/quic_config_peer.cc45
-rw-r--r--chromium/net/quic/test_tools/quic_config_peer.h42
-rw-r--r--chromium/net/quic/test_tools/quic_connection_peer.cc66
-rw-r--r--chromium/net/quic/test_tools/quic_connection_peer.h28
-rw-r--r--chromium/net/quic/test_tools/quic_data_stream_peer.h1
-rw-r--r--chromium/net/quic/test_tools/quic_flow_controller_peer.cc61
-rw-r--r--chromium/net/quic/test_tools/quic_flow_controller_peer.h42
-rw-r--r--chromium/net/quic/test_tools/quic_framer_peer.cc26
-rw-r--r--chromium/net/quic/test_tools/quic_framer_peer.h7
-rw-r--r--chromium/net/quic/test_tools/quic_packet_creator_peer.cc6
-rw-r--r--chromium/net/quic/test_tools/quic_packet_creator_peer.h2
-rw-r--r--chromium/net/quic/test_tools/quic_packet_generator_peer.cc20
-rw-r--r--chromium/net/quic/test_tools/quic_packet_generator_peer.h29
-rw-r--r--chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc6
-rw-r--r--chromium/net/quic/test_tools/quic_received_packet_manager_peer.h2
-rw-r--r--chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc90
-rw-r--r--chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h31
-rw-r--r--chromium/net/quic/test_tools/quic_session_peer.cc20
-rw-r--r--chromium/net/quic/test_tools/quic_session_peer.h13
-rw-r--r--chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc28
-rw-r--r--chromium/net/quic/test_tools/quic_stream_sequencer_peer.h31
-rw-r--r--chromium/net/quic/test_tools/quic_test_packet_maker.cc249
-rw-r--r--chromium/net/quic/test_tools/quic_test_packet_maker.h91
-rw-r--r--chromium/net/quic/test_tools/quic_test_utils.cc326
-rw-r--r--chromium/net/quic/test_tools/quic_test_utils.h346
-rw-r--r--chromium/net/quic/test_tools/quic_test_writer.cc23
-rw-r--r--chromium/net/quic/test_tools/quic_test_writer.h33
-rw-r--r--chromium/net/quic/test_tools/reliable_quic_stream_peer.cc35
-rw-r--r--chromium/net/quic/test_tools/reliable_quic_stream_peer.h8
-rw-r--r--chromium/net/quic/test_tools/simple_quic_framer.cc95
-rw-r--r--chromium/net/quic/test_tools/simple_quic_framer.h12
328 files changed, 30539 insertions, 16679 deletions
diff --git a/chromium/net/quic/congestion_control/available_channel_estimator.cc b/chromium/net/quic/congestion_control/available_channel_estimator.cc
deleted file mode 100644
index ef091abce43..00000000000
--- a/chromium/net/quic/congestion_control/available_channel_estimator.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/available_channel_estimator.h"
-
-static const int kNumberOfSamples = 9;
-
-namespace net {
-
-AvailableChannelEstimator::AvailableChannelEstimator(
- QuicPacketSequenceNumber sequence_number,
- QuicTime first_send_time,
- QuicTime first_receive_time)
- : first_sequence_number_(sequence_number),
- first_send_time_(first_send_time),
- first_receive_time_(first_receive_time),
- last_incorporated_sequence_number_(sequence_number),
- last_time_sent_(QuicTime::Zero()),
- last_receive_time_(QuicTime::Zero()),
- number_of_sequence_numbers_(0),
- received_bytes_(0) {
-}
-
-void AvailableChannelEstimator::OnIncomingFeedback(
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount packet_size,
- QuicTime sent_time,
- QuicTime receive_time) {
- if (sequence_number <= first_sequence_number_) {
- // Ignore pre-probe feedback.
- return;
- }
- if (sequence_number <= last_incorporated_sequence_number_) {
- // Ignore old feedback; will remove duplicates.
- return;
- }
- // Remember the highest received sequence number.
- last_incorporated_sequence_number_ = sequence_number;
- if (number_of_sequence_numbers_ < kNumberOfSamples) {
- // We don't care how many sequence numbers we have after we pass
- // kNumberOfSamples.
- number_of_sequence_numbers_++;
- }
- last_receive_time_ = receive_time;
- last_time_sent_ = sent_time;
- received_bytes_ += packet_size;
- // TODO(pwestin): the variance here should give us information about accuracy.
-}
-
-AvailableChannelEstimateState
- AvailableChannelEstimator::GetAvailableChannelEstimate(
- QuicBandwidth* bandwidth) const {
- if (number_of_sequence_numbers_ < 2) {
- return kAvailableChannelEstimateUnknown;
- }
- QuicTime::Delta send_delta = last_time_sent_.Subtract(first_send_time_);
- QuicTime::Delta receive_delta =
- last_receive_time_.Subtract(first_receive_time_);
-
- // TODO(pwestin): room for improvement here. Keeping it simple for now.
- *bandwidth = QuicBandwidth::FromBytesAndTimeDelta(received_bytes_,
- receive_delta);
-
- QuicTime::Delta diff = receive_delta.Subtract(send_delta);
- QuicTime::Delta ten_percent_of_send_time =
- QuicTime::Delta::FromMicroseconds(send_delta.ToMicroseconds() / 10);
-
- if (diff < ten_percent_of_send_time) {
- return kAvailableChannelEstimateSenderLimited;
- }
- if (number_of_sequence_numbers_ < kNumberOfSamples) {
- return kAvailableChannelEstimateUncertain;
- }
- return kAvailableChannelEstimateGood;
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/available_channel_estimator.h b/chromium/net/quic/congestion_control/available_channel_estimator.h
deleted file mode 100644
index e2ad19a8b66..00000000000
--- a/chromium/net/quic/congestion_control/available_channel_estimator.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Based on the inter arrival time of the received packets relative to the time
-// those packets where sent we can estimate the available capacity of the
-// channel.
-// We can only use packet trains that are sent out faster than the acctual
-// available channel capacity.
-// Note 1: this is intended to be a temporary class created when you send out a
-// channel probing burst. Once the last packet have arrived you ask it for an
-// estimate.
-// Note 2: During this phase we should not update the overuse detector.
-#ifndef NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_
-#define NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_
-
-#include "base/basictypes.h"
-#include "net/base/net_export.h"
-#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-enum NET_EXPORT_PRIVATE AvailableChannelEstimateState {
- kAvailableChannelEstimateUnknown = 0,
- kAvailableChannelEstimateUncertain = 1,
- kAvailableChannelEstimateGood = 2,
- kAvailableChannelEstimateSenderLimited = 3,
-};
-
-class NET_EXPORT_PRIVATE AvailableChannelEstimator {
- public:
- explicit AvailableChannelEstimator(
- QuicPacketSequenceNumber first_sequence_number,
- QuicTime first_send_time,
- QuicTime first_receive_time);
-
- // Update the statistics with each receive time, for every packet we get a
- // feedback message for.
- void OnIncomingFeedback(QuicPacketSequenceNumber sequence_number,
- QuicByteCount packet_size,
- QuicTime sent_time,
- QuicTime receive_time);
-
- // Get the current estimated available channel capacity.
- // bandwidth_estimate is invalid if kAvailableChannelEstimateUnknown
- // is returned.
- AvailableChannelEstimateState GetAvailableChannelEstimate(
- QuicBandwidth* bandwidth_estimate) const;
-
- private:
- const QuicPacketSequenceNumber first_sequence_number_;
- const QuicTime first_send_time_;
- const QuicTime first_receive_time_;
- QuicPacketSequenceNumber last_incorporated_sequence_number_;
- QuicTime last_time_sent_;
- QuicTime last_receive_time_;
- int number_of_sequence_numbers_;
- QuicByteCount received_bytes_;
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_
diff --git a/chromium/net/quic/congestion_control/available_channel_estimator_test.cc b/chromium/net/quic/congestion_control/available_channel_estimator_test.cc
deleted file mode 100644
index b4a4b9c341c..00000000000
--- a/chromium/net/quic/congestion_control/available_channel_estimator_test.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/congestion_control/available_channel_estimator.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class AvailableChannelEstimatorTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- srand(1234);
- packet_size_ = 1200;
- sequence_number_ = 1;
- QuicTime receive_time = receive_clock_.Now();
- QuicTime sent_time = send_clock_.Now();
- estimator_.reset(new AvailableChannelEstimator(sequence_number_,
- sent_time,
- receive_time));
- }
-
- MockClock send_clock_;
- MockClock receive_clock_;
- QuicPacketSequenceNumber sequence_number_;
- QuicByteCount packet_size_;
- scoped_ptr<AvailableChannelEstimator> estimator_;
-};
-
-TEST_F(AvailableChannelEstimatorTest, SimpleBasic) {
- QuicBandwidth bandwidth = QuicBandwidth::Zero();
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1);
- receive_clock_.AdvanceTime(received_delta);
- send_clock_.AdvanceTime(send_delta);
- QuicTime receive_time = receive_clock_.Now();
- QuicTime sent_time = send_clock_.Now();
- estimator_->OnIncomingFeedback(++sequence_number_,
- packet_size_,
- sent_time,
- receive_time);
- EXPECT_EQ(kAvailableChannelEstimateUnknown,
- estimator_->GetAvailableChannelEstimate(&bandwidth));
-
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.Now();
- send_clock_.AdvanceTime(send_delta);
- sent_time = send_clock_.Now();
-
- estimator_->OnIncomingFeedback(++sequence_number_,
- packet_size_,
- sent_time,
- receive_time);
- EXPECT_EQ(kAvailableChannelEstimateUncertain,
- estimator_->GetAvailableChannelEstimate(&bandwidth));
-
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
- bandwidth);
-}
-
-// TODO(pwestin): simulate cross traffic.
-TEST_F(AvailableChannelEstimatorTest, SimpleUncertainEstimate) {
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1);
-
- for (int i = 0; i < 8; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.Now();
- send_clock_.AdvanceTime(send_delta);
- QuicTime sent_time = send_clock_.Now();
- estimator_->OnIncomingFeedback(++sequence_number_,
- packet_size_,
- sent_time,
- receive_time);
- }
- QuicBandwidth bandwidth = QuicBandwidth::Zero();
- EXPECT_EQ(kAvailableChannelEstimateUncertain,
- estimator_->GetAvailableChannelEstimate(&bandwidth));
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
- bandwidth);
-}
-
-TEST_F(AvailableChannelEstimatorTest, SimpleGoodEstimate) {
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1);
-
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.Now();
- send_clock_.AdvanceTime(send_delta);
- QuicTime sent_time = send_clock_.Now();
- estimator_->OnIncomingFeedback(++sequence_number_,
- packet_size_,
- sent_time,
- receive_time);
- }
- QuicBandwidth bandwidth = QuicBandwidth::Zero();
- EXPECT_EQ(kAvailableChannelEstimateGood,
- estimator_->GetAvailableChannelEstimate(&bandwidth));
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
- bandwidth);
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/channel_estimator.cc b/chromium/net/quic/congestion_control/channel_estimator.cc
deleted file mode 100644
index a012b666a39..00000000000
--- a/chromium/net/quic/congestion_control/channel_estimator.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/channel_estimator.h"
-
-// To get information about bandwidth, our send rate for a pair of packets must
-// be much faster (ideally back to back) than the receive rate. In that
-// scenario, the arriving packet pair will tend to arrive at the max bandwidth
-// of the channel. Said another way, when our inter-departure time is a small
-// fraction of the inter-arrival time for the same pair of packets, then we can
-// get an estimate of bandwidth from that interarrival time. The following
-// constant is the threshold ratio for deriving bandwidth information.
-static const int kInterarrivalRatioThresholdForBandwidthEstimation = 5;
-static const size_t kMinNumberOfSamples = 10;
-static const size_t kMaxNumberOfSamples = 100;
-
-namespace net {
-
-ChannelEstimator::ChannelEstimator()
- : last_sequence_number_(0),
- last_send_time_(QuicTime::Zero()),
- last_receive_time_(QuicTime::Zero()),
- sorted_bitrate_estimates_(kMaxNumberOfSamples) {
-}
-
-ChannelEstimator::~ChannelEstimator() {
-}
-
-void ChannelEstimator::OnAcknowledgedPacket(
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount packet_size,
- QuicTime send_time,
- QuicTime receive_time) {
- if (last_sequence_number_ > sequence_number) {
- // Old packet. The sequence_number use the full 64 bits even though it's
- // less on the wire.
- return;
- }
- if (last_sequence_number_ != sequence_number - 1) {
- DVLOG(1) << "Skip channel estimator due to lost packet(s)";
- } else if (last_send_time_.IsInitialized()) {
- QuicTime::Delta sent_delta = send_time.Subtract(last_send_time_);
- QuicTime::Delta received_delta = receive_time.Subtract(last_receive_time_);
- if (received_delta.ToMicroseconds() >
- kInterarrivalRatioThresholdForBandwidthEstimation *
- sent_delta.ToMicroseconds()) {
- UpdateFilter(received_delta, packet_size, sequence_number);
- }
- }
- last_sequence_number_ = sequence_number;
- last_send_time_ = send_time;
- last_receive_time_ = receive_time;
-}
-
-ChannelEstimateState ChannelEstimator::GetChannelEstimate(
- QuicBandwidth* estimate) const {
- if (sorted_bitrate_estimates_.Size() < kMinNumberOfSamples) {
- // Not enough data to make an estimate.
- return kChannelEstimateUnknown;
- }
- // Our data is stored in a sorted map, we need to iterate through our map to
- // find the estimated bitrates at our targeted percentiles.
-
- // Calculate 25th percentile.
- size_t beginning_window = sorted_bitrate_estimates_.Size() / 4;
- // Calculate 50th percentile.
- size_t median = sorted_bitrate_estimates_.Size() / 2;
- // Calculate 75th percentile.
- size_t end_window = sorted_bitrate_estimates_.Size() - beginning_window;
-
- QuicBandwidth bitrate_25th_percentile = QuicBandwidth::Zero();
- QuicBandwidth median_bitrate = QuicBandwidth::Zero();
- QuicBandwidth bitrate_75th_percentile = QuicBandwidth::Zero();
- QuicMaxSizedMap<QuicBandwidth, QuicPacketSequenceNumber>::ConstIterator it =
- sorted_bitrate_estimates_.Begin();
-
- for (size_t i = 0; i <= end_window; ++i, ++it) {
- DCHECK(it != sorted_bitrate_estimates_.End());
- if (i == beginning_window) {
- bitrate_25th_percentile = it->first;
- }
- if (i == median) {
- median_bitrate = it->first;
- }
- if (i == end_window) {
- bitrate_75th_percentile = it->first;
- }
- }
- *estimate = median_bitrate;
- DVLOG(1) << "Channel estimate is:"
- << median_bitrate.ToKBitsPerSecond() << " Kbit/s";
- // If the bitrates in our 25th to 75th percentile window varies more than
- // 25% of the median bitrate we consider the estimate to be uncertain.
- if (bitrate_75th_percentile.Subtract(bitrate_25th_percentile) >
- median_bitrate.Scale(0.25f)) {
- return kChannelEstimateUncertain;
- }
- return kChannelEstimateGood;
-}
-
-void ChannelEstimator::UpdateFilter(QuicTime::Delta received_delta,
- QuicByteCount size_delta,
- QuicPacketSequenceNumber sequence_number) {
- QuicBandwidth estimate =
- QuicBandwidth::FromBytesAndTimeDelta(size_delta, received_delta);
- sorted_bitrate_estimates_.Insert(estimate, sequence_number);
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/channel_estimator.h b/chromium/net/quic/congestion_control/channel_estimator.h
deleted file mode 100644
index 9be8031145b..00000000000
--- a/chromium/net/quic/congestion_control/channel_estimator.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Based on the inter arrival time of the received packets relative to the time
-// those packets where sent we can estimate the max capacity of the channel.
-// We can only use packet pair that are sent out faster than the acctual
-// channel capacity.
-#ifndef NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_
-#define NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_
-
-#include <list>
-#include <map>
-
-#include "base/basictypes.h"
-#include "net/base/net_export.h"
-#include "net/quic/congestion_control/quic_max_sized_map.h"
-#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-enum ChannelEstimateState {
- kChannelEstimateUnknown = 0,
- kChannelEstimateUncertain = 1,
- kChannelEstimateGood = 2
-};
-
-class NET_EXPORT_PRIVATE ChannelEstimator {
- public:
- ChannelEstimator();
- ~ChannelEstimator();
-
- // This method should be called each time we acquire a receive time for a
- // packet we previously sent. It calculates deltas between consecutive
- // receive times, and may use that to update the channel bandwidth estimate.
- void OnAcknowledgedPacket(QuicPacketSequenceNumber sequence_number,
- QuicByteCount packet_size,
- QuicTime send_time,
- QuicTime receive_time);
-
- // Get the current estimated state and channel capacity.
- // Note: estimate will not be valid when kChannelEstimateUnknown is returned.
- ChannelEstimateState GetChannelEstimate(QuicBandwidth* estimate) const;
-
- private:
- void UpdateFilter(QuicTime::Delta received_delta, QuicByteCount size_delta,
- QuicPacketSequenceNumber sequence_number);
-
- QuicPacketSequenceNumber last_sequence_number_;
- QuicTime last_send_time_;
- QuicTime last_receive_time_;
- QuicMaxSizedMap<QuicBandwidth, QuicPacketSequenceNumber>
- sorted_bitrate_estimates_;
-
- DISALLOW_COPY_AND_ASSIGN(ChannelEstimator);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_
diff --git a/chromium/net/quic/congestion_control/channel_estimator_test.cc b/chromium/net/quic/congestion_control/channel_estimator_test.cc
deleted file mode 100644
index 460e436589e..00000000000
--- a/chromium/net/quic/congestion_control/channel_estimator_test.cc
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "net/quic/congestion_control/channel_estimator.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class ChannelEstimatorTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- srand(1234);
- packet_size_ = 1200;
- sequence_number_ = 1;
- }
-
- QuicPacketSequenceNumber sequence_number_;
- QuicByteCount packet_size_;
- MockClock send_clock_;
- MockClock receive_clock_;
- ChannelEstimator channel_estimator_;
-};
-
-TEST_F(ChannelEstimatorTest, SimpleNonDetect) {
- // In this test, the send times differ by the same delta as the receive times,
- // so we haven't sent packets closely enough to detect "spreading," or
- // effective bandwidth.
- QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 1000; ++i) {
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(delta);
- receive_clock_.AdvanceTime(delta);
- }
- QuicBandwidth estimate = QuicBandwidth::Zero();
- EXPECT_EQ(kChannelEstimateUnknown,
- channel_estimator_.GetChannelEstimate(&estimate));
- EXPECT_TRUE(estimate.IsZero());
-}
-
-TEST_F(ChannelEstimatorTest, SimplePacketPairDetect) {
- // In this test, we start by sending packet pairs back-to-back and
- // add a receive side spreading that indicate an effective bandwidth.
- // We do 2 testes with different effective bandwidth to make sure that we
- // detect the new effective bandwidth.
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- QuicTime send_time = send_clock_.ApproximateNow();
-
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(send_delta);
- }
- QuicBandwidth estimate = QuicBandwidth::Zero();
- EXPECT_EQ(kChannelEstimateGood,
- channel_estimator_.GetChannelEstimate(&estimate));
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
- estimate);
- received_delta = QuicTime::Delta::FromMilliseconds(1);
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- QuicTime send_time = send_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(send_delta);
- }
- EXPECT_EQ(kChannelEstimateGood,
- channel_estimator_.GetChannelEstimate(&estimate));
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
- estimate);
-}
-
-TEST_F(ChannelEstimatorTest, SimpleFlatSlope) {
- // In this test, we send packet pairs back-to-back and add a slowly increasing
- // receive side spreading. We expect the estimate to be good and that our
- // mean receive side spreading is returned as the estimate.
- QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta received_delta = initial_received_delta;
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- QuicTime send_time = send_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(send_delta);
- received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(10));
- }
- QuicBandwidth estimate = QuicBandwidth::Zero();
- EXPECT_EQ(kChannelEstimateGood,
- channel_estimator_.GetChannelEstimate(&estimate));
-
- // Calculate our mean receive delta.
- QuicTime::Delta increased_received_delta =
- received_delta.Subtract(initial_received_delta);
- QuicTime::Delta mean_received_delta = initial_received_delta.Add(
- QuicTime::Delta::FromMicroseconds(
- increased_received_delta.ToMicroseconds() / 2));
-
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_,
- mean_received_delta), estimate);
-}
-
-TEST_F(ChannelEstimatorTest, SimpleMediumSlope) {
- // In this test, we send packet pairs back-to-back and add an increasing
- // receive side spreading. We expect the estimate to be uncertaint and that
- // our mean receive side spreading is returned as the estimate.
- QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta received_delta = initial_received_delta;
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- QuicTime send_time = send_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(send_delta);
- received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(50));
- }
- QuicBandwidth estimate = QuicBandwidth::Zero();
- EXPECT_EQ(kChannelEstimateUncertain,
- channel_estimator_.GetChannelEstimate(&estimate));
-
- // Calculate our mean receive delta.
- QuicTime::Delta increased_received_delta =
- received_delta.Subtract(initial_received_delta);
- QuicTime::Delta mean_received_delta = initial_received_delta.Add(
- QuicTime::Delta::FromMicroseconds(
- increased_received_delta.ToMicroseconds() / 2));
-
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_,
- mean_received_delta), estimate);
-}
-
-TEST_F(ChannelEstimatorTest, SimpleSteepSlope) {
- // In this test, we send packet pairs back-to-back and add a rapidly
- // increasing receive side spreading. We expect the estimate to be uncertain
- // and that our mean receive side spreading is returned as the estimate.
- QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta received_delta = initial_received_delta;
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 100; ++i) {
- receive_clock_.AdvanceTime(received_delta);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- QuicTime send_time = send_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
- packet_size_,
- send_time,
- receive_time);
- send_clock_.AdvanceTime(send_delta);
- received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(100));
- }
- QuicBandwidth estimate = QuicBandwidth::Zero();
- EXPECT_EQ(kChannelEstimateUncertain,
- channel_estimator_.GetChannelEstimate(&estimate));
-
- // Calculate our mean receive delta.
- QuicTime::Delta increased_received_delta =
- received_delta.Subtract(initial_received_delta);
- QuicTime::Delta mean_received_delta = initial_received_delta.Add(
- QuicTime::Delta::FromMicroseconds(
- increased_received_delta.ToMicroseconds() / 2));
-
- EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_,
- mean_received_delta), estimate);
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/cube_root.h b/chromium/net/quic/congestion_control/cube_root.h
index 3b3736c1c59..293a7199f86 100644
--- a/chromium/net/quic/congestion_control/cube_root.h
+++ b/chromium/net/quic/congestion_control/cube_root.h
@@ -15,6 +15,9 @@ class NET_EXPORT_PRIVATE CubeRoot {
// Calculates the cube root using a table lookup followed by one Newton-
// Raphson iteration.
static uint32 Root(uint64 a);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CubeRoot);
};
} // namespace net
diff --git a/chromium/net/quic/congestion_control/cubic.cc b/chromium/net/quic/congestion_control/cubic.cc
index 3d03b9f60d1..f64e58b05b9 100644
--- a/chromium/net/quic/congestion_control/cubic.cc
+++ b/chromium/net/quic/congestion_control/cubic.cc
@@ -9,9 +9,15 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/time/time.h"
+#include "net/quic/congestion_control/cube_root.h"
+#include "net/quic/quic_protocol.h"
+
+using std::max;
namespace net {
+namespace {
+
// Constants based on TCP defaults.
// The following constants are in 2^10 fractions of a second instead of ms to
// allow a 10 shift right to divide.
@@ -21,93 +27,37 @@ const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3)
const int kCubeCongestionWindowScale = 410;
const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) /
kCubeCongestionWindowScale;
-const uint32 kBetaSPDY = 939; // Back off factor after loss for SPDY, reduces
- // the CWND by 1/12th.
-const uint32 kBetaLastMax = 871; // Additional back off factor after loss for
- // the stored max value.
-namespace {
-// Find last bit in a 64-bit word.
-int FindMostSignificantBit(uint64 x) {
- if (!x) {
- return 0;
- }
- int r = 0;
- if (x & 0xffffffff00000000ull) {
- x >>= 32;
- r += 32;
- }
- if (x & 0xffff0000u) {
- x >>= 16;
- r += 16;
- }
- if (x & 0xff00u) {
- x >>= 8;
- r += 8;
- }
- if (x & 0xf0u) {
- x >>= 4;
- r += 4;
- }
- if (x & 0xcu) {
- x >>= 2;
- r += 2;
- }
- if (x & 0x02u) {
- x >>= 1;
- r++;
- }
- if (x & 0x01u) {
- r++;
- }
- return r;
-}
+const uint32 kNumConnections = 2;
+const float kBeta = 0.7f; // Default Cubic backoff factor.
+// Additional backoff factor when loss occurs in the concave part of the Cubic
+// curve. This additional backoff factor is expected to give up bandwidth to
+// new concurrent flows and speed up convergence.
+const float kBetaLastMax = 0.85f;
+
+// kNConnectionBeta is the backoff factor after loss for our N-connection
+// emulation, which emulates the effective backoff of an ensemble of N TCP-Reno
+// connections on a single loss event. The effective multiplier is computed as:
+const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections;
+
+// TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that
+// kBeta here is a cwnd multiplier, and is equal to 1-beta from the CUBIC paper.
+// We derive the equivalent kNConnectionAlpha for an N-connection emulation as:
+const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections *
+ (1 - kNConnectionBeta) / (1 + kNConnectionBeta);
+// TODO(jri): Compute kNConnectionBeta and kNConnectionAlpha from
+// number of active streams.
-// 6 bits table [0..63]
-const uint32 cube_root_table[] = {
- 0, 54, 54, 54, 118, 118, 118, 118, 123, 129, 134, 138, 143, 147, 151,
- 156, 157, 161, 164, 168, 170, 173, 176, 179, 181, 185, 187, 190, 192, 194,
- 197, 199, 200, 202, 204, 206, 209, 211, 213, 215, 217, 219, 221, 222, 224,
- 225, 227, 229, 231, 232, 234, 236, 237, 239, 240, 242, 244, 245, 246, 248,
- 250, 251, 252, 254
-};
} // namespace
-Cubic::Cubic(const QuicClock* clock)
+Cubic::Cubic(const QuicClock* clock, QuicConnectionStats* stats)
: clock_(clock),
epoch_(QuicTime::Zero()),
- last_update_time_(QuicTime::Zero()) {
+ last_update_time_(QuicTime::Zero()),
+ stats_(stats) {
Reset();
}
-// Calculate the cube root using a table lookup followed by one Newton-Raphson
-// iteration.
-uint32 Cubic::CubeRoot(uint64 a) {
- uint32 msb = FindMostSignificantBit(a);
- DCHECK_LE(msb, 64u);
-
- if (msb < 7) {
- // MSB in our table.
- return ((cube_root_table[static_cast<uint32>(a)]) + 31) >> 6;
- }
- // MSB 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ...
- // cubic_shift 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, ...
- uint32 cubic_shift = (msb - 4);
- cubic_shift = ((cubic_shift * 342) >> 10); // Div by 3, biased high.
-
- // 4 to 6 bits accuracy depending on MSB.
- uint32 down_shifted_to_6bit = (a >> (cubic_shift * 3));
- uint64 root = ((cube_root_table[down_shifted_to_6bit] + 10) << cubic_shift)
- >> 6;
-
- // Make one Newton-Raphson iteration.
- // Since x has an error (inaccuracy due to the use of fix point) we get a
- // more accurate result by doing x * (x - 1) instead of x * x.
- root = 2 * root + (a / (root * (root - 1)));
- root = ((root * 341) >> 10); // Div by 3, biased low.
- return static_cast<uint32>(root);
-}
-
void Cubic::Reset() {
epoch_ = QuicTime::Zero(); // Reset time.
last_update_time_ = QuicTime::Zero(); // Reset time.
@@ -120,18 +70,36 @@ void Cubic::Reset() {
last_target_congestion_window_ = 0;
}
+void Cubic::UpdateCongestionControlStats(
+ QuicTcpCongestionWindow new_cubic_mode_cwnd,
+ QuicTcpCongestionWindow new_reno_mode_cwnd) {
+
+ QuicTcpCongestionWindow highest_new_cwnd = std::max(new_cubic_mode_cwnd,
+ new_reno_mode_cwnd);
+ if (last_congestion_window_ < highest_new_cwnd) {
+ // cwnd will increase to highest_new_cwnd.
+ stats_->cwnd_increase_congestion_avoidance +=
+ highest_new_cwnd - last_congestion_window_;
+ if (new_cubic_mode_cwnd > new_reno_mode_cwnd) {
+ // This cwnd increase is due to cubic mode.
+ stats_->cwnd_increase_cubic_mode +=
+ new_cubic_mode_cwnd - last_congestion_window_;
+ }
+ }
+}
+
QuicTcpCongestionWindow Cubic::CongestionWindowAfterPacketLoss(
QuicTcpCongestionWindow current_congestion_window) {
if (current_congestion_window < last_max_congestion_window_) {
// We never reached the old max, so assume we are competing with another
// flow. Use our extra back off factor to allow the other flow to go up.
last_max_congestion_window_ =
- (kBetaLastMax * current_congestion_window) >> 10;
+ static_cast<int>(kBetaLastMax * current_congestion_window);
} else {
last_max_congestion_window_ = current_congestion_window;
}
epoch_ = QuicTime::Zero(); // Reset time.
- return (current_congestion_window * kBetaSPDY) >> 10;
+ return static_cast<int>(current_congestion_window * kNConnectionBeta);
}
QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck(
@@ -143,8 +111,8 @@ QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck(
// Cubic is "independent" of RTT, the update is limited by the time elapsed.
if (last_congestion_window_ == current_congestion_window &&
(current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval())) {
- return std::max(last_target_congestion_window_,
- estimated_tcp_congestion_window_);
+ return max(last_target_congestion_window_,
+ estimated_tcp_congestion_window_);
}
last_congestion_window_ = current_congestion_window;
last_update_time_ = current_time;
@@ -160,7 +128,7 @@ QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck(
time_to_origin_point_ = 0;
origin_point_congestion_window_ = current_congestion_window;
} else {
- time_to_origin_point_ = CubeRoot(kCubeFactor *
+ time_to_origin_point_ = CubeRoot::Root(kCubeFactor *
(last_max_congestion_window_ - current_congestion_window));
origin_point_congestion_window_ =
last_max_congestion_window_;
@@ -180,22 +148,35 @@ QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck(
QuicTcpCongestionWindow target_congestion_window =
origin_point_congestion_window_ - delta_congestion_window;
+ DCHECK_LT(0u, estimated_tcp_congestion_window_);
+ // With dynamic beta/alpha based on number of active streams, it is possible
+ // for the required_ack_count to become much lower than acked_packets_count_
+ // suddenly, leading to more than one iteration through the following loop.
+ while (true) {
+ // Update estimated TCP congestion_window.
+ uint32 required_ack_count =
+ estimated_tcp_congestion_window_ / kNConnectionAlpha;
+ if (acked_packets_count_ < required_ack_count) {
+ break;
+ }
+ acked_packets_count_ -= required_ack_count;
+ estimated_tcp_congestion_window_++;
+ }
+
+ // Update cubic mode and reno mode stats in QuicConnectionStats.
+ UpdateCongestionControlStats(target_congestion_window,
+ estimated_tcp_congestion_window_);
+
// We have a new cubic congestion window.
last_target_congestion_window_ = target_congestion_window;
- // Update estimated TCP congestion_window.
- // Note: we do a normal Reno congestion avoidance calculation not the
- // calculation described in section 3.3 TCP-friendly region of the document.
- while (acked_packets_count_ >= estimated_tcp_congestion_window_) {
- acked_packets_count_ -= estimated_tcp_congestion_window_;
- estimated_tcp_congestion_window_++;
- }
// Compute target congestion_window based on cubic target and estimated TCP
// congestion_window, use highest (fastest).
if (target_congestion_window < estimated_tcp_congestion_window_) {
target_congestion_window = estimated_tcp_congestion_window_;
}
- DVLOG(1) << "Target congestion_window:" << target_congestion_window;
+
+ DVLOG(1) << "Target congestion_window: " << target_congestion_window;
return target_congestion_window;
}
diff --git a/chromium/net/quic/congestion_control/cubic.h b/chromium/net/quic/congestion_control/cubic.h
index e365bbe0ed7..da41ddf8ab2 100644
--- a/chromium/net/quic/congestion_control/cubic.h
+++ b/chromium/net/quic/congestion_control/cubic.h
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "net/base/net_export.h"
#include "net/quic/quic_clock.h"
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/quic_time.h"
namespace net {
@@ -20,7 +21,7 @@ typedef uint32 QuicTcpCongestionWindow;
class NET_EXPORT_PRIVATE Cubic {
public:
- explicit Cubic(const QuicClock* clock);
+ Cubic(const QuicClock* clock, QuicConnectionStats* stats);
// Call after a timeout to reset the cubic state.
void Reset();
@@ -39,16 +40,14 @@ class NET_EXPORT_PRIVATE Cubic {
QuicTcpCongestionWindow current,
QuicTime::Delta delay_min);
- protected:
- // Calculates the cubic root using a table lookup followed by one Newton-
- // Raphson iteration.
- uint32 CubeRoot(uint64 a);
-
private:
static const QuicTime::Delta MaxCubicTimeInterval() {
return QuicTime::Delta::FromMilliseconds(30);
}
+ // Update congestion control variables in QuicConnectionStats.
+ void UpdateCongestionControlStats(QuicTcpCongestionWindow new_cubic_mode_cwnd,
+ QuicTcpCongestionWindow new_reno_mode_cwnd);
const QuicClock* clock_;
// Time when this cycle started, after last loss event.
@@ -80,8 +79,12 @@ class NET_EXPORT_PRIVATE Cubic {
// Last congestion window in packets computed by cubic function.
QuicTcpCongestionWindow last_target_congestion_window_;
+ // QuicConnectionStats includes congestion control related stats.
+ QuicConnectionStats* stats_;
+
DISALLOW_COPY_AND_ASSIGN(Cubic);
};
} // namespace net
+
#endif // NET_QUIC_CONGESTION_CONTROL_CUBIC_H_
diff --git a/chromium/net/quic/congestion_control/cubic_test.cc b/chromium/net/quic/congestion_control/cubic_test.cc
index c01bb050643..915274a13d3 100644
--- a/chromium/net/quic/congestion_control/cubic_test.cc
+++ b/chromium/net/quic/congestion_control/cubic_test.cc
@@ -4,66 +4,35 @@
#include "base/basictypes.h"
#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
#include "net/quic/congestion_control/cubic.h"
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/test_tools/mock_clock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace test {
-class CubicPeer : public Cubic {
- public:
- explicit CubicPeer(QuicClock* clock)
- : Cubic(clock) {
- }
- using Cubic::CubeRoot;
-};
+const float kBeta = 0.7f; // Default Cubic backoff factor.
+const uint32 kNumConnections = 2;
+const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections;
+const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections *
+ (1 - kNConnectionBeta) / (1 + kNConnectionBeta);
class CubicTest : public ::testing::Test {
protected:
CubicTest()
: one_ms_(QuicTime::Delta::FromMilliseconds(1)),
- hundred_ms_(QuicTime::Delta::FromMilliseconds(100)) {
- }
- virtual void SetUp() {
- cubic_.reset(new CubicPeer(&clock_));
+ hundred_ms_(QuicTime::Delta::FromMilliseconds(100)),
+ cubic_(&clock_, &stats_) {
}
const QuicTime::Delta one_ms_;
const QuicTime::Delta hundred_ms_;
MockClock clock_;
- scoped_ptr<CubicPeer> cubic_;
+ QuicConnectionStats stats_;
+ Cubic cubic_;
};
-TEST_F(CubicTest, CubeRootLow) {
- for (uint32 i = 1; i < 256; ++i) {
- uint64 cube = i * i * i;
- uint8 cube_root = cubic_->CubeRoot(cube);
- EXPECT_EQ(i, cube_root);
- }
-}
-
-TEST_F(CubicTest, CubeRootHigh) {
- // Test the range we will opperate in, 1300 to 130 000.
- // We expect some loss in accuracy, accepting +-0.2%.
- for (uint64 i = 1300; i < 20000; i += 100) {
- uint64 cube = i * i * i;
- uint32 cube_root = cubic_->CubeRoot(cube);
- uint32 margin = cube_root >> 9; // Calculate 0.2% roughly by
- // dividing by 512.
- EXPECT_LE(i - margin, cube_root);
- EXPECT_GE(i + margin, cube_root);
- }
- for (uint64 i = 20000; i < 130000; i *= 2) {
- uint64 cube = i * i * i;
- uint32 cube_root = cubic_->CubeRoot(cube);
- uint32 margin = cube_root >> 9;
- EXPECT_LE(i - margin, cube_root);
- EXPECT_GE(i + margin, cube_root);
- }
-}
-
-TEST_F(CubicTest, AboveOrgin) {
+TEST_F(CubicTest, AboveOrigin) {
// Convex growth.
const QuicTime::Delta rtt_min = hundred_ms_;
uint32 current_cwnd = 10;
@@ -71,36 +40,84 @@ TEST_F(CubicTest, AboveOrgin) {
// Initialize the state.
clock_.AdvanceTime(one_ms_);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
current_cwnd = expected_cwnd;
// Normal TCP phase.
for (int i = 0; i < 48; ++i) {
- for (uint32 n = 1; n < current_cwnd; ++n) {
+ for (uint32 n = 1; n < current_cwnd / kNConnectionAlpha; ++n) {
// Call once per ACK.
- EXPECT_EQ(current_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
+ EXPECT_NEAR(current_cwnd,
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min), 1);
}
clock_.AdvanceTime(hundred_ms_);
- current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min);
- EXPECT_EQ(expected_cwnd, current_cwnd);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ EXPECT_NEAR(expected_cwnd, current_cwnd, 1);
expected_cwnd++;
}
// Cubic phase.
- for (int j = 48; j < 100; ++j) {
+ for (int i = 0; i < 52; ++i) {
for (uint32 n = 1; n < current_cwnd; ++n) {
// Call once per ACK.
EXPECT_EQ(current_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
}
clock_.AdvanceTime(hundred_ms_);
- current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
}
- float elapsed_time_s = 10.0f + 0.1f; // We need to add the RTT here.
+ // Total time elapsed so far; add min_rtt (0.1s) here as well.
+ float elapsed_time_s = 10.0f + 0.1f;
+ // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4.
expected_cwnd = 11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410)
/ 1024;
EXPECT_EQ(expected_cwnd, current_cwnd);
}
+TEST_F(CubicTest, CwndIncreaseStatsDuringConvexRegion) {
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ uint32 current_cwnd = 10;
+ uint32 expected_cwnd = current_cwnd + 1;
+ // Initialize controller state.
+ clock_.AdvanceTime(one_ms_);
+ expected_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ current_cwnd = expected_cwnd;
+ // Testing Reno mode increase.
+ for (int i = 0; i < 48; ++i) {
+ for (uint32 n = 1; n < current_cwnd / kNConnectionAlpha; ++n) {
+ // Call once per ACK, causing cwnd growth in Reno mode.
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ // Advance current time so that cwnd update is allowed to happen by Cubic.
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ EXPECT_NEAR(expected_cwnd - 10, stats_.cwnd_increase_congestion_avoidance,
+ 1);
+ EXPECT_NEAR(1u, stats_.cwnd_increase_cubic_mode, 1);
+ expected_cwnd++;
+ }
+ uint32 old_cwnd = current_cwnd;
+ stats_.cwnd_increase_cubic_mode = 0;
+ stats_.cwnd_increase_congestion_avoidance = 0;
+
+ // Testing Cubic mode increase.
+ for (int i = 0; i < 52; ++i) {
+ for (uint32 n = 1; n < current_cwnd; ++n) {
+ // Call once per ACK.
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ // Total time elapsed so far; add min_rtt (0.1s) here as well.
+ float elapsed_time_s = 10.0f + 0.1f;
+ // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4.
+ expected_cwnd = 11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410)
+ / 1024;
+ EXPECT_EQ(expected_cwnd - old_cwnd, stats_.cwnd_increase_cubic_mode);
+ EXPECT_EQ(expected_cwnd - old_cwnd,
+ stats_.cwnd_increase_congestion_avoidance);
+}
+
+
TEST_F(CubicTest, LossEvents) {
const QuicTime::Delta rtt_min = hundred_ms_;
uint32 current_cwnd = 422;
@@ -108,16 +125,16 @@ TEST_F(CubicTest, LossEvents) {
// Initialize the state.
clock_.AdvanceTime(one_ms_);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
- expected_cwnd = current_cwnd * 939 / 1024;
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
+ expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterPacketLoss(current_cwnd));
- expected_cwnd = current_cwnd * 939 / 1024;
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
+ expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterPacketLoss(current_cwnd));
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
}
-TEST_F(CubicTest, BelowOrgin) {
+TEST_F(CubicTest, BelowOrigin) {
// Concave growth.
const QuicTime::Delta rtt_min = hundred_ms_;
uint32 current_cwnd = 422;
@@ -125,26 +142,27 @@ TEST_F(CubicTest, BelowOrgin) {
// Initialize the state.
clock_.AdvanceTime(one_ms_);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
- expected_cwnd = current_cwnd * 939 / 1024;
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
+ expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta);
EXPECT_EQ(expected_cwnd,
- cubic_->CongestionWindowAfterPacketLoss(current_cwnd));
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
current_cwnd = expected_cwnd;
- // First update after epoch.
- current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min);
+ // First update after loss to initialize the epoch.
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ uint32 old_cwnd = current_cwnd;
// Cubic phase.
- for (int i = 0; i < 54; ++i) {
- for (uint32 n = 1; n < current_cwnd; ++n) {
- // Call once per ACK.
- EXPECT_EQ(current_cwnd,
- cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min));
- }
+ stats_.cwnd_increase_cubic_mode = 0;
+ stats_.cwnd_increase_congestion_avoidance = 0;
+ for (int i = 0; i < 40 ; ++i) {
clock_.AdvanceTime(hundred_ms_);
- current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
}
- expected_cwnd = 440;
+ expected_cwnd = 422;
EXPECT_EQ(expected_cwnd, current_cwnd);
+ EXPECT_EQ(expected_cwnd - old_cwnd, stats_.cwnd_increase_cubic_mode);
+ EXPECT_EQ(expected_cwnd - old_cwnd,
+ stats_.cwnd_increase_congestion_avoidance);
}
-} // namespace testing
+} // namespace test
} // namespace net
diff --git a/chromium/net/quic/congestion_control/fix_rate_receiver.cc b/chromium/net/quic/congestion_control/fix_rate_receiver.cc
index 950b49c0d6b..a13362ff226 100644
--- a/chromium/net/quic/congestion_control/fix_rate_receiver.cc
+++ b/chromium/net/quic/congestion_control/fix_rate_receiver.cc
@@ -27,8 +27,7 @@ bool FixRateReceiver::GenerateCongestionFeedback(
void FixRateReceiver::RecordIncomingPacket(
QuicByteCount /*bytes*/,
QuicPacketSequenceNumber /*sequence_number*/,
- QuicTime /*timestamp*/,
- bool /*recovered*/) {
+ QuicTime /*timestamp*/) {
// Nothing to do for this simple implementation.
}
diff --git a/chromium/net/quic/congestion_control/fix_rate_receiver.h b/chromium/net/quic/congestion_control/fix_rate_receiver.h
index 690f13880f1..4ed24b993d4 100644
--- a/chromium/net/quic/congestion_control/fix_rate_receiver.h
+++ b/chromium/net/quic/congestion_control/fix_rate_receiver.h
@@ -30,8 +30,7 @@ class NET_EXPORT_PRIVATE FixRateReceiver : public ReceiveAlgorithmInterface {
// Implements ReceiveAlgorithmInterface.
virtual void RecordIncomingPacket(QuicByteCount bytes,
QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool recovered) OVERRIDE;
+ QuicTime timestamp) OVERRIDE;
private:
friend class test::FixRateReceiverPeer;
diff --git a/chromium/net/quic/congestion_control/fix_rate_sender.cc b/chromium/net/quic/congestion_control/fix_rate_sender.cc
index 8280f1eeb31..c346a63d6ff 100644
--- a/chromium/net/quic/congestion_control/fix_rate_sender.cc
+++ b/chromium/net/quic/congestion_control/fix_rate_sender.cc
@@ -9,8 +9,11 @@
#include <algorithm>
#include "base/logging.h"
+#include "net/quic/congestion_control/rtt_stats.h"
#include "net/quic/quic_protocol.h"
+using std::max;
+
namespace {
const int kInitialBitrate = 100000; // In bytes per second.
const uint64 kWindowSizeUs = 10000; // 10 ms.
@@ -18,12 +21,11 @@ namespace {
namespace net {
-FixRateSender::FixRateSender(const QuicClock* clock)
- : bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)),
+FixRateSender::FixRateSender(const RttStats* rtt_stats)
+ : rtt_stats_(rtt_stats),
+ bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)),
max_segment_size_(kDefaultMaxPacketSize),
fix_rate_leaky_bucket_(bitrate_),
- paced_sender_(bitrate_, max_segment_size_),
- data_in_flight_(0),
latest_rtt_(QuicTime::Delta::Zero()) {
DVLOG(1) << "FixRateSender";
}
@@ -34,101 +36,55 @@ FixRateSender::~FixRateSender() {
void FixRateSender::SetFromConfig(const QuicConfig& config, bool is_server) {
}
-void FixRateSender::SetMaxPacketSize(QuicByteCount max_packet_size) {
- max_segment_size_ = max_packet_size;
- paced_sender_.set_max_segment_size(max_segment_size_);
-}
-
void FixRateSender::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& /*sent_packets*/) {
- if (feedback.type != kFixRate) {
- LOG(DFATAL) << "Invalid incoming CongestionFeedbackType:" << feedback.type;
- }
+ QuicTime feedback_receive_time) {
+ LOG_IF(DFATAL, feedback.type != kFixRate) <<
+ "Invalid incoming CongestionFeedbackType:" << feedback.type;
if (feedback.type == kFixRate) {
bitrate_ = feedback.fix_rate.bitrate;
fix_rate_leaky_bucket_.SetDrainingRate(feedback_receive_time, bitrate_);
- paced_sender_.UpdateBandwidthEstimate(feedback_receive_time, bitrate_);
}
// Silently ignore invalid messages in release mode.
}
-void FixRateSender::OnPacketAcked(
- QuicPacketSequenceNumber /*acked_sequence_number*/,
- QuicByteCount bytes_acked,
- QuicTime::Delta rtt) {
- // RTT can't be negative.
- DCHECK_LE(0, rtt.ToMicroseconds());
-
- data_in_flight_ -= bytes_acked;
- if (rtt.IsInfinite()) {
- return;
- }
- latest_rtt_ = rtt;
-}
-
-void FixRateSender::OnPacketLost(QuicPacketSequenceNumber /*sequence_number*/,
- QuicTime /*ack_receive_time*/) {
- // Ignore losses for fix rate sender.
+void FixRateSender::OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) {
}
bool FixRateSender::OnPacketSent(
QuicTime sent_time,
+ QuicByteCount /*bytes_in_flight*/,
QuicPacketSequenceNumber /*sequence_number*/,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData /*has_retransmittable_data*/) {
fix_rate_leaky_bucket_.Add(sent_time, bytes);
- paced_sender_.OnPacketSent(sent_time, bytes);
- if (transmission_type == NOT_RETRANSMISSION) {
- data_in_flight_ += bytes;
- }
+
return true;
}
-void FixRateSender::OnRetransmissionTimeout() { }
-
-void FixRateSender::OnPacketAbandoned(
- QuicPacketSequenceNumber /*sequence_number*/,
- QuicByteCount /*abandoned_bytes*/) {
-}
+void FixRateSender::OnRetransmissionTimeout(bool packets_retransmitted) { }
QuicTime::Delta FixRateSender::TimeUntilSend(
QuicTime now,
- TransmissionType /* transmission_type */,
- HasRetransmittableData /*has_retransmittable_data*/,
- IsHandshake /*handshake*/) {
- if (CongestionWindow() > fix_rate_leaky_bucket_.BytesPending(now)) {
- if (CongestionWindow() <= data_in_flight_) {
- // We need an ack before we send more.
- return QuicTime::Delta::Infinite();
- }
- return paced_sender_.TimeUntilSend(now, QuicTime::Delta::Zero());
- }
- QuicTime::Delta time_remaining = fix_rate_leaky_bucket_.TimeRemaining(now);
- if (time_remaining.IsZero()) {
- // We need an ack before we send more.
- return QuicTime::Delta::Infinite();
- }
- return paced_sender_.TimeUntilSend(now, time_remaining);
+ QuicByteCount /*bytes_in_flight*/,
+ HasRetransmittableData /*has_retransmittable_data*/) const {
+ return fix_rate_leaky_bucket_.TimeRemaining(now);
}
-QuicByteCount FixRateSender::CongestionWindow() {
+QuicByteCount FixRateSender::CongestionWindow() const {
QuicByteCount window_size_bytes = bitrate_.ToBytesPerPeriod(
QuicTime::Delta::FromMicroseconds(kWindowSizeUs));
// Make sure window size is not less than a packet.
- return std::max(kDefaultMaxPacketSize, window_size_bytes);
+ return max(kDefaultMaxPacketSize, window_size_bytes);
}
QuicBandwidth FixRateSender::BandwidthEstimate() const {
return bitrate_;
}
-QuicTime::Delta FixRateSender::SmoothedRtt() const {
- // TODO(satyamshekhar): Calculate and return smoothed rtt.
- return latest_rtt_;
-}
QuicTime::Delta FixRateSender::RetransmissionDelay() const {
// TODO(pwestin): Calculate and return retransmission delay.
diff --git a/chromium/net/quic/congestion_control/fix_rate_sender.h b/chromium/net/quic/congestion_control/fix_rate_sender.h
index d3d48c7e3da..3cb07ff4ddf 100644
--- a/chromium/net/quic/congestion_control/fix_rate_sender.h
+++ b/chromium/net/quic/congestion_control/fix_rate_sender.h
@@ -11,58 +11,52 @@
#include "base/compiler_specific.h"
#include "net/base/net_export.h"
#include "net/quic/quic_clock.h"
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/quic_time.h"
#include "net/quic/congestion_control/leaky_bucket.h"
-#include "net/quic/congestion_control/paced_sender.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
namespace net {
+class RttStats;
+
class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface {
public:
- explicit FixRateSender(const QuicClock* clock);
+ explicit FixRateSender(const RttStats* rtt_stats);
virtual ~FixRateSender();
// Start implementation of SendAlgorithmInterface.
virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE;
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE;
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& sent_packets) OVERRIDE;
- virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) OVERRIDE;
- virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime ack_receive_time) OVERRIDE;
+ QuicTime feedback_receive_time) OVERRIDE;
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) OVERRIDE;
virtual bool OnPacketSent(
QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData has_retransmittable_data) OVERRIDE;
- virtual void OnRetransmissionTimeout() OVERRIDE;
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) OVERRIDE;
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE;
virtual QuicTime::Delta TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) OVERRIDE;
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const OVERRIDE;
virtual QuicBandwidth BandwidthEstimate() const OVERRIDE;
- virtual QuicTime::Delta SmoothedRtt() const OVERRIDE;
virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE;
virtual QuicByteCount GetCongestionWindow() const OVERRIDE;
// End implementation of SendAlgorithmInterface.
private:
- QuicByteCount CongestionWindow();
+ QuicByteCount CongestionWindow() const;
+ const RttStats* rtt_stats_;
QuicBandwidth bitrate_;
QuicByteCount max_segment_size_;
LeakyBucket fix_rate_leaky_bucket_;
- PacedSender paced_sender_;
- QuicByteCount data_in_flight_;
QuicTime::Delta latest_rtt_;
DISALLOW_COPY_AND_ASSIGN(FixRateSender);
diff --git a/chromium/net/quic/congestion_control/fix_rate_test.cc b/chromium/net/quic/congestion_control/fix_rate_test.cc
index 752d37fe641..c82bb60d823 100644
--- a/chromium/net/quic/congestion_control/fix_rate_test.cc
+++ b/chromium/net/quic/congestion_control/fix_rate_test.cc
@@ -8,6 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "net/quic/congestion_control/fix_rate_receiver.h"
#include "net/quic/congestion_control/fix_rate_sender.h"
+#include "net/quic/congestion_control/rtt_stats.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/test_tools/mock_clock.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -16,6 +17,9 @@
namespace net {
namespace test {
+// bytes_in_flight is unused by FixRateSender's OnPacketSent.
+QuicByteCount kUnused = 0;
+
class FixRateReceiverPeer : public FixRateReceiver {
public:
FixRateReceiverPeer()
@@ -29,16 +33,14 @@ class FixRateReceiverPeer : public FixRateReceiver {
class FixRateTest : public ::testing::Test {
protected:
FixRateTest()
- : rtt_(QuicTime::Delta::FromMilliseconds(30)),
- sender_(new FixRateSender(&clock_)),
+ : sender_(new FixRateSender(&rtt_stats_)),
receiver_(new FixRateReceiverPeer()),
start_(clock_.Now()) {
// Make sure clock does not start at 0.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2));
}
- const QuicTime::Delta rtt_;
+ RttStats rtt_stats_;
MockClock clock_;
- SendAlgorithmInterface::SentPacketsMap unused_packet_map_;
scoped_ptr<FixRateSender> sender_;
scoped_ptr<FixRateReceiverPeer> receiver_;
const QuicTime start_;
@@ -48,7 +50,7 @@ TEST_F(FixRateTest, ReceiverAPI) {
QuicCongestionFeedbackFrame feedback;
QuicTime timestamp(QuicTime::Zero());
receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(300));
- receiver_->RecordIncomingPacket(1, 1, timestamp, false);
+ receiver_->RecordIncomingPacket(1, 1, timestamp);
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
EXPECT_EQ(kFixRate, feedback.type);
EXPECT_EQ(300000u, feedback.fix_rate.bitrate.ToBytesPerSecond());
@@ -58,65 +60,41 @@ TEST_F(FixRateTest, SenderAPI) {
QuicCongestionFeedbackFrame feedback;
feedback.type = kFixRate;
feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(300);
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- unused_packet_map_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond());
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- sender_->OnPacketSent(clock_.Now(), 1, kDefaultMaxPacketSize,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ sender_->OnPacketSent(clock_.Now(), kUnused, 1, kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA);
+ EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(),
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ clock_.AdvanceTime(sender_->TimeUntilSend(clock_.Now(),
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA));
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- sender_->OnPacketSent(clock_.Now(), 2, kDefaultMaxPacketSize,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- sender_->OnPacketSent(clock_.Now(), 3, 600, NOT_RETRANSMISSION,
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ sender_->OnPacketSent(clock_.Now(), kUnused, 2, kDefaultMaxPacketSize,
HAS_RETRANSMITTABLE_DATA);
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
- sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2));
- EXPECT_EQ(QuicTime::Delta::Infinite(), sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
- sender_->OnPacketAcked(1, kDefaultMaxPacketSize, rtt_);
- sender_->OnPacketAcked(2, kDefaultMaxPacketSize, rtt_);
- sender_->OnPacketAcked(3, 600, rtt_);
+ EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(),
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ // Advance the time twice as much and expect only one packet to be sent.
+ clock_.AdvanceTime(sender_->TimeUntilSend(
+ clock_.Now(),
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).Multiply(2));
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-}
-
-TEST_F(FixRateTest, FixRatePacing) {
- const QuicByteCount packet_size = 1200;
- const QuicBandwidth bitrate = QuicBandwidth::FromKBytesPerSecond(240);
- const int64 num_packets = 200;
- QuicCongestionFeedbackFrame feedback;
- receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(240));
- ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- unused_packet_map_);
- QuicTime acc_advance_time(QuicTime::Zero());
- QuicPacketSequenceNumber sequence_number = 0;
- for (int i = 0; i < num_packets; i += 2) {
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE).IsZero());
- sender_->OnPacketSent(clock_.Now(), sequence_number++, packet_size,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE).IsZero());
- sender_->OnPacketSent(clock_.Now(), sequence_number++, packet_size,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- QuicTime::Delta advance_time = sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- clock_.AdvanceTime(advance_time);
- sender_->OnPacketAcked(sequence_number - 1, packet_size, rtt_);
- sender_->OnPacketAcked(sequence_number - 2, packet_size, rtt_);
- acc_advance_time = acc_advance_time.Add(advance_time);
- }
- EXPECT_EQ(num_packets * packet_size * 1000000 / bitrate.ToBytesPerSecond(),
- static_cast<uint64>(acc_advance_time.Subtract(start_)
- .ToMicroseconds()));
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ sender_->OnPacketSent(clock_.Now(), kUnused, 3, kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA);
+ EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(),
+ kDefaultMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
}
} // namespace test
diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start.cc b/chromium/net/quic/congestion_control/hybrid_slow_start.cc
index 0edb10e86ea..2ae3b503265 100644
--- a/chromium/net/quic/congestion_control/hybrid_slow_start.cc
+++ b/chromium/net/quic/congestion_control/hybrid_slow_start.cc
@@ -6,104 +6,133 @@
#include <algorithm>
+using std::max;
+using std::min;
+
namespace net {
// Note(pwestin): the magic clamping numbers come from the original code in
// tcp_cubic.c.
+const int64 kHybridStartLowWindow = 16;
// Number of delay samples for detecting the increase of delay.
-const int kHybridStartMinSamples = 8;
+const uint32 kHybridStartMinSamples = 8;
const int kHybridStartDelayFactorExp = 4; // 2^4 = 16
-const int kHybridStartDelayMinThresholdUs = 2000;
+// The original paper specifies 2 and 8ms, but those have changed over time.
+const int kHybridStartDelayMinThresholdUs = 4000;
const int kHybridStartDelayMaxThresholdUs = 16000;
HybridSlowStart::HybridSlowStart(const QuicClock* clock)
: clock_(clock),
started_(false),
- found_ack_train_(false),
- found_delay_(false),
+ hystart_found_(NOT_FOUND),
+ last_sent_sequence_number_(0),
round_start_(QuicTime::Zero()),
end_sequence_number_(0),
- last_time_(QuicTime::Zero()),
- sample_count_(0),
- current_rtt_(QuicTime::Delta::Zero()) {
+ last_close_ack_pair_time_(QuicTime::Zero()),
+ rtt_sample_count_(0),
+ current_min_rtt_(QuicTime::Delta::Zero()) {
+}
+
+void HybridSlowStart::OnPacketAcked(
+ QuicPacketSequenceNumber acked_sequence_number, bool in_slow_start) {
+ // OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end
+ // the round when the final packet of the burst is received and start it on
+ // the next incoming ack.
+ if (in_slow_start && IsEndOfRound(acked_sequence_number)) {
+ started_ = false;
+ }
+}
+
+void HybridSlowStart::OnPacketSent(QuicPacketSequenceNumber sequence_number) {
+ last_sent_sequence_number_ = sequence_number;
}
void HybridSlowStart::Restart() {
- found_ack_train_ = false;
- found_delay_ = false;
+ started_ = false;
+ hystart_found_ = NOT_FOUND;
}
-void HybridSlowStart::Reset(QuicPacketSequenceNumber end_sequence_number) {
- DVLOG(1) << "Reset hybrid slow start @" << end_sequence_number;
- round_start_ = last_time_ = clock_->ApproximateNow();
- end_sequence_number_ = end_sequence_number;
- current_rtt_ = QuicTime::Delta::Zero();
- sample_count_ = 0;
+void HybridSlowStart::StartReceiveRound(QuicPacketSequenceNumber last_sent) {
+ DVLOG(1) << "Reset hybrid slow start @" << last_sent;
+ round_start_ = last_close_ack_pair_time_ = clock_->ApproximateNow();
+ end_sequence_number_ = last_sent;
+ current_min_rtt_ = QuicTime::Delta::Zero();
+ rtt_sample_count_ = 0;
started_ = true;
}
-bool HybridSlowStart::EndOfRound(QuicPacketSequenceNumber ack) {
+bool HybridSlowStart::IsEndOfRound(QuicPacketSequenceNumber ack) const {
return end_sequence_number_ <= ack;
}
-void HybridSlowStart::Update(QuicTime::Delta rtt, QuicTime::Delta delay_min) {
- // The original code doesn't invoke this until we hit 16 packet per burst.
- // Since the code handles lower than 16 grecefully and I removed that
- // limit.
- if (found_ack_train_ || found_delay_) {
- return;
+bool HybridSlowStart::ShouldExitSlowStart(QuicTime::Delta latest_rtt,
+ QuicTime::Delta min_rtt,
+ int64 congestion_window) {
+ if (!started_) {
+ // Time to start the hybrid slow start.
+ StartReceiveRound(last_sent_sequence_number_);
+ }
+ if (hystart_found_ != NOT_FOUND) {
+ return true;
}
QuicTime current_time = clock_->ApproximateNow();
// First detection parameter - ack-train detection.
// Since slow start burst out packets we can indirectly estimate the inter-
// arrival time by looking at the arrival time of the ACKs if the ACKs are
- // spread out more then half the minimum RTT packets are beeing spread out
+ // spread out more then half the minimum RTT packets are being spread out
// more than the capacity.
- // This first trigger will not come into play until we hit roughly 4.8 Mbit/s.
+ // This first trigger will not come into play until we hit roughly 9.6 Mbps
+ // with delayed acks (or 4.8Mbps without delayed acks)
+ // TODO(ianswett): QUIC always uses delayed acks, even at the beginning, so
+ // this should likely be at least 4ms.
// TODO(pwestin): we need to make sure our pacing don't trigger this detector.
- if (current_time.Subtract(last_time_).ToMicroseconds() <=
- kHybridStartDelayMinThresholdUs) {
- last_time_ = current_time;
+ // TODO(ianswett): Pacing or other cases could be handled by checking the send
+ // time of the first acked packet in a receive round.
+ if (current_time.Subtract(last_close_ack_pair_time_).ToMicroseconds() <=
+ kHybridStartDelayMinThresholdUs) {
+ last_close_ack_pair_time_ = current_time;
if (current_time.Subtract(round_start_).ToMicroseconds() >=
- (delay_min.ToMicroseconds() >> 1)) {
- found_ack_train_ = true;
+ min_rtt.ToMicroseconds() >> 1) {
+ hystart_found_ = ACK_TRAIN;
}
+ } else if (last_close_ack_pair_time_ == round_start_) {
+ // If the previous ack wasn't close, then move forward the round start time
+ // to the incoming ack.
+ last_close_ack_pair_time_ = round_start_ = current_time;
}
// Second detection parameter - delay increase detection.
- // Compare the minimum delay (current_rtt_) of the current
+ // Compare the minimum delay (current_min_rtt_) of the current
// burst of packets relative to the minimum delay during the session.
// Note: we only look at the first few(8) packets in each burst, since we
// only want to compare the lowest RTT of the burst relative to previous
// bursts.
- sample_count_++;
- if (sample_count_ <= kHybridStartMinSamples) {
- if (current_rtt_.IsZero() || current_rtt_ > rtt) {
- current_rtt_ = rtt;
+ rtt_sample_count_++;
+ if (rtt_sample_count_ <= kHybridStartMinSamples) {
+ if (current_min_rtt_.IsZero() || current_min_rtt_ > latest_rtt) {
+ current_min_rtt_ = latest_rtt;
}
}
- // We only need to check this once.
- if (sample_count_ == kHybridStartMinSamples) {
- int accepted_variance_us = delay_min.ToMicroseconds() >>
+ // We only need to check this once per round.
+ if (rtt_sample_count_ == kHybridStartMinSamples) {
+ // Divide min_rtt by 16 to get a rtt increase threshold for exiting.
+ int min_rtt_increase_threshold_us = min_rtt.ToMicroseconds() >>
kHybridStartDelayFactorExp;
- accepted_variance_us = std::min(accepted_variance_us,
- kHybridStartDelayMaxThresholdUs);
- QuicTime::Delta accepted_variance = QuicTime::Delta::FromMicroseconds(
- std::max(accepted_variance_us, kHybridStartDelayMinThresholdUs));
+ // Ensure the rtt threshold is never less than 2ms or more than 16ms.
+ min_rtt_increase_threshold_us = min(min_rtt_increase_threshold_us,
+ kHybridStartDelayMaxThresholdUs);
+ QuicTime::Delta min_rtt_increase_threshold =
+ QuicTime::Delta::FromMicroseconds(max(min_rtt_increase_threshold_us,
+ kHybridStartDelayMinThresholdUs));
- if (current_rtt_ > delay_min.Add(accepted_variance)) {
- found_delay_ = true;
+ if (current_min_rtt_ > min_rtt.Add(min_rtt_increase_threshold)) {
+ hystart_found_= DELAY;
}
}
-}
-
-bool HybridSlowStart::Exit() {
- // If either one of the two conditions are met we exit from slow start
- // immediately.
- if (found_ack_train_ || found_delay_) {
- return true;
- }
- return false;
+ // Exit from slow start if the cwnd is greater than 16 and an ack train or
+ // increasing delay are found.
+ return congestion_window >= kHybridStartLowWindow &&
+ hystart_found_ != NOT_FOUND;
}
} // namespace net
diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start.h b/chromium/net/quic/congestion_control/hybrid_slow_start.h
index cee9c731257..5d36c53a334 100644
--- a/chromium/net/quic/congestion_control/hybrid_slow_start.h
+++ b/chromium/net/quic/congestion_control/hybrid_slow_start.h
@@ -26,36 +26,61 @@ class NET_EXPORT_PRIVATE HybridSlowStart {
public:
explicit HybridSlowStart(const QuicClock* clock);
+ void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
+ bool in_slow_start);
+
+ void OnPacketSent(QuicPacketSequenceNumber sequence_number);
+
+ // ShouldExitSlowStart should be called on every new ack frame, since a new
+ // RTT measurement can be made then.
+ // rtt: the RTT for this ack packet.
+ // min_rtt: is the lowest delay (RTT) we have seen during the session.
+ // congestion_window: the congestion window in packets.
+ bool ShouldExitSlowStart(QuicTime::Delta rtt,
+ QuicTime::Delta min_rtt,
+ int64 congestion_window);
+
// Start a new slow start phase.
void Restart();
+ // TODO(ianswett): The following methods should be private, but that requires
+ // a follow up CL to update the unit test.
// Returns true if this ack the last sequence number of our current slow start
// round.
// Call Reset if this returns true.
- bool EndOfRound(QuicPacketSequenceNumber ack);
-
- // Call for each round (burst) in the slow start phase.
- void Reset(QuicPacketSequenceNumber end_sequence_number);
+ bool IsEndOfRound(QuicPacketSequenceNumber ack) const;
- // rtt: it the RTT for this ack packet.
- // delay_min: is the lowest delay (RTT) we have seen during the session.
- void Update(QuicTime::Delta rtt, QuicTime::Delta delay_min);
+ // Call for the start of each receive round (burst) in the slow start phase.
+ void StartReceiveRound(QuicPacketSequenceNumber last_sent);
- // Returns true when we should exit slow start.
- bool Exit();
-
- bool started() { return started_; }
+ // Whether slow start has started.
+ bool started() const {
+ return started_;
+ }
private:
+ // Whether a condition for exiting slow start has been found.
+ enum HystartState {
+ NOT_FOUND,
+ ACK_TRAIN, // A closely spaced ack train is too long.
+ DELAY, // Too much increase in the round's min_rtt was observed.
+ };
+
const QuicClock* clock_;
+ // Whether the hybrid slow start has been started.
bool started_;
- bool found_ack_train_;
- bool found_delay_;
- QuicTime round_start_; // Beginning of each slow start round.
- QuicPacketSequenceNumber end_sequence_number_; // End of slow start round.
- QuicTime last_time_; // Last time when the ACK spacing was close.
- uint8 sample_count_; // Number of samples to decide current RTT.
- QuicTime::Delta current_rtt_; // The minimum rtt of current round.
+ HystartState hystart_found_;
+ // Last sequence number sent which was CWND limited.
+ QuicPacketSequenceNumber last_sent_sequence_number_;
+
+ // Variables for tracking acks received during a slow start round.
+ QuicTime round_start_; // Beginning of each slow start receive round.
+ QuicPacketSequenceNumber end_sequence_number_; // End of the receive round.
+ // Last time when the spacing between ack arrivals was less than 2 ms.
+ // Defaults to the beginning of the round.
+ QuicTime last_close_ack_pair_time_;
+ uint32 rtt_sample_count_; // Number of rtt samples in the current round.
+ QuicTime::Delta current_min_rtt_; // The minimum rtt of current round.
DISALLOW_COPY_AND_ASSIGN(HybridSlowStart);
};
diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc b/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc
index 0a9f91fd3f0..91c5d9f2d65 100644
--- a/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc
+++ b/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc
@@ -18,38 +18,40 @@ class HybridSlowStartTest : public ::testing::Test {
rtt_(QuicTime::Delta::FromMilliseconds(60)) {
}
virtual void SetUp() {
- slowStart_.reset(new HybridSlowStart(&clock_));
+ slow_start_.reset(new HybridSlowStart(&clock_));
}
const QuicTime::Delta one_ms_;
const QuicTime::Delta rtt_;
MockClock clock_;
- scoped_ptr<HybridSlowStart> slowStart_;
+ scoped_ptr<HybridSlowStart> slow_start_;
};
TEST_F(HybridSlowStartTest, Simple) {
QuicPacketSequenceNumber sequence_number = 1;
QuicPacketSequenceNumber end_sequence_number = 3;
- slowStart_->Reset(end_sequence_number);
+ slow_start_->StartReceiveRound(end_sequence_number);
- EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++));
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++));
// Test duplicates.
- EXPECT_FALSE(slowStart_->EndOfRound(sequence_number));
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number));
- EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++));
- EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++));
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++));
+ EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++));
// Test without a new registered end_sequence_number;
- EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++));
+ EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++));
end_sequence_number = 20;
- slowStart_->Reset(end_sequence_number);
+ slow_start_->StartReceiveRound(end_sequence_number);
while (sequence_number < end_sequence_number) {
- EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++));
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++));
}
- EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++));
+ EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++));
}
+// TODO(ianswett): Add tests which more realistically invoke the methods,
+// simulating how actual acks arrive and packets are sent.
TEST_F(HybridSlowStartTest, AckTrain) {
// At a typical RTT 60 ms, assuming that the inter arrival is 1 ms,
// we expect to be able to send a burst of 30 packet before we trigger the
@@ -58,24 +60,22 @@ TEST_F(HybridSlowStartTest, AckTrain) {
QuicPacketSequenceNumber sequence_number = 2;
QuicPacketSequenceNumber end_sequence_number = 2;
for (int burst = 0; burst < kMaxLoopCount; ++burst) {
- slowStart_->Reset(end_sequence_number);
+ slow_start_->StartReceiveRound(end_sequence_number);
do {
clock_.AdvanceTime(one_ms_);
- slowStart_->Update(rtt_, rtt_);
- EXPECT_FALSE(slowStart_->Exit());
- } while (!slowStart_->EndOfRound(sequence_number++));
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100));
+ } while (!slow_start_->IsEndOfRound(sequence_number++));
end_sequence_number *= 2; // Exponential growth.
}
- slowStart_->Reset(end_sequence_number);
+ slow_start_->StartReceiveRound(end_sequence_number);
- for (int n = 0; n < 29 && !slowStart_->EndOfRound(sequence_number++); ++n) {
+ for (int n = 0;
+ n < 29 && !slow_start_->IsEndOfRound(sequence_number++); ++n) {
clock_.AdvanceTime(one_ms_);
- slowStart_->Update(rtt_, rtt_);
- EXPECT_FALSE(slowStart_->Exit());
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100));
}
clock_.AdvanceTime(one_ms_);
- slowStart_->Update(rtt_, rtt_);
- EXPECT_TRUE(slowStart_->Exit());
+ EXPECT_TRUE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100));
}
TEST_F(HybridSlowStartTest, Delay) {
@@ -84,24 +84,23 @@ TEST_F(HybridSlowStartTest, Delay) {
const int kHybridStartMinSamples = 8; // Number of acks required to trigger.
QuicPacketSequenceNumber end_sequence_number = 1;
- slowStart_->Reset(end_sequence_number++);
+ slow_start_->StartReceiveRound(end_sequence_number++);
// Will not trigger since our lowest RTT in our burst is the same as the long
// term RTT provided.
for (int n = 0; n < kHybridStartMinSamples; ++n) {
- slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(n)), rtt_);
- EXPECT_FALSE(slowStart_->Exit());
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(
+ rtt_.Add(QuicTime::Delta::FromMilliseconds(n)), rtt_, 100));
}
- slowStart_->Reset(end_sequence_number++);
+ slow_start_->StartReceiveRound(end_sequence_number++);
for (int n = 1; n < kHybridStartMinSamples; ++n) {
- slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(n + 4)),
- rtt_);
- EXPECT_FALSE(slowStart_->Exit());
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(
+ rtt_.Add(QuicTime::Delta::FromMilliseconds(n + 5)), rtt_, 100));
}
// Expect to trigger since all packets in this burst was above the long term
// RTT provided.
- slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(4)), rtt_);
- EXPECT_TRUE(slowStart_->Exit());
+ EXPECT_TRUE(slow_start_->ShouldExitSlowStart(
+ rtt_.Add(QuicTime::Delta::FromMilliseconds(5)), rtt_, 100));
}
} // namespace test
diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc
deleted file mode 100644
index 2438493cc0b..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h"
-
-#include <algorithm>
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "net/quic/congestion_control/cube_root.h"
-#include "net/quic/quic_protocol.h"
-
-namespace {
-// The following constants are in 2^10 fractions of a second instead of ms to
-// allow a 10 shift right to divide.
-const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3)
- // where 0.100 is 100 ms which is the scaling
- // round trip time.
-// TODO(pwestin): Tuning parameter, currently close to TCP cubic at 100ms RTT.
-const int kPacedCubeScale = 6000;
-const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) / kPacedCubeScale;
-} // namespace
-
-namespace net {
-
-InterArrivalBitrateRampUp::InterArrivalBitrateRampUp(const QuicClock* clock)
- : clock_(clock),
- current_rate_(QuicBandwidth::Zero()),
- channel_estimate_(QuicBandwidth::Zero()),
- available_channel_estimate_(QuicBandwidth::Zero()),
- halfway_point_(QuicBandwidth::Zero()),
- epoch_(QuicTime::Zero()),
- last_update_time_(QuicTime::Zero()) {
-}
-
-void InterArrivalBitrateRampUp::Reset(QuicBandwidth new_rate,
- QuicBandwidth available_channel_estimate,
- QuicBandwidth channel_estimate) {
- epoch_ = clock_->ApproximateNow();
- last_update_time_ = epoch_;
- available_channel_estimate_ = std::max(new_rate, available_channel_estimate);
- channel_estimate_ = std::max(channel_estimate, available_channel_estimate_);
-
- halfway_point_ = available_channel_estimate_.Add(
- (channel_estimate_.Subtract(available_channel_estimate_)).Scale(0.5f));
-
- if (new_rate < available_channel_estimate_) {
- time_to_origin_point_ = CalcuateTimeToOriginPoint(
- available_channel_estimate_.Subtract(new_rate));
- } else if (new_rate >= channel_estimate_) {
- time_to_origin_point_ = 0;
- } else if (new_rate >= halfway_point_) {
- time_to_origin_point_ =
- CalcuateTimeToOriginPoint(channel_estimate_.Subtract(new_rate));
- } else {
- time_to_origin_point_ = CalcuateTimeToOriginPoint(
- new_rate.Subtract(available_channel_estimate_));
- }
- current_rate_ = new_rate;
- DVLOG(1) << "Reset; time to origin point:" << time_to_origin_point_;
-}
-
-void InterArrivalBitrateRampUp::UpdateChannelEstimate(
- QuicBandwidth channel_estimate) {
- if (available_channel_estimate_ > channel_estimate ||
- current_rate_ > channel_estimate ||
- channel_estimate_ == channel_estimate) {
- // Ignore, because one of the following reasons:
- // 1) channel estimate is bellow our current available estimate which we
- // value higher that this estimate.
- // 2) channel estimate is bellow our current send rate.
- // 3) channel estimate has not changed.
- return;
- }
- if (available_channel_estimate_ == halfway_point_ &&
- channel_estimate_ == halfway_point_) {
- // First time we get a usable channel estimate.
- channel_estimate_ = channel_estimate;
- halfway_point_ = available_channel_estimate_.Add(
- (channel_estimate_.Subtract(available_channel_estimate_).Scale(0.5f)));
- DVLOG(1) << "UpdateChannelEstimate; first usable value:"
- << channel_estimate.ToKBitsPerSecond() << " Kbits/s";
- return;
- }
- if (current_rate_ < halfway_point_) {
- // Update channel estimate without recalculating if we are bellow the
- // halfway point.
- channel_estimate_ = channel_estimate;
- return;
- }
- // We are between halfway point and our channel_estimate.
- epoch_ = clock_->ApproximateNow();
- last_update_time_ = epoch_;
- channel_estimate_ = channel_estimate;
-
- time_to_origin_point_ =
- CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate_));
-
- DVLOG(1) << "UpdateChannelEstimate; time to origin point:"
- << time_to_origin_point_;
-}
-
-QuicBandwidth InterArrivalBitrateRampUp::GetNewBitrate(
- QuicBandwidth sent_bitrate) {
- DCHECK(epoch_.IsInitialized());
- QuicTime current_time = clock_->ApproximateNow();
- // Cubic is "independent" of RTT, the update is limited by the time elapsed.
- if (current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval()) {
- return current_rate_;
- }
- QuicTime::Delta time_from_last_update =
- current_time.Subtract(last_update_time_);
-
- last_update_time_ = current_time;
-
- if (!sent_bitrate.IsZero() &&
- sent_bitrate.Add(sent_bitrate) < current_rate_) {
- // Don't go up in bitrate when we are not sending.
- // We need to update the epoch to reflect this state.
- epoch_ = epoch_.Add(time_from_last_update);
- DVLOG(1) << "Don't increase; our sent bitrate is:"
- << sent_bitrate.ToKBitsPerSecond() << " Kbits/s"
- << " current target rate is:"
- << current_rate_.ToKBitsPerSecond() << " Kbits/s";
- return current_rate_;
- }
- QuicTime::Delta time_from_epoch = current_time.Subtract(epoch_);
-
- // Change the time unit from microseconds to 2^10 fractions per second. This
- // is done to allow us to use shift as a divide operator.
- int64 elapsed_time = (time_from_epoch.ToMicroseconds() << 10) /
- kNumMicrosPerSecond;
-
- int64 offset = time_to_origin_point_ - elapsed_time;
- // Note: using int64 since QuicBandwidth can't be negative
- int64 delta_pace_kbps = (kPacedCubeScale * offset * offset * offset) >>
- kCubeScale;
-
- bool start_bellow_halfway_point = false;
- if (current_rate_ < halfway_point_) {
- start_bellow_halfway_point = true;
-
- // available_channel_estimate_ is the orgin of the cubic function.
- QuicBandwidth current_rate = QuicBandwidth::FromBytesPerSecond(
- available_channel_estimate_.ToBytesPerSecond() -
- (delta_pace_kbps << 10));
-
- if (start_bellow_halfway_point && current_rate >= halfway_point_) {
- // We passed the halfway point, recalculate with new orgin.
- epoch_ = clock_->ApproximateNow();
- // channel_estimate_ is the new orgin of the cubic function.
- if (current_rate >= channel_estimate_) {
- time_to_origin_point_ = 0;
- } else {
- time_to_origin_point_ =
- CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate));
- }
- DVLOG(1) << "Passed the halfway point; time to origin point:"
- << time_to_origin_point_;
- }
- current_rate_ = current_rate;
- } else {
- // channel_estimate_ is the orgin of the cubic function.
- current_rate_ = QuicBandwidth::FromBytesPerSecond(
- channel_estimate_.ToBytesPerSecond() - (delta_pace_kbps << 10));
- }
- return current_rate_;
-}
-
-uint32 InterArrivalBitrateRampUp::CalcuateTimeToOriginPoint(
- QuicBandwidth rate_difference) const {
- return CubeRoot::Root(kCubeFactor * rate_difference.ToKBytesPerSecond());
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h
deleted file mode 100644
index 931992391a5..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Ramp up bitrate from a start point normally our "current_rate" as long as we
-// have no packet loss or delay events.
-// The first half of the ramp up curve follows a cubic function with its orgin
-// at the estimated available bandwidth, onece the bitrate pass the halfway
-// point between the estimated available bandwidth and the estimated max
-// bandwidth it will follw a new cubic function with its orgin at the estimated
-// max bandwidth.
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_
-
-#include "base/basictypes.h"
-#include "net/base/net_export.h"
-#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_clock.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-class NET_EXPORT_PRIVATE InterArrivalBitrateRampUp {
- public:
- explicit InterArrivalBitrateRampUp(const QuicClock* clock);
-
- // Call after a decision to lower the bitrate and after a probe.
- void Reset(QuicBandwidth current_rate,
- QuicBandwidth available_channel_estimate,
- QuicBandwidth channel_estimate);
-
- // Call everytime we get a new channel estimate.
- void UpdateChannelEstimate(QuicBandwidth channel_estimate);
-
- // Compute a new send pace to use.
- QuicBandwidth GetNewBitrate(QuicBandwidth sent_bitrate);
-
- private:
- uint32 CalcuateTimeToOriginPoint(QuicBandwidth rate_difference) const;
-
- static const QuicTime::Delta MaxCubicTimeInterval() {
- return QuicTime::Delta::FromMilliseconds(30);
- }
-
- const QuicClock* clock_;
-
- QuicBandwidth current_rate_;
- QuicBandwidth channel_estimate_;
- QuicBandwidth available_channel_estimate_;
- QuicBandwidth halfway_point_;
-
- // Time when this cycle started, after a Reset.
- QuicTime epoch_;
-
- // Time when we updated current_rate_.
- QuicTime last_update_time_;
-
- // Time to origin point of cubic function in 2^10 fractions of a second.
- uint32 time_to_origin_point_;
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalBitrateRampUp);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc
deleted file mode 100644
index acae78d2056..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc
+++ /dev/null
@@ -1,404 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class InterArrivalBitrateRampUpTest : public ::testing::Test {
- protected:
- InterArrivalBitrateRampUpTest()
- : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
- hundred_ms_(QuicTime::Delta::FromMilliseconds(100)),
- bitrate_ramp_up_(&clock_) {
- }
- virtual void SetUp() {
- clock_.AdvanceTime(one_ms_);
- }
- const QuicTime::Delta one_ms_;
- const QuicTime::Delta hundred_ms_;
- MockClock clock_;
- InterArrivalBitrateRampUp bitrate_ramp_up_;
-};
-
-TEST_F(InterArrivalBitrateRampUpTest, GoodEstimates) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
- QuicBandwidth available_channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // First concave growth, towards available_channel_estimate.
- for (int i = 0; i < 25; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(available_channel_estimate, sent_bitrate);
- EXPECT_LE(start_rate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
-
- // First convex growth, from available_channel_estimate.
- for (int j = 0; j < 25; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-
- // Second concave growth, towards channel_estimate.
- for (int i = 0; i < 24; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
-
- // Second convex growth, from channel_estimate.
- for (int j = 0; j < 25; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000,
- sent_bitrate.ToBytesPerSecond(), 10000);
-
- // Verify that we increase cubic.
- for (int j = 0; j < 23; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 750000,
- sent_bitrate.ToBytesPerSecond(), 10000);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, GoodEstimatesLimitedSendRate) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
- QuicBandwidth available_channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth max_sent_rate =
- QuicBandwidth::FromKBytesPerSecond(125);
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // First concave growth, towards available_channel_estimate.
- // Should pass without being affected by the max_sent_rate.
- for (int i = 0; i < 25; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- // Cap our previus sent rate.
- sent_bitrate = std::min(sent_bitrate, max_sent_rate);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(available_channel_estimate, sent_bitrate);
- EXPECT_LE(start_rate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- // Cap our previus sent rate.
- sent_bitrate = std::min(sent_bitrate, max_sent_rate);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
-
- // First convex growth, from available_channel_estimate.
- for (int j = 0; j < 25; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- // Cap our previus sent rate.
- sent_bitrate = std::min(sent_bitrate, max_sent_rate);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = std::min(sent_bitrate, max_sent_rate);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- // We expect 2 * sent_bitrate to cap the rate.
- EXPECT_LE(max_sent_rate.Add(max_sent_rate), sent_bitrate);
- // Remove our sent cap.
- // Expect bitrate to continue to ramp from its previous rate.
- for (int j = 0; j < 5; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_LE(max_sent_rate.Add(max_sent_rate), sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, GoodEstimatesCloseToChannelEstimate) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
- QuicBandwidth available_channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // First concave growth, towards available_channel_estimate.
- for (int i = 0; i < 25; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(available_channel_estimate, sent_bitrate);
- EXPECT_LE(start_rate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
-
- // First convex growth, from available_channel_estimate.
- for (int j = 0; j < 15; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-
- // Second concave growth, towards channel_estimate.
- for (int i = 0; i < 14; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
-
- // Second convex growth, from channel_estimate.
- for (int j = 0; j < 25; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000,
- sent_bitrate.ToBytesPerSecond(), 10000);
-
- // Verify that we increase cubic.
- for (int j = 0; j < 24; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 780000,
- sent_bitrate.ToBytesPerSecond(), 20000);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, UncertainEstimates) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
- QuicBandwidth available_channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(400 * 0.7f);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // First concave growth, towards available_channel_estimate.
- for (int i = 0; i < 20; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(available_channel_estimate, sent_bitrate);
- EXPECT_LE(start_rate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
-
- // First convex growth, from available_channel_estimate.
- for (int j = 0; j < 23; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-
- // Second concave growth, towards channel_estimate.
- for (int i = 0; i < 12; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
-
- // Second convex growth, from channel_estimate.
- for (int j = 0; j < 30; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000,
- sent_bitrate.ToBytesPerSecond(), 10000);
-
- // Verify that we increase cubic.
- for (int j = 0; j < 23; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 750000,
- sent_bitrate.ToBytesPerSecond(), 20000);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, UnknownEstimates) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
- QuicBandwidth available_channel_estimate =
- QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400);
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- available_channel_estimate);
-
- // First convex growth, from start_rate.
- for (int j = 0; j < 20; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(start_rate, sent_bitrate);
- EXPECT_GE(available_channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(available_channel_estimate.ToBytesPerSecond(),
- sent_bitrate.ToBytesPerSecond(), 10000);
-
- // Verify that we increase cubic.
- for (int j = 0; j < 31; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_NEAR(channel_estimate.ToBytesPerSecond(),
- sent_bitrate.ToBytesPerSecond(), 10000);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, UpdatingChannelEstimateHigher) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth available_channel_estimate = start_rate;
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // Convex growth, from available_channel_estimate.
- for (int j = 0; j < 16; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-
- // Increse channel estimate.
- channel_estimate = QuicBandwidth::FromKBytesPerSecond(300);
- bitrate_ramp_up_.UpdateChannelEstimate(channel_estimate);
-
- // Concave growth, towards channel_estimate.
- for (int i = 0; i < 22; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
-}
-
-TEST_F(InterArrivalBitrateRampUpTest, UpdatingChannelEstimateLower) {
- QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(200);
- QuicBandwidth available_channel_estimate = start_rate;
- QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250);
- QuicBandwidth halfway_point = available_channel_estimate.Add(
- channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
- QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
- bitrate_ramp_up_.Reset(start_rate,
- available_channel_estimate,
- channel_estimate);
-
- // Convex growth, from available_channel_estimate.
- for (int j = 0; j < 16; ++j) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(available_channel_estimate, sent_bitrate);
- EXPECT_GE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
-
- // Decrese channel estimate.
- channel_estimate = QuicBandwidth::FromKBytesPerSecond(240);
- bitrate_ramp_up_.UpdateChannelEstimate(channel_estimate);
-
- // Concave growth, towards channel_estimate.
- for (int i = 0; i < 11; ++i) {
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_GE(channel_estimate, sent_bitrate);
- EXPECT_LE(halfway_point, sent_bitrate);
- }
- clock_.AdvanceTime(hundred_ms_);
- sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
- EXPECT_LE(channel_estimate, sent_bitrate);
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc
deleted file mode 100644
index 5e500b371bc..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_overuse_detector.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include <algorithm>
-
-// Initial noise variance, equal to a standard deviation of 1 millisecond.
-static const float kInitialVarianceNoise = 1000000.0;
-
-// Minimum variance of the time delta.
-static const int kMinVarianceDelta = 10000;
-
-// Threshold for accumulated delta.
-static const int kThresholdAccumulatedDeltasUs = 1000;
-
-// Same as above, described as numerator and denominator.
-static const int kBetaNumerator = 49;
-static const int kBetaDenominator = 50;
-
-// Trigger a signal when the accumulated time drift is larger than
-// 5 x standard deviation.
-// A lower value triggers earlier with more false detect as a side effect.
-static const int kDetectDriftStandardDeviation = 5;
-
-// Trigger an overuse when the time difference between send time and receive
-// is larger than 7 x standard deviation.
-// A lower value triggers earlier with more false detect as a side effect.
-static const float kDetectTimeDiffStandardDeviation = 7;
-
-// Trigger an overuse when the mean of the time difference diverges too far
-// from 0.
-// A higher value trigger earlier with more false detect as a side effect.
-static const int kDetectSlopeFactor = 14;
-
-// We need to get some initial statistics before the detection can start.
-static const int kMinSamplesBeforeDetect = 10;
-
-namespace net {
-
-InterArrivalOveruseDetector::InterArrivalOveruseDetector()
- : last_sequence_number_(0),
- num_of_deltas_(0),
- accumulated_deltas_(QuicTime::Delta::Zero()),
- delta_mean_(0.0),
- delta_variance_(kInitialVarianceNoise),
- delta_overuse_counter_(0),
- delta_estimate_(kBandwidthSteady),
- slope_overuse_counter_(0),
- slope_estimate_(kBandwidthSteady),
- send_receive_offset_(QuicTime::Delta::Infinite()),
- estimated_congestion_delay_(QuicTime::Delta::Zero()) {
-}
-
-void InterArrivalOveruseDetector::OnAcknowledgedPacket(
- QuicPacketSequenceNumber sequence_number,
- QuicTime send_time,
- bool last_of_send_time,
- QuicTime receive_time) {
- if (last_sequence_number_ >= sequence_number) {
- // This is an old packet and should be ignored. Note that we are called
- // with a full 64 bit sequence number, even if the wire format may only
- // convey some low-order bits of that number.
- DVLOG(1) << "Skip old packet";
- return;
- }
-
- last_sequence_number_ = sequence_number;
-
- if (current_packet_group_.send_time != send_time) {
- // First value in this group. If the last packet of a group is lost we
- // overwrite the old value and start over with a new measurement.
- current_packet_group_.send_time = send_time;
- // The receive_time value might not be first in a packet burst if that
- // packet was lost, however we will still use it in this calculation.
- UpdateSendReceiveTimeOffset(receive_time.Subtract(send_time));
- }
- if (!last_of_send_time) {
- // We expect more packet with this send time.
- return;
- }
- // First packet of a later group, the previous group sample is ready.
- if (previous_packet_group_.send_time.IsInitialized()) {
- QuicTime::Delta sent_delta = send_time.Subtract(
- previous_packet_group_.send_time);
- QuicTime::Delta receive_delta = receive_time.Subtract(
- previous_packet_group_.last_receive_time);
- // We assume that groups of packets are sent together as bursts (tagged
- // with identical send times) because it is too computationally expensive
- // to pace them out individually. The received_delta is then the total
- // delta between the receipt of the last packet of the previous group, and
- // the last packet of the current group. Assuming we are transmitting
- // these bursts on average at the available bandwidth rate, there should be
- // no change in overall spread (both deltas should be the same).
- UpdateFilter(receive_delta, sent_delta);
- }
- // Save current as previous.
- previous_packet_group_ = current_packet_group_;
- previous_packet_group_.last_receive_time = receive_time;
-}
-
-void InterArrivalOveruseDetector::UpdateSendReceiveTimeOffset(
- QuicTime::Delta offset) {
- // Note the send and receive time can have a randomly large offset, however
- // they are stable in relation to each other, hence no or extremely low clock
- // drift relative to the duration of our stream.
- if (offset.ToMicroseconds() < send_receive_offset_.ToMicroseconds()) {
- send_receive_offset_ = offset;
- }
- estimated_congestion_delay_ = offset.Subtract(send_receive_offset_);
-}
-
-BandwidthUsage InterArrivalOveruseDetector::GetState(
- QuicTime::Delta* estimated_congestion_delay) {
- *estimated_congestion_delay = estimated_congestion_delay_;
- int64 sigma_delta = sqrt(static_cast<double>(delta_variance_));
- DetectSlope(sigma_delta);
- DetectDrift(sigma_delta);
- return std::max(slope_estimate_, delta_estimate_);
-}
-
-void InterArrivalOveruseDetector::UpdateFilter(QuicTime::Delta received_delta,
- QuicTime::Delta sent_delta) {
- ++num_of_deltas_;
- QuicTime::Delta time_diff = received_delta.Subtract(sent_delta);
- UpdateDeltaEstimate(time_diff);
- accumulated_deltas_ = accumulated_deltas_.Add(time_diff);
-}
-
-void InterArrivalOveruseDetector::UpdateDeltaEstimate(
- QuicTime::Delta residual) {
- DCHECK_EQ(1, kBetaDenominator - kBetaNumerator);
- int64 residual_us = residual.ToMicroseconds();
- delta_mean_ =
- (kBetaNumerator * delta_mean_ + residual_us) / kBetaDenominator;
- delta_variance_ =
- (kBetaNumerator * delta_variance_ +
- (delta_mean_ - residual_us) * (delta_mean_ - residual_us)) /
- kBetaDenominator;
-
- if (delta_variance_ < kMinVarianceDelta) {
- delta_variance_ = kMinVarianceDelta;
- }
-}
-
-void InterArrivalOveruseDetector::DetectDrift(int64 sigma_delta) {
- // We have 2 drift detectors. The accumulate of deltas and the absolute time
- // differences.
- if (num_of_deltas_ < kMinSamplesBeforeDetect) {
- return;
- }
- if (delta_overuse_counter_ > 0 &&
- accumulated_deltas_.ToMicroseconds() > kThresholdAccumulatedDeltasUs) {
- if (delta_estimate_ != kBandwidthDraining) {
- DVLOG(1) << "Bandwidth estimate drift: Draining buffer(s) "
- << accumulated_deltas_.ToMilliseconds() << " ms";
- delta_estimate_ = kBandwidthDraining;
- }
- return;
- }
- if ((sigma_delta * kDetectTimeDiffStandardDeviation >
- estimated_congestion_delay_.ToMicroseconds()) &&
- (sigma_delta * kDetectDriftStandardDeviation >
- abs(accumulated_deltas_.ToMicroseconds()))) {
- if (delta_estimate_ != kBandwidthSteady) {
- DVLOG(1) << "Bandwidth estimate drift: Steady"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta
- << " offset:" << send_receive_offset_.ToMicroseconds()
- << " delta:" << estimated_congestion_delay_.ToMicroseconds()
- << " drift:" << accumulated_deltas_.ToMicroseconds();
- delta_estimate_ = kBandwidthSteady;
- // Reset drift counter.
- accumulated_deltas_ = QuicTime::Delta::Zero();
- delta_overuse_counter_ = 0;
- }
- return;
- }
- if (accumulated_deltas_.ToMicroseconds() > 0) {
- if (delta_estimate_ != kBandwidthOverUsing) {
- ++delta_overuse_counter_;
- DVLOG(1) << "Bandwidth estimate drift: Over using"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta
- << " offset:" << send_receive_offset_.ToMicroseconds()
- << " delta:" << estimated_congestion_delay_.ToMicroseconds()
- << " drift:" << accumulated_deltas_.ToMicroseconds();
- delta_estimate_ = kBandwidthOverUsing;
- }
- } else {
- if (delta_estimate_ != kBandwidthUnderUsing) {
- --delta_overuse_counter_;
- DVLOG(1) << "Bandwidth estimate drift: Under using"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta
- << " offset:" << send_receive_offset_.ToMicroseconds()
- << " delta:" << estimated_congestion_delay_.ToMicroseconds()
- << " drift:" << accumulated_deltas_.ToMicroseconds();
- delta_estimate_ = kBandwidthUnderUsing;
- }
- // Adding decay of negative accumulated_deltas_ since it could be caused by
- // a starting with full buffers. This way we will always converge to 0.
- accumulated_deltas_ = accumulated_deltas_.Add(
- QuicTime::Delta::FromMicroseconds(sigma_delta >> 3));
- }
-}
-
-void InterArrivalOveruseDetector::DetectSlope(int64 sigma_delta) {
- // We use the mean change since it has a constant expected mean 0
- // regardless of number of packets and spread. It is also safe to use during
- // packet loss, since a lost packet only results in a missed filter update
- // not a drift.
- if (num_of_deltas_ < kMinSamplesBeforeDetect) {
- return;
- }
- if (slope_overuse_counter_ > 0 && delta_mean_ > 0) {
- if (slope_estimate_ != kBandwidthDraining) {
- DVLOG(1) << "Bandwidth estimate slope: Draining buffer(s)";
- }
- slope_estimate_ = kBandwidthDraining;
- return;
- }
- if (sigma_delta > abs(delta_mean_) * kDetectSlopeFactor) {
- if (slope_estimate_ != kBandwidthSteady) {
- DVLOG(1) << "Bandwidth estimate slope: Steady"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta;
- slope_overuse_counter_ = 0;
- slope_estimate_ = kBandwidthSteady;
- }
- return;
- }
- if (delta_mean_ > 0) {
- if (slope_estimate_ != kBandwidthOverUsing) {
- ++slope_overuse_counter_;
- DVLOG(1) << "Bandwidth estimate slope: Over using"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta;
- slope_estimate_ = kBandwidthOverUsing;
- }
- } else {
- if (slope_estimate_ != kBandwidthUnderUsing) {
- --slope_overuse_counter_;
- DVLOG(1) << "Bandwidth estimate slope: Under using"
- << " mean:" << delta_mean_
- << " sigma:" << sigma_delta;
- slope_estimate_ = kBandwidthUnderUsing;
- }
- }
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h
deleted file mode 100644
index 185236279bb..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This class is a helper class to the inter arrival congestion control. It
-// provide a signal to the inter arrival congestion control of the estimated
-// state of our transport channel. The estimate is based on the inter arrival
-// time of the received packets relative to the time those packets were sent;
-// we can estimate the build up of buffers on the network before packets are
-// lost.
-//
-// Note: this class is not thread-safe.
-
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_
-
-#include "base/basictypes.h"
-#include "net/base/net_export.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-enum NET_EXPORT_PRIVATE RateControlRegion {
- kRateControlRegionUnknown = 0,
- kRateControlRegionUnderMax = 1,
- kRateControlRegionNearMax = 2
-};
-
-// Note: Order is important.
-enum NET_EXPORT_PRIVATE BandwidthUsage {
- kBandwidthSteady = 0,
- kBandwidthUnderUsing = 1,
- kBandwidthDraining = 2,
- kBandwidthOverUsing = 3,
-};
-
-// Normal state transition diagram
-//
-// kBandwidthUnderUsing
-// |
-// |
-// kBandwidthSteady
-// | ^
-// | |
-// kBandwidthOverUsing |
-// | |
-// | |
-// kBandwidthDraining
-//
-// The above transitions is in normal operation, with extreme values we don't
-// enforce the state transitions, hence you could in extreme scenarios go
-// between any states.
-//
-// kBandwidthSteady When the packets arrive in the same pace as we sent
-// them. In this state we can increase our send pace.
-//
-// kBandwidthOverUsing When the packets arrive slower than the pace we sent
-// them. In this state we should decrease our send pace.
-// When we enter into this state we will also get an
-// estimate on how much delay we have built up. The
-// reduction in send pace should be chosen to drain the
-// built up delay within reasonable time.
-//
-// kBandwidthUnderUsing When the packets arrive faster than the pace we sent
-// them. In this state another stream disappeared from
-// a shared link leaving us more available bandwidth.
-// In this state we should hold our pace to make sure we
-// fully drain the buffers before we start increasing
-// our send rate. We do this to avoid operating with
-// semi-full buffers.
-//
-// kBandwidthDraining We can only be in this state after we have been in a
-// overuse state. In this state we should hold our pace
-// to make sure we fully drain the buffers before we
-// start increasing our send rate. We do this to avoid
-// operating with semi-full buffers.
-
-class NET_EXPORT_PRIVATE InterArrivalOveruseDetector {
- public:
- InterArrivalOveruseDetector();
-
- // Update the statistics with the received delta times, call for every
- // received delta time. This function assumes that there is no re-orderings.
- // If multiple packets are sent at the same time (identical send_time)
- // last_of_send_time should be set to false for all but the last calls to
- // this function. If there is only one packet sent at a given time
- // last_of_send_time must be true.
- // received_delta is the time difference between receiving this packet and the
- // previously received packet.
- void OnAcknowledgedPacket(QuicPacketSequenceNumber sequence_number,
- QuicTime send_time,
- bool last_of_send_time,
- QuicTime receive_time);
-
- // Get the current estimated state and update the estimated congestion delay.
- // |estimated_congestion_delay| will be updated with the estimated built up
- // buffer delay; it must not be NULL as it will be updated with the estimate.
- // Note 1: estimated_buffer_delay will only be valid when kBandwidthOverUsing
- // is returned.
- // Note 2: it's assumed that the pacer lower its send pace to drain the
- // built up buffer within reasonable time. The pacer should use the
- // estimated_buffer_delay as a guidance on how much to back off.
- // Note 3: The absolute value of estimated_congestion_delay is less reliable
- // than the state itself. It is also biased to low since we can't know
- // how full the buffers are when the flow starts.
- BandwidthUsage GetState(QuicTime::Delta* estimated_congestion_delay);
-
- private:
- struct PacketGroup {
- PacketGroup()
- : send_time(QuicTime::Zero()),
- last_receive_time(QuicTime::Zero()) {
- }
- QuicTime send_time;
- QuicTime last_receive_time;
- };
-
- // Update the statistics with the absolute receive time relative to the
- // absolute send time.
- void UpdateSendReceiveTimeOffset(QuicTime::Delta offset);
-
- // Update the filter with this new data point.
- void UpdateFilter(QuicTime::Delta received_delta,
- QuicTime::Delta sent_delta);
-
- // Update the estimate with this residual.
- void UpdateDeltaEstimate(QuicTime::Delta residual);
-
- // Estimate the state based on the slope of the changes.
- void DetectSlope(int64 sigma_delta);
-
- // Estimate the state based on the accumulated drift of the changes.
- void DetectDrift(int64 sigma_delta);
-
- // Current grouping of packets that were sent at the same time.
- PacketGroup current_packet_group_;
- // Grouping of packets that were sent at the same time, just before the
- // current_packet_group_ above.
- PacketGroup previous_packet_group_;
- // Sequence number of the last acknowledged packet.
- QuicPacketSequenceNumber last_sequence_number_;
- // Number of received delta times with unique send time.
- int num_of_deltas_;
- // Estimated accumulation of received delta times.
- // Note: Can be negative and can drift over time which is why we bias it
- // towards 0 and reset it given some triggers.
- QuicTime::Delta accumulated_deltas_;
- // Current running mean of our received delta times.
- int delta_mean_;
- // Current running variance of our received delta times.
- int64 delta_variance_;
- // Number of overuse signals currently triggered in this state.
- // Note: negative represent underuse.
- int delta_overuse_counter_;
- // State estimated by the delta times.
- BandwidthUsage delta_estimate_;
- // Number of overuse signals currently triggered in this state.
- // Note: negative represent underuse.
- int slope_overuse_counter_;
- // State estimated by the slope of the delta times.
- BandwidthUsage slope_estimate_;
- // Lowest offset between send and receive time ever received in this session.
- QuicTime::Delta send_receive_offset_;
- // Last received time difference between our normalized send and receive time.
- QuicTime::Delta estimated_congestion_delay_;
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalOveruseDetector);
-};
-
-} // namespace net
-
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc
deleted file mode 100644
index 8d37749a577..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc
+++ /dev/null
@@ -1,1114 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdlib.h>
-
-#include <cmath>
-
-#include "base/logging.h"
-#include "base/rand_util.h"
-#include "net/quic/congestion_control/inter_arrival_overuse_detector.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-static const double kPi = 3.14159265;
-
-namespace net {
-namespace test {
-
-class InterArrivalOveruseDetectorTest : public ::testing::Test {
- protected:
- InterArrivalOveruseDetectorTest();
-
- QuicTime::Delta GaussianRandom(QuicTime::Delta mean,
- QuicTime::Delta standard_deviation);
-
- int Run100000Samples(int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation);
-
- int RunUntilOveruse(int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst,
- QuicTime::Delta *estimated_buffer_delay);
-
- int RunUntilSteady(int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst);
-
- int RunUntilNotDraining(int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst);
-
- int RunUntilUnderusing(int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst);
-
- void RunXBursts(int bursts,
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst);
-
- QuicPacketSequenceNumber sequence_number_;
- MockClock send_clock_;
- MockClock receive_clock_;
- QuicTime::Delta drift_from_mean_;
- InterArrivalOveruseDetector overuse_detector_;
- unsigned int seed_;
-};
-
-InterArrivalOveruseDetectorTest::InterArrivalOveruseDetectorTest()
- : sequence_number_(1),
- drift_from_mean_(QuicTime::Delta::Zero()),
- seed_(1234) {
-}
-
-QuicTime::Delta InterArrivalOveruseDetectorTest::GaussianRandom(
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation) {
- // Creating a Normal distribution variable from two independent uniform
- // variables based on the Box-Muller transform.
- double uniform1 = base::RandDouble();
- double uniform2 = base::RandDouble();
-
- QuicTime::Delta random = QuicTime::Delta::FromMicroseconds(
- static_cast<int>(standard_deviation.ToMicroseconds() *
- sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2))).
- Add(mean).Subtract(drift_from_mean_);
- if (random < QuicTime::Delta::Zero()) {
- // Don't do negative deltas.
- drift_from_mean_ = drift_from_mean_.Subtract(mean);
- return QuicTime::Delta::Zero();
- }
- drift_from_mean_ = drift_from_mean_.Add(random).Subtract(mean);
- return random;
-}
-
-int InterArrivalOveruseDetectorTest::Run100000Samples(
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation) {
- int unique_overuse = 0;
- int last_overuse = -1;
- for (int i = 0; i < 100000; ++i) {
- // Assume that we send out the packets with perfect pacing.
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean, standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- // We expect to randomly hit a few false detects, count the unique
- // overuse events, hence not multiple signals in a row.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- if (kBandwidthOverUsing == overuse_detector_.GetState(
- &estimated_buffer_delay)) {
- if (last_overuse + 1 != i) {
- unique_overuse++;
- }
- last_overuse = i;
- }
- }
- return unique_overuse;
-}
-
-int InterArrivalOveruseDetectorTest::RunUntilOveruse(
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst,
- QuicTime::Delta *estimated_buffer_delay) {
- // Simulate a higher send pace, that is too high.
- for (int i = 0; i < 1000; ++i) {
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean.Add(drift_per_burst),
- standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- if (kBandwidthOverUsing == overuse_detector_.GetState(
- estimated_buffer_delay)) {
- return i + 1;
- }
- }
- return -1;
-}
-
-int InterArrivalOveruseDetectorTest::RunUntilSteady(
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst) {
- // Simulate a lower send pace, that is lower than the capacity.
- for (int i = 0; i < 1000; ++i) {
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst),
- standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- if (kBandwidthSteady ==
- overuse_detector_.GetState(&estimated_buffer_delay)) {
- return i + 1;
- }
- }
- return -1;
-}
-
-int InterArrivalOveruseDetectorTest::RunUntilNotDraining(
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst) {
- // Simulate a lower send pace, that is lower than the capacity.
- for (int i = 0; i < 1000; ++i) {
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst),
- standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- if (kBandwidthDraining >
- overuse_detector_.GetState(&estimated_buffer_delay)) {
- return i + 1;
- }
- }
- return -1;
-}
-
-int InterArrivalOveruseDetectorTest::RunUntilUnderusing(
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst) {
- // Simulate a lower send pace, that is lower than the capacity.
- for (int i = 0; i < 1000; ++i) {
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst),
- standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- if (kBandwidthUnderUsing == overuse_detector_.GetState(
- &estimated_buffer_delay)) {
- return i + 1;
- }
- }
- return -1;
-}
-
-void InterArrivalOveruseDetectorTest::RunXBursts(
- int bursts,
- int packets_per_burst,
- QuicTime::Delta mean,
- QuicTime::Delta standard_deviation,
- QuicTime::Delta drift_per_burst) {
- for (int i = 0; i < bursts; ++i) {
- send_clock_.AdvanceTime(mean);
- QuicTime send_time = send_clock_.ApproximateNow();
- // Do only one random delta for all packets in a burst.
- receive_clock_.AdvanceTime(GaussianRandom(mean.Add(drift_per_burst),
- standard_deviation));
- QuicTime receive_time = receive_clock_.ApproximateNow();
- for (int j = 0; j <= packets_per_burst; ++j) {
- overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
- send_time,
- (j == packets_per_burst),
- receive_time);
- }
- }
-}
-
-// TODO(pwestin): test packet loss impact on accuracy.
-// TODO(pwestin): test colored noise by dropping late frames.
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_TestNoise) {
- int count[100];
- memset(count, 0, sizeof(count));
- for (int i = 0; i < 10000; ++i) {
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(30);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(10);
- count[GaussianRandom(mean, standard_deviation).ToMilliseconds()]++;
- }
- for (int j = 0; j < 100; ++j) {
- DLOG(INFO) << j << ":" << count[j];
- }
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleNonOveruse) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
-
- for (int i = 0; i < 1000; ++i) {
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
- send_clock_.AdvanceTime(delta);
- receive_clock_.AdvanceTime(delta);
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- EXPECT_EQ(kBandwidthSteady,
- overuse_detector_.GetState(&estimated_buffer_delay));
- }
-}
-
-TEST_F(InterArrivalOveruseDetectorTest,
- DISABLED_SimpleNonOveruseSendClockAhead) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
- send_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1234));
-
- for (int i = 0; i < 1000; ++i) {
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
- send_clock_.AdvanceTime(delta);
- receive_clock_.AdvanceTime(delta);
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- EXPECT_EQ(kBandwidthSteady,
- overuse_detector_.GetState(&estimated_buffer_delay));
- }
-}
-
-TEST_F(InterArrivalOveruseDetectorTest,
- DISABLED_SimpleNonOveruseSendClockBehind) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
- receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1234));
-
- for (int i = 0; i < 1000; ++i) {
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
- send_clock_.AdvanceTime(delta);
- receive_clock_.AdvanceTime(delta);
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- EXPECT_EQ(kBandwidthSteady,
- overuse_detector_.GetState(&estimated_buffer_delay));
- }
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleNonOveruseWithVariance) {
- QuicPacketSequenceNumber sequence_number = 1;
- for (int i = 0; i < 1000; ++i) {
- if (i % 2) {
- receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
- } else {
- receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(15));
- }
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
- send_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- EXPECT_EQ(kBandwidthSteady,
- overuse_detector_.GetState(&estimated_buffer_delay));
- }
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleOveruse) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
-
- for (int i = 0; i < 100000; ++i) {
- send_clock_.AdvanceTime(send_delta);
- receive_clock_.AdvanceTime(received_delta);
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- // Sending 2 packets the same time as that is what we expect to do.
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- false,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
-
- EXPECT_EQ(kBandwidthSteady,
- overuse_detector_.GetState(&estimated_buffer_delay));
- }
- // Simulate a higher send pace, that is too high by receiving 1 millisecond
- // late per packet.
- received_delta = QuicTime::Delta::FromMilliseconds(6);
- send_clock_.AdvanceTime(send_delta);
- receive_clock_.AdvanceTime(received_delta);
- QuicTime send_time = send_clock_.ApproximateNow();
- QuicTime receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- false,
- receive_time);
- receive_clock_.AdvanceTime(received_delta);
- receive_time = receive_clock_.ApproximateNow();
- overuse_detector_.OnAcknowledgedPacket(sequence_number++,
- send_time,
- true,
- receive_time);
- EXPECT_EQ(kBandwidthOverUsing,
- overuse_detector_.GetState(&estimated_buffer_delay));
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance10Kbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1000);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 6 updates with 5 milliseconds before
- // detection.
- // Resulting in a minimal buffer build up of 30 milliseconds.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(6, bursts_until_overuse);
- EXPECT_NEAR(40, estimated_buffer_delay.ToMilliseconds(), 15);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(6, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(7, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance10Kbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1000);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(50);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(50);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 6 updates with 50 milliseconds before
- // detection.
- // Resulting in a minimal buffer build up of 300 milliseconds.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(6, bursts_until_overuse);
- EXPECT_NEAR(400, estimated_buffer_delay.ToMilliseconds(), 150);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(6, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(7, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance100Kbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(100);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 6 updates with 5 milliseconds
- // before detection.
- // Resulting in a minimal buffer build up of 30 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(6, bursts_until_overuse);
- EXPECT_NEAR(40, estimated_buffer_delay.ToMilliseconds(), 15);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(1,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(7, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance100Kbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(100);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(50);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(50);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 4 updates with 50 milliseconds
- // before detection.
- // Resulting in a minimal buffer build up of 200 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(4, bursts_until_overuse);
- EXPECT_NEAR(300, estimated_buffer_delay.ToMilliseconds(), 150);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(1,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance1Mbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 4 updates with 5 millisecond
- // before detection.
- // Resulting in a minimal buffer build up of 20 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(4, bursts_until_overuse);
- EXPECT_NEAR(30, estimated_buffer_delay.ToMilliseconds(), 15);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(14, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(4, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1Mbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 6 updates with 1 millisecond
- // before detection.
- // Resulting in a minimal buffer build up of 6 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(6, bursts_until_overuse);
- EXPECT_NEAR(8, estimated_buffer_delay.ToMilliseconds(), 3);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(16, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(4, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance20Mbit) {
- int packets_per_burst = 2;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 4 updates with 500 microsecond
- // before detection.
- // Resulting in a minimal buffer build up of 2 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(4, bursts_until_overuse);
- EXPECT_NEAR(3, estimated_buffer_delay.ToMilliseconds(), 2);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(100,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_NEAR(100, bursts_until_not_draining, 10);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(1, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance100Mbit) {
- int packets_per_burst = 10;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 4 updates with 500 microsecond
- // before detection.
- // Resulting in a minimal buffer build up of 2 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(4, bursts_until_overuse);
- EXPECT_NEAR(3, estimated_buffer_delay.ToMilliseconds(), 2);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(100,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_NEAR(100, bursts_until_not_draining, 10);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(1, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_VeryHighVariance100Mbit) {
- int packets_per_burst = 10;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500);
-
- // We get false overuse in this scenario due to that the standard deviation is
- // higher than our mean and the fact that a delta time can't be negative. This
- // results in an under estimated standard deviation in the estimator causing
- // false detects.
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(2000, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 25 updates with 500 microsecond
- // before detection.
- // Resulting in a minimal buffer build up of 12.5 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(17, bursts_until_overuse);
- EXPECT_NEAR(22, estimated_buffer_delay.ToMilliseconds(), 15);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(100,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(117, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(1, bursts_until_underusing);
-}
-
-//
-// Tests simulating big drop in bitrate.
-//
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitTo100Kbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(100);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 1 update with 100 millisecond
- // before detection.
- // Resulting in a minimal buffer build up of 100 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(1, bursts_until_overuse);
- EXPECT_NEAR(104, estimated_buffer_delay.ToMilliseconds(), 3);
-
- // Back off 20% lower than estimate to drain.
- mean = QuicTime::Delta::FromMilliseconds(100);
- drift_per_burst = QuicTime::Delta::FromMilliseconds(20);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(3, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance20MbitTo1Mbit) {
- int packets_per_burst = 2;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(10);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 1 update with 10 milliseconds
- // before detection.
- // Resulting in a minimal buffer build up of 10 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(1, bursts_until_overuse);
- EXPECT_NEAR(12, estimated_buffer_delay.ToMilliseconds(), 2);
-
- // Back off 20% lower than estimate to drain.
- mean = QuicTime::Delta::FromMilliseconds(10);
- drift_per_burst = QuicTime::Delta::FromMilliseconds(2);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(3, bursts_until_underusing);
-}
-
-//
-// Tests that we can detect slow drifts.
-//
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitSmallSteps) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(100);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 41 updates with 100 microseconds before
- // detection.
- // Resulting in a minimal buffer build up of 4.1 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(41, bursts_until_overuse);
- EXPECT_NEAR(7, estimated_buffer_delay.ToMilliseconds(), 3);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(29, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(71, bursts_until_underusing);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitTinySteps) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(10);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a higher send pace, that is too high.
- // With current tuning we require 345 updates with 10 microseconds before
- // detection.
- // Resulting in a minimal buffer build up of 3.45 ms.
- QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
- int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst,
- &estimated_buffer_delay);
- EXPECT_GE(345, bursts_until_overuse);
- EXPECT_NEAR(7, estimated_buffer_delay.ToMilliseconds(), 3);
-
- // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
- // detection.
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(18, bursts_until_not_draining);
-
- // After draining the buffer additionally we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(683, bursts_until_underusing);
-}
-
-//
-// Tests simulating starting with full buffers.
-//
-
-TEST_F(InterArrivalOveruseDetectorTest,
- DISABLED_StartedWithFullBuffersHighVariance1Mbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a lower send pace.
- // Draining the buffer until we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(6, bursts_until_underusing);
-
- // After draining the buffers we are back in a normal state.
- drift_per_burst = QuicTime::Delta::FromMilliseconds(0);
- int bursts_until_steady = RunUntilSteady(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(6, bursts_until_steady);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest,
- DISABLED_StartedWithFullBuffersHighVariance1MbitSlowDrift) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a faster receive pace.
- // Draining the buffer until we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(21, bursts_until_underusing);
-
- // Simulate an RTT of 100 ms. Hence underusing for additional 100 ms before
- // detection.
- drift_per_burst = QuicTime::Delta::FromMilliseconds(-1);
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- drift_per_burst = QuicTime::Delta::FromMilliseconds(0);
- int bursts_until_steady = RunUntilSteady(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(4, bursts_until_steady);
-}
-
-TEST_F(InterArrivalOveruseDetectorTest,
- DISABLED_StartedWithFullBuffersLowVariance1Mbit) {
- int packets_per_burst = 1;
- QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
- QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
- QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1);
-
- int overuse_signals = Run100000Samples(packets_per_burst,
- mean,
- standard_deviation);
- EXPECT_GE(1, overuse_signals);
-
- // Simulate a lower send pace.
- // Draining the buffer until we detect an underuse.
- int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(5, bursts_until_underusing);
-
- // Simulate an RTT of 100 ms. Hence underusing for additional 100 ms before
- // detection.
- drift_per_burst = QuicTime::Delta::FromMilliseconds(-1);
- RunXBursts(10,
- packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
-
- // After draining the buffers we are back in a normal state.
- drift_per_burst = QuicTime::Delta::FromMilliseconds(0);
- int bursts_until_steady = RunUntilSteady(packets_per_burst,
- mean,
- standard_deviation,
- drift_per_burst);
- EXPECT_GE(41, bursts_until_steady);
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe.cc b/chromium/net/quic/congestion_control/inter_arrival_probe.cc
deleted file mode 100644
index 6d3af3208fe..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_probe.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_probe.h"
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-
-namespace {
-const int kProbeSizePackets = 10;
-const net::QuicByteCount kMinPacketSize = 500;
-const int64 kDefaultBytesPerSecond = 40000;
-const float kUncertainScaleFactor = 0.5; // TODO(pwestin): revisit this factor.
-}
-
-namespace net {
-
-InterArrivalProbe::InterArrivalProbe(QuicByteCount max_segment_size)
- : max_segment_size_(max_segment_size),
- estimate_available_(false),
- available_channel_estimate_(QuicBandwidth::Zero()),
- unacked_data_(0) {
-}
-
-InterArrivalProbe::~InterArrivalProbe() {
-}
-
-void InterArrivalProbe::set_max_segment_size(QuicByteCount max_segment_size) {
- max_segment_size_ = max_segment_size;
-}
-
-bool InterArrivalProbe::GetEstimate(QuicBandwidth* available_channel_estimate) {
- if (!estimate_available_) {
- return false;
- }
- *available_channel_estimate = available_channel_estimate_;
- return true;
-}
-
-void InterArrivalProbe::OnPacketSent(QuicByteCount bytes) {
- if (!estimate_available_) {
- unacked_data_ += bytes;
- }
-}
-
-void InterArrivalProbe::OnAcknowledgedPacket(QuicByteCount bytes) {
- if (!estimate_available_) {
- DCHECK_LE(bytes, unacked_data_);
- unacked_data_ -= bytes;
- }
-}
-
-QuicByteCount InterArrivalProbe::GetAvailableCongestionWindow() {
- if (estimate_available_) {
- return 0;
- }
- return (kProbeSizePackets * max_segment_size_) - unacked_data_;
-}
-
-void InterArrivalProbe::OnIncomingFeedback(
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes_sent,
- QuicTime time_sent,
- QuicTime time_received) {
- if (estimate_available_) {
- return;
- }
-
- if (available_channel_estimator_.get() == NULL) {
- if (bytes_sent < kMinPacketSize) {
- // Packet too small to start the probe phase.
- return;
- }
- first_sequence_number_ = sequence_number;
- available_channel_estimator_.reset(new AvailableChannelEstimator(
- sequence_number, time_sent, time_received));
- return;
- }
-
- available_channel_estimator_->OnIncomingFeedback(sequence_number,
- bytes_sent,
- time_sent,
- time_received);
- if (sequence_number < kProbeSizePackets - 1 + first_sequence_number_) {
- // We need more feedback before we have a probe estimate.
- return;
- }
- // Get the current estimated available channel capacity.
- // available_channel_estimate is invalid if kAvailableChannelEstimateUnknown
- // is returned.
- QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
- AvailableChannelEstimateState available_channel_estimate_state =
- available_channel_estimator_->GetAvailableChannelEstimate(
- &available_channel_estimate);
- switch (available_channel_estimate_state) {
- case kAvailableChannelEstimateUnknown:
- // Backup when we miss our probe.
- available_channel_estimate_ =
- QuicBandwidth::FromBytesPerSecond(kDefaultBytesPerSecond);
- break;
- case kAvailableChannelEstimateUncertain:
- available_channel_estimate_ =
- available_channel_estimate.Scale(kUncertainScaleFactor);
- break;
- case kAvailableChannelEstimateGood:
- available_channel_estimate_ = available_channel_estimate;
- break;
- case kAvailableChannelEstimateSenderLimited:
- available_channel_estimate_ =
- std::max(available_channel_estimate,
- QuicBandwidth::FromBytesPerSecond(kDefaultBytesPerSecond));
- break;
- }
- estimate_available_ = true;
- available_channel_estimator_.reset(NULL);
- DVLOG(1) << "Probe estimate:"
- << available_channel_estimate_.ToKBitsPerSecond()
- << " Kbits/s";
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe.h b/chromium/net/quic/congestion_control/inter_arrival_probe.h
deleted file mode 100644
index 0cba7eec8db..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_probe.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Class that handle the initial probing phase of inter arrival congestion
-// control.
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/base/net_export.h"
-#include "net/quic/congestion_control/available_channel_estimator.h"
-#include "net/quic/quic_bandwidth.h"
-
-namespace net {
-
-class NET_EXPORT_PRIVATE InterArrivalProbe {
- public:
- explicit InterArrivalProbe(QuicByteCount max_segment_size);
- ~InterArrivalProbe();
-
- void set_max_segment_size(QuicByteCount max_segment_size);
-
- // Call every time a packet is sent to the network.
- void OnPacketSent(QuicByteCount bytes);
-
- // Call once for each sent packet that we receive an acknowledgement from
- // the peer for.
- void OnAcknowledgedPacket(QuicByteCount bytes);
-
- // Call to get the number of bytes that can be sent as part of this probe.
- QuicByteCount GetAvailableCongestionWindow();
-
- // Call once for each sent packet we receive a congestion feedback from the
- // peer for.
- // If a peer sends both and ack and feedback for a sent packet, both
- // OnAcknowledgedPacket and OnIncomingFeedback should be called.
- void OnIncomingFeedback(QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes_sent,
- QuicTime time_sent,
- QuicTime time_received);
-
- // Returns false as long as we are probing, available_channel_estimate is
- // invalid during that time. When the probe is completed this function return
- // true and available_channel_estimate contains the estimate.
- bool GetEstimate(QuicBandwidth* available_channel_estimate);
-
- private:
- QuicByteCount max_segment_size_;
- scoped_ptr<AvailableChannelEstimator> available_channel_estimator_;
- QuicPacketSequenceNumber first_sequence_number_;
- bool estimate_available_;
- QuicBandwidth available_channel_estimate_;
- QuicByteCount unacked_data_;
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalProbe);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc b/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc
deleted file mode 100644
index d242a6caf4e..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/congestion_control/inter_arrival_probe.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class InterArrivalProbeTest : public ::testing::Test {
- protected:
- InterArrivalProbeTest()
- : probe_(kDefaultMaxPacketSize),
- start_(QuicTime::Zero()) {
- }
-
- InterArrivalProbe probe_;
- QuicTime start_;
-};
-
-TEST_F(InterArrivalProbeTest, CongestionWindow) {
- for (size_t i = 0; i < 10; i++) {
- probe_.OnPacketSent(kDefaultMaxPacketSize);
- EXPECT_EQ((9 - i) * kDefaultMaxPacketSize,
- probe_.GetAvailableCongestionWindow());
- }
- probe_.OnAcknowledgedPacket(kDefaultMaxPacketSize);
- EXPECT_EQ(kDefaultMaxPacketSize, probe_.GetAvailableCongestionWindow());
-
- probe_.OnPacketSent(kDefaultMaxPacketSize);
- EXPECT_EQ(0u, probe_.GetAvailableCongestionWindow());
-}
-
-TEST_F(InterArrivalProbeTest, Estimate) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicByteCount bytes_sent = kDefaultMaxPacketSize;
- QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10));
- QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1));
- QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
-
- for (size_t i = 0; i < 10; ++i) {
- EXPECT_FALSE(probe_.GetEstimate(&available_channel_estimate));
-
- probe_.OnIncomingFeedback(sequence_number++,
- bytes_sent,
- time_sent,
- time_received);
- time_sent = time_sent.Add(QuicTime::Delta::FromMilliseconds(1));
- time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10));
- }
- EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate));
- EXPECT_EQ(kDefaultMaxPacketSize * 100,
- static_cast<uint64>(available_channel_estimate.ToBytesPerSecond()));
-}
-
-TEST_F(InterArrivalProbeTest, EstimateWithLoss) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicByteCount bytes_sent = kDefaultMaxPacketSize;
- QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10));
- QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1));
- QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
-
- for (size_t i = 0; i < 6; ++i) {
- EXPECT_FALSE(probe_.GetEstimate(&available_channel_estimate));
-
- probe_.OnIncomingFeedback(sequence_number,
- bytes_sent,
- time_sent,
- time_received);
- sequence_number += 2;
- time_sent = time_sent.Add(QuicTime::Delta::FromMilliseconds(1));
- time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10));
- }
- EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate));
- EXPECT_EQ(kDefaultMaxPacketSize * 50,
- static_cast<uint64>(available_channel_estimate.ToBytesPerSecond()));
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver.cc b/chromium/net/quic/congestion_control/inter_arrival_receiver.cc
deleted file mode 100644
index 770b287db0b..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_receiver.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_receiver.h"
-
-#include "base/basictypes.h"
-
-namespace net {
-
-InterArrivalReceiver::InterArrivalReceiver()
- : accumulated_number_of_recoverd_lost_packets_(0) {
-}
-
-InterArrivalReceiver::~InterArrivalReceiver() {
-}
-
-bool InterArrivalReceiver::GenerateCongestionFeedback(
- QuicCongestionFeedbackFrame* feedback) {
- if (received_packet_times_.size() <= 1) {
- // Don't waste resources by sending a feedback frame for only one packet.
- return false;
- }
- feedback->type = kInterArrival;
- feedback->inter_arrival.accumulated_number_of_lost_packets =
- accumulated_number_of_recoverd_lost_packets_;
-
- // Copy our current receive set to our feedback message, we will not resend
- // this data if it is lost.
- feedback->inter_arrival.received_packet_times = received_packet_times_;
-
- // Prepare for the next set of arriving packets by clearing our current set.
- received_packet_times_.clear();
- return true;
-}
-
-void InterArrivalReceiver::RecordIncomingPacket(
- QuicByteCount /*bytes*/,
- QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool revived) {
- if (revived) {
- ++accumulated_number_of_recoverd_lost_packets_;
- }
- received_packet_times_.insert(std::make_pair(sequence_number, timestamp));
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver.h b/chromium/net/quic/congestion_control/inter_arrival_receiver.h
deleted file mode 100644
index a9de62cb1ea..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_receiver.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "net/base/net_export.h"
-#include "net/quic/congestion_control/receive_algorithm_interface.h"
-#include "net/quic/quic_clock.h"
-#include "net/quic/quic_protocol.h"
-
-namespace net {
-
-class NET_EXPORT_PRIVATE InterArrivalReceiver
- : public ReceiveAlgorithmInterface {
- public:
- InterArrivalReceiver();
- virtual ~InterArrivalReceiver();
-
- // Start implementation of ReceiveAlgorithmInterface.
- virtual bool GenerateCongestionFeedback(
- QuicCongestionFeedbackFrame* feedback) OVERRIDE;
-
- virtual void RecordIncomingPacket(QuicByteCount bytes,
- QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool revived) OVERRIDE;
- // End implementation of ReceiveAlgorithmInterface.
-
- private:
- // We need to keep track of FEC recovered packets.
- int accumulated_number_of_recoverd_lost_packets_;
-
- // The set of received packets since the last feedback was sent, along with
- // their arrival times.
- TimeMap received_packet_times_;
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalReceiver);
-};
-
-} // namespace net
-
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc b/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc
deleted file mode 100644
index 927fb6d92f7..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "net/quic/congestion_control/inter_arrival_receiver.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class InterArrivalReceiverTest : public ::testing::Test {
- protected:
- InterArrivalReceiver receiver_;
- MockClock clock_;
-};
-
-TEST_F(InterArrivalReceiverTest, SimpleReceiver) {
- QuicTime start = clock_.ApproximateNow();
- QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
- clock_.AdvanceTime(received_delta);
- QuicTime receive_timestamp = clock_.ApproximateNow();
- receiver_.RecordIncomingPacket(1, 1, receive_timestamp, false);
-
- QuicCongestionFeedbackFrame feedback;
- ASSERT_FALSE(receiver_.GenerateCongestionFeedback(&feedback));
-
- clock_.AdvanceTime(received_delta);
- receive_timestamp = clock_.ApproximateNow();
- // Packet not received; but rather revived by FEC.
- receiver_.RecordIncomingPacket(1, 2, receive_timestamp, true);
- clock_.AdvanceTime(received_delta);
- receive_timestamp = clock_.ApproximateNow();
- receiver_.RecordIncomingPacket(1, 3, receive_timestamp, false);
-
- ASSERT_TRUE(receiver_.GenerateCongestionFeedback(&feedback));
-
- EXPECT_EQ(kInterArrival, feedback.type);
- EXPECT_EQ(1, feedback.inter_arrival.accumulated_number_of_lost_packets);
- EXPECT_EQ(3u, feedback.inter_arrival.received_packet_times.size());
- TimeMap::iterator it = feedback.inter_arrival.received_packet_times.begin();
- EXPECT_EQ(1u, it->first);
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), it->second.Subtract(start));
- it = feedback.inter_arrival.received_packet_times.begin();
- it++;
- EXPECT_EQ(2u, it->first);
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), it->second.Subtract(start));
- it++;
- EXPECT_EQ(3u, it->first);
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(30), it->second.Subtract(start));
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender.cc b/chromium/net/quic/congestion_control/inter_arrival_sender.cc
deleted file mode 100644
index 632a41612be..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_sender.cc
+++ /dev/null
@@ -1,529 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_sender.h"
-
-namespace net {
-
-namespace {
-const int64 kProbeBitrateKBytesPerSecond = 1200; // 9.6 Mbit/s
-const float kPacketLossBitrateReduction = 0.7f;
-const float kUncertainSafetyMargin = 0.7f;
-const float kMaxBitrateReduction = 0.9f;
-const float kMinBitrateReduction = 0.05f;
-const uint64 kMinBitrateKbit = 10;
-const int kInitialRttMs = 60; // At a typical RTT 60 ms.
-const float kAlpha = 0.125f;
-const float kOneMinusAlpha = 1 - kAlpha;
-
-static const int kBitrateSmoothingPeriodMs = 1000;
-static const int kMinBitrateSmoothingPeriodMs = 500;
-
-} // namespace
-
-InterArrivalSender::InterArrivalSender(const QuicClock* clock)
- : probing_(true),
- max_segment_size_(kDefaultMaxPacketSize),
- current_bandwidth_(QuicBandwidth::Zero()),
- smoothed_rtt_(QuicTime::Delta::Zero()),
- channel_estimator_(new ChannelEstimator()),
- bitrate_ramp_up_(new InterArrivalBitrateRampUp(clock)),
- overuse_detector_(new InterArrivalOveruseDetector()),
- probe_(new InterArrivalProbe(max_segment_size_)),
- state_machine_(new InterArrivalStateMachine(clock)),
- paced_sender_(new PacedSender(QuicBandwidth::FromKBytesPerSecond(
- kProbeBitrateKBytesPerSecond), max_segment_size_)),
- accumulated_number_of_lost_packets_(0),
- bandwidth_usage_state_(kBandwidthSteady),
- back_down_time_(QuicTime::Zero()),
- back_down_bandwidth_(QuicBandwidth::Zero()),
- back_down_congestion_delay_(QuicTime::Delta::Zero()) {
-}
-
-InterArrivalSender::~InterArrivalSender() {
-}
-
-void InterArrivalSender::SetFromConfig(const QuicConfig& config,
- bool is_server) {
-}
-
-void InterArrivalSender::SetMaxPacketSize(QuicByteCount max_packet_size) {
- max_segment_size_ = max_packet_size;
- paced_sender_->set_max_segment_size(max_segment_size_);
- probe_->set_max_segment_size(max_segment_size_);
-}
-
-// TODO(pwestin): this is really inefficient (4% CPU on the GFE loadtest).
-// static
-QuicBandwidth InterArrivalSender::CalculateSentBandwidth(
- const SendAlgorithmInterface::SentPacketsMap& sent_packets_map,
- QuicTime feedback_receive_time) {
- const QuicTime::Delta kBitrateSmoothingPeriod =
- QuicTime::Delta::FromMilliseconds(kBitrateSmoothingPeriodMs);
- const QuicTime::Delta kMinBitrateSmoothingPeriod =
- QuicTime::Delta::FromMilliseconds(kMinBitrateSmoothingPeriodMs);
-
- QuicByteCount sum_bytes_sent = 0;
-
- // Sum packet from new until they are kBitrateSmoothingPeriod old.
- SendAlgorithmInterface::SentPacketsMap::const_reverse_iterator history_rit =
- sent_packets_map.rbegin();
-
- QuicTime::Delta max_diff = QuicTime::Delta::Zero();
- for (; history_rit != sent_packets_map.rend(); ++history_rit) {
- QuicTime::Delta diff =
- feedback_receive_time.Subtract(history_rit->second->send_timestamp());
- if (diff > kBitrateSmoothingPeriod) {
- break;
- }
- sum_bytes_sent += history_rit->second->bytes_sent();
- max_diff = diff;
- }
- if (max_diff < kMinBitrateSmoothingPeriod) {
- // No estimate.
- return QuicBandwidth::Zero();
- }
- return QuicBandwidth::FromBytesAndTimeDelta(sum_bytes_sent, max_diff);
-}
-
-void InterArrivalSender::OnIncomingQuicCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& sent_packets) {
- DCHECK(feedback.type == kInterArrival);
-
- if (feedback.type != kInterArrival) {
- return;
- }
-
- QuicBandwidth sent_bandwidth = CalculateSentBandwidth(sent_packets,
- feedback_receive_time);
-
- TimeMap::const_iterator received_it;
- for (received_it = feedback.inter_arrival.received_packet_times.begin();
- received_it != feedback.inter_arrival.received_packet_times.end();
- ++received_it) {
- QuicPacketSequenceNumber sequence_number = received_it->first;
-
- SentPacketsMap::const_iterator sent_it = sent_packets.find(sequence_number);
- if (sent_it == sent_packets.end()) {
- // Too old data; ignore and move forward.
- DVLOG(1) << "Too old feedback move forward, sequence_number:"
- << sequence_number;
- continue;
- }
- QuicTime time_received = received_it->second;
- QuicTime time_sent = sent_it->second->send_timestamp();
- QuicByteCount bytes_sent = sent_it->second->bytes_sent();
-
- channel_estimator_->OnAcknowledgedPacket(
- sequence_number, bytes_sent, time_sent, time_received);
- if (probing_) {
- probe_->OnIncomingFeedback(
- sequence_number, bytes_sent, time_sent, time_received);
- } else {
- bool last_of_send_time = false;
- SentPacketsMap::const_iterator next_sent_it = ++sent_it;
- if (next_sent_it == sent_packets.end()) {
- // No more sent packets; hence this must be the last.
- last_of_send_time = true;
- } else {
- if (time_sent != next_sent_it->second->send_timestamp()) {
- // Next sent packet have a different send time.
- last_of_send_time = true;
- }
- }
- overuse_detector_->OnAcknowledgedPacket(
- sequence_number, time_sent, last_of_send_time, time_received);
- }
- }
- if (probing_) {
- probing_ = ProbingPhase(feedback_receive_time);
- return;
- }
-
- bool packet_loss_event = false;
- if (accumulated_number_of_lost_packets_ !=
- feedback.inter_arrival.accumulated_number_of_lost_packets) {
- accumulated_number_of_lost_packets_ =
- feedback.inter_arrival.accumulated_number_of_lost_packets;
- packet_loss_event = true;
- }
- InterArrivalState state = state_machine_->GetInterArrivalState();
-
- if (state == kInterArrivalStatePacketLoss ||
- state == kInterArrivalStateCompetingTcpFLow) {
- if (packet_loss_event) {
- if (!state_machine_->PacketLossEvent()) {
- // Less than one RTT since last PacketLossEvent.
- return;
- }
- EstimateBandwidthAfterLossEvent(feedback_receive_time);
- } else {
- EstimateNewBandwidth(feedback_receive_time, sent_bandwidth);
- }
- return;
- }
- EstimateDelayBandwidth(feedback_receive_time, sent_bandwidth);
-}
-
-bool InterArrivalSender::ProbingPhase(QuicTime feedback_receive_time) {
- QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
- if (!probe_->GetEstimate(&available_channel_estimate)) {
- // Continue probing phase.
- return true;
- }
- QuicBandwidth channel_estimate = QuicBandwidth::Zero();
- ChannelEstimateState channel_estimator_state =
- channel_estimator_->GetChannelEstimate(&channel_estimate);
-
- QuicBandwidth new_rate =
- available_channel_estimate.Scale(kUncertainSafetyMargin);
-
- switch (channel_estimator_state) {
- case kChannelEstimateUnknown:
- channel_estimate = available_channel_estimate;
- break;
- case kChannelEstimateUncertain:
- channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
- break;
- case kChannelEstimateGood:
- // Do nothing.
- break;
- }
- new_rate = std::max(new_rate,
- QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit));
-
- bitrate_ramp_up_->Reset(new_rate, available_channel_estimate,
- channel_estimate);
-
- current_bandwidth_ = new_rate;
- paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_rate);
- DVLOG(1) << "Probe result; new rate:"
- << new_rate.ToKBitsPerSecond() << " Kbits/s "
- << " available estimate:"
- << available_channel_estimate.ToKBitsPerSecond() << " Kbits/s "
- << " channel estimate:"
- << channel_estimate.ToKBitsPerSecond() << " Kbits/s ";
- return false;
-}
-
-void InterArrivalSender::OnPacketAcked(
- QuicPacketSequenceNumber /*acked_sequence_number*/,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) {
- // RTT can't be negative.
- DCHECK_LE(0, rtt.ToMicroseconds());
-
- if (probing_) {
- probe_->OnAcknowledgedPacket(acked_bytes);
- }
-
- if (rtt.IsInfinite()) {
- return;
- }
-
- if (smoothed_rtt_.IsZero()) {
- smoothed_rtt_ = rtt;
- } else {
- smoothed_rtt_ = QuicTime::Delta::FromMicroseconds(
- kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() +
- kAlpha * rtt.ToMicroseconds());
- }
- state_machine_->set_rtt(smoothed_rtt_);
-}
-
-void InterArrivalSender::OnPacketLost(
- QuicPacketSequenceNumber /*sequence_number*/,
- QuicTime ack_receive_time) {
- // Packet loss was reported.
- if (!probing_) {
- if (!state_machine_->PacketLossEvent()) {
- // Less than one RTT since last PacketLossEvent.
- return;
- }
- // Calculate new pace rate.
- EstimateBandwidthAfterLossEvent(ack_receive_time);
- }
-}
-
-bool InterArrivalSender::OnPacketSent(
- QuicTime sent_time,
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes,
- TransmissionType /*transmission_type*/,
- HasRetransmittableData /*has_retransmittable_data*/) {
- if (probing_) {
- probe_->OnPacketSent(bytes);
- }
- paced_sender_->OnPacketSent(sent_time, bytes);
- return true;
-}
-
-void InterArrivalSender::OnRetransmissionTimeout() {
- // TODO(ianswett): Decrease the available bandwidth.
-}
-
-void InterArrivalSender::OnPacketAbandoned(
- QuicPacketSequenceNumber /*sequence_number*/,
- QuicByteCount abandoned_bytes) {
- // TODO(pwestin): use for out outer_congestion_window_ logic.
- if (probing_) {
- probe_->OnAcknowledgedPacket(abandoned_bytes);
- }
-}
-
-QuicTime::Delta InterArrivalSender::TimeUntilSend(
- QuicTime now,
- TransmissionType /*transmission_type*/,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake /*handshake*/) {
- // TODO(pwestin): implement outer_congestion_window_ logic.
- QuicTime::Delta outer_window = QuicTime::Delta::Zero();
-
- if (probing_) {
- if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA &&
- probe_->GetAvailableCongestionWindow() == 0) {
- outer_window = QuicTime::Delta::Infinite();
- }
- }
- return paced_sender_->TimeUntilSend(now, outer_window);
-}
-
-void InterArrivalSender::EstimateDelayBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth sent_bandwidth) {
- QuicTime::Delta estimated_congestion_delay = QuicTime::Delta::Zero();
- BandwidthUsage new_bandwidth_usage_state =
- overuse_detector_->GetState(&estimated_congestion_delay);
-
- switch (new_bandwidth_usage_state) {
- case kBandwidthDraining:
- case kBandwidthUnderUsing:
- // Hold our current bitrate.
- break;
- case kBandwidthOverUsing:
- if (!state_machine_->IncreasingDelayEvent()) {
- // Less than one RTT since last IncreasingDelayEvent.
- return;
- }
- EstimateBandwidthAfterDelayEvent(feedback_receive_time,
- estimated_congestion_delay);
- break;
- case kBandwidthSteady:
- // Calculate new pace rate.
- if (bandwidth_usage_state_ == kBandwidthDraining ||
- bandwidth_usage_state_ == kBandwidthOverUsing) {
- EstimateNewBandwidthAfterDraining(feedback_receive_time,
- estimated_congestion_delay);
- } else {
- EstimateNewBandwidth(feedback_receive_time, sent_bandwidth);
- }
- break;
- }
- bandwidth_usage_state_ = new_bandwidth_usage_state;
-}
-
-QuicBandwidth InterArrivalSender::BandwidthEstimate() const {
- return current_bandwidth_;
-}
-
-QuicTime::Delta InterArrivalSender::SmoothedRtt() const {
- if (smoothed_rtt_.IsZero()) {
- return QuicTime::Delta::FromMilliseconds(kInitialRttMs);
- }
- return smoothed_rtt_;
-}
-
-QuicTime::Delta InterArrivalSender::RetransmissionDelay() const {
- // TODO(pwestin): Calculate and return retransmission delay.
- // Use 2 * the smoothed RTT for now.
- return smoothed_rtt_.Add(smoothed_rtt_);
-}
-
-QuicByteCount InterArrivalSender::GetCongestionWindow() const {
- return 0;
-}
-
-void InterArrivalSender::EstimateNewBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth sent_bandwidth) {
- QuicBandwidth new_bandwidth = bitrate_ramp_up_->GetNewBitrate(sent_bandwidth);
- if (current_bandwidth_ == new_bandwidth) {
- return;
- }
- current_bandwidth_ = new_bandwidth;
- state_machine_->IncreaseBitrateDecision();
-
- QuicBandwidth channel_estimate = QuicBandwidth::Zero();
- ChannelEstimateState channel_estimator_state =
- channel_estimator_->GetChannelEstimate(&channel_estimate);
-
- if (channel_estimator_state == kChannelEstimateGood) {
- bitrate_ramp_up_->UpdateChannelEstimate(channel_estimate);
- }
- paced_sender_->UpdateBandwidthEstimate(feedback_receive_time,
- current_bandwidth_);
- DVLOG(1) << "New bandwidth estimate in steady state:"
- << current_bandwidth_.ToKBitsPerSecond()
- << " Kbits/s";
-}
-
-// Did we drain the network buffers in our expected pace?
-void InterArrivalSender::EstimateNewBandwidthAfterDraining(
- QuicTime feedback_receive_time,
- QuicTime::Delta estimated_congestion_delay) {
- if (current_bandwidth_ > back_down_bandwidth_) {
- // Do nothing, our current bandwidth is higher than our bandwidth at the
- // previous back down.
- DVLOG(1) << "Current bandwidth estimate is higher than before draining";
- return;
- }
- if (estimated_congestion_delay >= back_down_congestion_delay_) {
- // Do nothing, our estimated delay have increased.
- DVLOG(1) << "Current delay estimate is higher than before draining";
- return;
- }
- DCHECK(back_down_time_.IsInitialized());
- QuicTime::Delta buffer_reduction =
- back_down_congestion_delay_.Subtract(estimated_congestion_delay);
- QuicTime::Delta elapsed_time =
- feedback_receive_time.Subtract(back_down_time_).Subtract(SmoothedRtt());
-
- QuicBandwidth new_estimate = QuicBandwidth::Zero();
- if (buffer_reduction >= elapsed_time) {
- // We have drained more than the elapsed time... go back to our old rate.
- new_estimate = back_down_bandwidth_;
- } else {
- float fraction_of_rate =
- static_cast<float>(buffer_reduction.ToMicroseconds()) /
- elapsed_time.ToMicroseconds(); // < 1.0
-
- QuicBandwidth draining_rate = back_down_bandwidth_.Scale(fraction_of_rate);
- QuicBandwidth max_estimated_draining_rate =
- back_down_bandwidth_.Subtract(current_bandwidth_);
- if (draining_rate > max_estimated_draining_rate) {
- // We drained faster than our old send rate, go back to our old rate.
- new_estimate = back_down_bandwidth_;
- } else {
- // Use our drain rate and our kMinBitrateReduction to go to our
- // new estimate.
- new_estimate = std::max(current_bandwidth_,
- current_bandwidth_.Add(draining_rate).Scale(
- 1.0f - kMinBitrateReduction));
- DVLOG(1) << "Draining calculation; current rate:"
- << current_bandwidth_.ToKBitsPerSecond() << " Kbits/s "
- << "draining rate:"
- << draining_rate.ToKBitsPerSecond() << " Kbits/s "
- << "new estimate:"
- << new_estimate.ToKBitsPerSecond() << " Kbits/s "
- << " buffer reduction:"
- << buffer_reduction.ToMicroseconds() << " us "
- << " elapsed time:"
- << elapsed_time.ToMicroseconds() << " us ";
- }
- }
- if (new_estimate == current_bandwidth_) {
- return;
- }
-
- QuicBandwidth channel_estimate = QuicBandwidth::Zero();
- ChannelEstimateState channel_estimator_state =
- channel_estimator_->GetChannelEstimate(&channel_estimate);
-
- // TODO(pwestin): we need to analyze channel_estimate too.
- switch (channel_estimator_state) {
- case kChannelEstimateUnknown:
- channel_estimate = current_bandwidth_;
- break;
- case kChannelEstimateUncertain:
- channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
- break;
- case kChannelEstimateGood:
- // Do nothing, estimate is accurate.
- break;
- }
- bitrate_ramp_up_->Reset(new_estimate, back_down_bandwidth_, channel_estimate);
- state_machine_->IncreaseBitrateDecision();
- paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_estimate);
- current_bandwidth_ = new_estimate;
- DVLOG(1) << "New bandwidth estimate after draining:"
- << new_estimate.ToKBitsPerSecond() << " Kbits/s";
-}
-
-void InterArrivalSender::EstimateBandwidthAfterDelayEvent(
- QuicTime feedback_receive_time,
- QuicTime::Delta estimated_congestion_delay) {
- QuicByteCount estimated_byte_buildup =
- current_bandwidth_.ToBytesPerPeriod(estimated_congestion_delay);
-
- // To drain all build up buffer within one RTT we need to reduce the
- // bitrate with the following.
- // TODO(pwestin): this is a crude first implementation.
- int64 draining_rate_per_rtt = (estimated_byte_buildup *
- kNumMicrosPerSecond) / SmoothedRtt().ToMicroseconds();
-
- float decrease_factor =
- draining_rate_per_rtt / current_bandwidth_.ToBytesPerSecond();
-
- decrease_factor = std::max(decrease_factor, kMinBitrateReduction);
- decrease_factor = std::min(decrease_factor, kMaxBitrateReduction);
- back_down_congestion_delay_ = estimated_congestion_delay;
- QuicBandwidth new_target_bitrate =
- current_bandwidth_.Scale(1.0f - decrease_factor);
-
- // While in delay sensing mode send at least one packet per RTT.
- QuicBandwidth min_delay_bitrate =
- QuicBandwidth::FromBytesAndTimeDelta(max_segment_size_, SmoothedRtt());
- new_target_bitrate = std::max(new_target_bitrate, min_delay_bitrate);
-
- ResetCurrentBandwidth(feedback_receive_time, new_target_bitrate);
-
- DVLOG(1) << "New bandwidth estimate after delay event:"
- << current_bandwidth_.ToKBitsPerSecond()
- << " Kbits/s min delay bitrate:"
- << min_delay_bitrate.ToKBitsPerSecond()
- << " Kbits/s RTT:"
- << SmoothedRtt().ToMicroseconds()
- << " us";
-}
-
-void InterArrivalSender::EstimateBandwidthAfterLossEvent(
- QuicTime feedback_receive_time) {
- ResetCurrentBandwidth(feedback_receive_time,
- current_bandwidth_.Scale(kPacketLossBitrateReduction));
- DVLOG(1) << "New bandwidth estimate after loss event:"
- << current_bandwidth_.ToKBitsPerSecond()
- << " Kbits/s";
-}
-
-void InterArrivalSender::ResetCurrentBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth new_rate) {
- new_rate = std::max(new_rate,
- QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit));
- QuicBandwidth channel_estimate = QuicBandwidth::Zero();
- ChannelEstimateState channel_estimator_state =
- channel_estimator_->GetChannelEstimate(&channel_estimate);
-
- switch (channel_estimator_state) {
- case kChannelEstimateUnknown:
- channel_estimate = current_bandwidth_;
- break;
- case kChannelEstimateUncertain:
- channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
- break;
- case kChannelEstimateGood:
- // Do nothing.
- break;
- }
- back_down_time_ = feedback_receive_time;
- back_down_bandwidth_ = current_bandwidth_;
- bitrate_ramp_up_->Reset(new_rate, current_bandwidth_, channel_estimate);
- if (new_rate != current_bandwidth_) {
- current_bandwidth_ = new_rate;
- paced_sender_->UpdateBandwidthEstimate(feedback_receive_time,
- current_bandwidth_);
- state_machine_->DecreaseBitrateDecision();
- }
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender.h b/chromium/net/quic/congestion_control/inter_arrival_sender.h
deleted file mode 100644
index fabc7b65e34..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_sender.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/base/net_export.h"
-#include "net/quic/congestion_control/channel_estimator.h"
-#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h"
-#include "net/quic/congestion_control/inter_arrival_overuse_detector.h"
-#include "net/quic/congestion_control/inter_arrival_probe.h"
-#include "net/quic/congestion_control/inter_arrival_state_machine.h"
-#include "net/quic/congestion_control/paced_sender.h"
-#include "net/quic/congestion_control/send_algorithm_interface.h"
-#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_clock.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-class NET_EXPORT_PRIVATE InterArrivalSender : public SendAlgorithmInterface {
- public:
- explicit InterArrivalSender(const QuicClock* clock);
- virtual ~InterArrivalSender();
-
- static QuicBandwidth CalculateSentBandwidth(
- const SendAlgorithmInterface::SentPacketsMap& sent_packets_map,
- QuicTime feedback_receive_time);
-
- // Start implementation of SendAlgorithmInterface.
- virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE;
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE;
- virtual void OnIncomingQuicCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& sent_packets) OVERRIDE;
- virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) OVERRIDE;
- virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime ack_receive_time) OVERRIDE;
- virtual bool OnPacketSent(
- QuicTime sent_time,
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data) OVERRIDE;
- virtual void OnRetransmissionTimeout() OVERRIDE;
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) OVERRIDE;
- virtual QuicTime::Delta TimeUntilSend(
- QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) OVERRIDE;
- virtual QuicBandwidth BandwidthEstimate() const OVERRIDE;
- virtual QuicTime::Delta SmoothedRtt() const OVERRIDE;
- virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE;
- virtual QuicByteCount GetCongestionWindow() const OVERRIDE;
- // End implementation of SendAlgorithmInterface.
-
- private:
- void EstimateDelayBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth sent_bandwidth);
- void EstimateNewBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth sent_bandwidth);
- void EstimateNewBandwidthAfterDraining(
- QuicTime feedback_receive_time,
- QuicTime::Delta estimated_congestion_delay);
- void EstimateBandwidthAfterLossEvent(QuicTime feedback_receive_time);
- void EstimateBandwidthAfterDelayEvent(
- QuicTime feedback_receive_time,
- QuicTime::Delta estimated_congestion_delay);
- void ResetCurrentBandwidth(QuicTime feedback_receive_time,
- QuicBandwidth new_rate);
- bool ProbingPhase(QuicTime feedback_receive_time);
-
- bool probing_; // Are we currently in the probing phase?
- QuicByteCount max_segment_size_;
- QuicBandwidth current_bandwidth_;
- QuicTime::Delta smoothed_rtt_;
- scoped_ptr<ChannelEstimator> channel_estimator_;
- scoped_ptr<InterArrivalBitrateRampUp> bitrate_ramp_up_;
- scoped_ptr<InterArrivalOveruseDetector> overuse_detector_;
- scoped_ptr<InterArrivalProbe> probe_;
- scoped_ptr<InterArrivalStateMachine> state_machine_;
- scoped_ptr<PacedSender> paced_sender_;
- int accumulated_number_of_lost_packets_;
- BandwidthUsage bandwidth_usage_state_;
- QuicTime back_down_time_; // Time when we decided to back down.
- QuicBandwidth back_down_bandwidth_; // Bandwidth before backing down.
- QuicTime::Delta back_down_congestion_delay_; // Delay when backing down.
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalSender);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc b/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc
deleted file mode 100644
index fe8dd3cf29f..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc
+++ /dev/null
@@ -1,569 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_sender.h"
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/stl_util.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using std::pair;
-
-namespace net {
-namespace test {
-
-class InterArrivalSenderTest : public ::testing::Test {
- protected:
- InterArrivalSenderTest()
- : rtt_(QuicTime::Delta::FromMilliseconds(60)),
- one_ms_(QuicTime::Delta::FromMilliseconds(1)),
- one_s_(QuicTime::Delta::FromMilliseconds(1000)),
- nine_ms_(QuicTime::Delta::FromMilliseconds(9)),
- send_start_time_(send_clock_.Now()),
- sender_(&send_clock_),
- sequence_number_(1),
- acked_sequence_number_(1),
- feedback_sequence_number_(1) {
- send_clock_.AdvanceTime(one_ms_);
- receive_clock_.AdvanceTime(one_ms_);
- }
-
- virtual ~InterArrivalSenderTest() {
- STLDeleteValues(&sent_packets_);
- }
-
- void SendAvailableCongestionWindow() {
- while (sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()) {
- QuicByteCount bytes_in_packet = kDefaultMaxPacketSize;
- sent_packets_[sequence_number_] =
- new class SendAlgorithmInterface::SentPacket(
- bytes_in_packet, send_clock_.Now(), HAS_RETRANSMITTABLE_DATA);
-
- sender_.OnPacketSent(send_clock_.Now(), sequence_number_, bytes_in_packet,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- sequence_number_++;
- }
- EXPECT_FALSE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
-
- void AckNPackets(int n) {
- for (int i = 0; i < n; ++i) {
- sender_.OnPacketAcked(
- acked_sequence_number_++, kDefaultMaxPacketSize, rtt_);
- }
- }
-
- void SendDelaySpikeFeedbackMessage(QuicTime::Delta spike_time) {
- QuicCongestionFeedbackFrame feedback;
- feedback.type = kInterArrival;
- feedback.inter_arrival.accumulated_number_of_lost_packets = 0;
- receive_clock_.AdvanceTime(spike_time);
- QuicTime receive_time = receive_clock_.ApproximateNow();
- feedback.inter_arrival.received_packet_times.insert(
- pair<QuicPacketSequenceNumber, QuicTime>(
- feedback_sequence_number_, receive_time));
- feedback_sequence_number_++;
-
- // We need to send feedback for 2 packets since they where sent at the
- // same time.
- feedback.inter_arrival.received_packet_times.insert(
- pair<QuicPacketSequenceNumber, QuicTime>(
- feedback_sequence_number_, receive_time));
- feedback_sequence_number_++;
-
- sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(),
- sent_packets_);
- }
-
- void SendFeedbackMessageNPackets(int n,
- QuicTime::Delta delta_odd,
- QuicTime::Delta delta_even) {
- QuicCongestionFeedbackFrame feedback;
- feedback.type = kInterArrival;
- feedback.inter_arrival.accumulated_number_of_lost_packets = 0;
- for (int i = 0; i < n; ++i) {
- if (feedback_sequence_number_ % 2) {
- receive_clock_.AdvanceTime(delta_even);
- } else {
- receive_clock_.AdvanceTime(delta_odd);
- }
- QuicTime receive_time = receive_clock_.ApproximateNow();
- feedback.inter_arrival.received_packet_times.insert(
- pair<QuicPacketSequenceNumber, QuicTime>(
- feedback_sequence_number_, receive_time));
- feedback_sequence_number_++;
- }
- sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(),
- sent_packets_);
- }
-
- QuicTime::Delta SenderDeltaSinceStart() {
- return send_clock_.ApproximateNow().Subtract(send_start_time_);
- }
-
- const QuicTime::Delta rtt_;
- const QuicTime::Delta one_ms_;
- const QuicTime::Delta one_s_;
- const QuicTime::Delta nine_ms_;
- MockClock send_clock_;
- MockClock receive_clock_;
- const QuicTime send_start_time_;
- InterArrivalSender sender_;
- QuicPacketSequenceNumber sequence_number_;
- QuicPacketSequenceNumber acked_sequence_number_;
- QuicPacketSequenceNumber feedback_sequence_number_;
- SendAlgorithmInterface::SentPacketsMap sent_packets_;
-};
-
-TEST_F(InterArrivalSenderTest, ProbeFollowedByFullRampUpCycle) {
- QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // Send 5 bursts.
- for (int i = 0; i < 4; ++i) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
- SendAvailableCongestionWindow();
-
- // We have now sent our probe.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite());
-
- AckNPackets(10);
- SendFeedbackMessageNPackets(10, one_ms_, nine_ms_);
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // We should now have our probe rate.
- QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41);
- int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond /
- acc_arrival_time.ToMicroseconds();
- EXPECT_NEAR(0.7f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- DLOG(INFO) << "After probe";
- // Send 50 bursts, make sure that we move fast in the beginning.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.875f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10);
-
- // Send 50 bursts, make sure that we slow down towards the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.95f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 1100, 10);
-
- // Send 50 bursts, make sure that we move very slow close to the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.99f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 1560, 10);
- DLOG(INFO) << "Near available channel estimate";
-
- // Send 50 bursts, make sure that we move very slow close to the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(1.00f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2000, 100);
- DLOG(INFO) << "At available channel estimate";
-
- // Send 50 bursts, make sure that we move very slow close to the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(1.01f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2500, 100);
-
- // Send 50 bursts, make sure that we accelerate after the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(1.01f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2900, 100);
-
- // Send 50 bursts, make sure that we accelerate after the probe rate.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(1.03f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 3400, 100);
-
- int64 max_rate = kDefaultMaxPacketSize * kNumMicrosPerSecond /
- one_ms_.ToMicroseconds();
-
- int64 halfway_rate = probe_rate + (max_rate - probe_rate) / 2;
-
- // Send until we reach halfway point.
- for (int i = 0; i < 570; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(halfway_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 5000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 6600, 100);
- DLOG(INFO) << "Near halfway point";
-
- // Send until we reach max channel capacity.
- for (int i = 0; i < 1500; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(max_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 5000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 10000, 200);
-}
-
-TEST_F(InterArrivalSenderTest, DelaySpikeFollowedBySlowDrain) {
- QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // Send 5 bursts.
- for (int i = 0; i < 4; ++i) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
- SendAvailableCongestionWindow();
-
- // We have now sent our probe.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite());
-
- AckNPackets(10);
- SendFeedbackMessageNPackets(10, one_ms_, nine_ms_);
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // We should now have our probe rate.
- QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41);
- int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond /
- acc_arrival_time.ToMicroseconds();
- EXPECT_NEAR(0.7f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
-
- // Send 50 bursts, make sure that we move fast in the beginning.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.875f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10);
-
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
-
- int64 rate_at_introduced_delay_spike = 0.875f * probe_rate;
- QuicTime::Delta spike_time = QuicTime::Delta::FromMilliseconds(100);
- SendDelaySpikeFeedbackMessage(spike_time);
-
- // Backing as much as we can, currently 90%.
- EXPECT_NEAR(0.1f * rate_at_introduced_delay_spike,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 610, 10);
-
- // Run until we are catched up after our introduced delay spike.
- while (send_clock_.Now() < receive_clock_.Now()) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, one_ms_);
- }
- // Expect that we go back to 67% of the rate before the spike.
- EXPECT_NEAR(0.67f * rate_at_introduced_delay_spike,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 820, 10);
-
- // Send 100 bursts, make sure that we slow down towards the rate we had
- // before the spike.
- for (int i = 0; i < 100; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.97f * rate_at_introduced_delay_spike,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2100, 10);
-}
-
-TEST_F(InterArrivalSenderTest, DelaySpikeFollowedByImmediateDrain) {
- QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // Send 5 bursts.
- for (int i = 0; i < 4; ++i) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
- SendAvailableCongestionWindow();
-
- // We have now sent our probe.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite());
-
- AckNPackets(10);
- SendFeedbackMessageNPackets(10, one_ms_, nine_ms_);
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // We should now have our probe rate.
- QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41);
- int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond /
- acc_arrival_time.ToMicroseconds();
- EXPECT_NEAR(0.7f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
-
- // Send 50 bursts, make sure that we move fast in the beginning.
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
- }
- EXPECT_NEAR(0.875f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10);
-
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- AckNPackets(2);
-
- int64 rate_at_introduced_delay_spike = 0.875f * probe_rate;
- QuicTime::Delta spike_time = QuicTime::Delta::FromMilliseconds(100);
- SendDelaySpikeFeedbackMessage(spike_time);
-
- // Backing as much as we can, currently 90%.
- EXPECT_NEAR(0.1f * rate_at_introduced_delay_spike,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 610, 10);
-
- // Move send time forward.
- send_clock_.AdvanceTime(spike_time);
- // Make sure our clocks are aligned again.
- receive_clock_.AdvanceTime(send_clock_.Now().Subtract(receive_clock_.Now()));
-
- SendAvailableCongestionWindow();
- AckNPackets(2);
- SendFeedbackMessageNPackets(2, one_ms_, one_ms_);
- // We should now be back where we introduced the delay spike.
- EXPECT_NEAR(rate_at_introduced_delay_spike,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
- EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 710, 10);
-}
-
-TEST_F(InterArrivalSenderTest, MinBitrateDueToDelay) {
- QuicBandwidth expected_min_bitrate = QuicBandwidth::FromKBitsPerSecond(10);
- QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // Send 5 bursts.
- for (int i = 0; i < 4; ++i) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
- SendAvailableCongestionWindow();
-
- AckNPackets(10);
-
- // One second spread per packet is expected to result in an estimate at
- // our minimum bitrate.
- SendFeedbackMessageNPackets(10, one_s_, one_s_);
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate());
-}
-
-TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) {
- QuicBandwidth expected_min_bitrate = QuicBandwidth::FromKBitsPerSecond(10);
- QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- // Send 5 bursts.
- for (int i = 0; i < 4; ++i) {
- SendAvailableCongestionWindow();
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
- SendAvailableCongestionWindow();
-
- AckNPackets(10);
- SendFeedbackMessageNPackets(10, nine_ms_, nine_ms_);
- send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
-
- QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(81);
- int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond /
- acc_arrival_time.ToMicroseconds();
- EXPECT_NEAR(0.7f * probe_rate,
- sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
-
- for (int i = 0; i < 15; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- sender_.OnPacketLost(acked_sequence_number_ - 1, send_clock_.Now());
- sender_.OnPacketAcked(acked_sequence_number_, kDefaultMaxPacketSize, rtt_);
- acked_sequence_number_ += 2; // Create a loss by not acking both packets.
- SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_);
- }
- // Test that our exponentail back off stop at expected_min_bitrate.
- EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate());
-
- for (int i = 0; i < 50; ++i) {
- SendAvailableCongestionWindow();
- QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- send_clock_.AdvanceTime(time_until_send);
- EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- sender_.OnPacketLost(acked_sequence_number_ - 1, send_clock_.Now());
- sender_.OnPacketAcked(acked_sequence_number_, kDefaultMaxPacketSize, rtt_);
- acked_sequence_number_ += 2; // Create a loss by not acking both packets.
- SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_);
-
- // Make sure our bitrate is fixed at the expected_min_bitrate.
- EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate());
- }
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc b/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc
deleted file mode 100644
index 7095e60306b..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/inter_arrival_state_machine.h"
-
-#include "base/logging.h"
-
-namespace {
-const int kIncreaseEventsBeforeDowngradingState = 5;
-const int kDecreaseEventsBeforeUpgradingState = 2;
-// Note: Can not be higher than kDecreaseEventsBeforeUpgradingState;
-const int kLossEventsBeforeUpgradingState = 2;
-// Timeout old loss and delay events after this time.
-const int kEventTimeoutMs = 10000;
-// A reasonable arbitrary chosen value for initial round trip time.
-const int kInitialRttMs = 80;
-}
-
-namespace net {
-
-InterArrivalStateMachine::InterArrivalStateMachine(const QuicClock* clock)
- : clock_(clock),
- current_state_(kInterArrivalStateStable),
- smoothed_rtt_(QuicTime::Delta::FromMilliseconds(kInitialRttMs)),
- decrease_event_count_(0),
- last_decrease_event_(QuicTime::Zero()),
- increase_event_count_(0),
- last_increase_event_(QuicTime::Zero()),
- loss_event_count_(0),
- last_loss_event_(QuicTime::Zero()),
- delay_event_count_(0),
- last_delay_event_(QuicTime::Zero()) {
-}
-
-InterArrivalState InterArrivalStateMachine::GetInterArrivalState() {
- return current_state_;
-}
-
-void InterArrivalStateMachine::IncreaseBitrateDecision() {
- // Multiple increase event without packet loss or delay events will drive
- // state back to stable.
- QuicTime current_time = clock_->ApproximateNow();
- if (current_time.Subtract(last_increase_event_) < smoothed_rtt_) {
- // Less than one RTT have passed; ignore this event.
- return;
- }
- last_increase_event_ = current_time;
- increase_event_count_++;
- decrease_event_count_ = 0; // Reset previous decrease events.
-
- if (increase_event_count_ < kIncreaseEventsBeforeDowngradingState) {
- // Not enough increase events to change state.
- return;
- }
- increase_event_count_ = 0; // Reset increase events.
-
- switch (current_state_) {
- case kInterArrivalStateStable:
- // Keep this state.
- break;
- case kInterArrivalStatePacketLoss:
- current_state_ = kInterArrivalStateStable;
- break;
- case kInterArrivalStateDelay:
- current_state_ = kInterArrivalStateStable;
- break;
- case kInterArrivalStateCompetingFlow:
- current_state_ = kInterArrivalStateDelay;
- break;
- case kInterArrivalStateCompetingTcpFLow:
- current_state_ = kInterArrivalStateDelay;
- break;
- }
-}
-
-void InterArrivalStateMachine::DecreaseBitrateDecision() {
- DCHECK(kDecreaseEventsBeforeUpgradingState >=
- kLossEventsBeforeUpgradingState);
-
- QuicTime current_time = clock_->ApproximateNow();
- if (current_time.Subtract(last_decrease_event_) < smoothed_rtt_) {
- // Less than one RTT have passed; ignore this event.
- return;
- }
- last_decrease_event_ = current_time;
- decrease_event_count_++;
- increase_event_count_ = 0; // Reset previous increase events.
- if (decrease_event_count_ < kDecreaseEventsBeforeUpgradingState) {
- // Not enough decrease events to change state.
- return;
- }
- decrease_event_count_ = 0; // Reset decrease events.
-
- switch (current_state_) {
- case kInterArrivalStateStable:
- if (delay_event_count_ == 0 && loss_event_count_ > 0) {
- // No recent delay events; only packet loss events.
- current_state_ = kInterArrivalStatePacketLoss;
- } else {
- current_state_ = kInterArrivalStateDelay;
- }
- break;
- case kInterArrivalStatePacketLoss:
- // Keep this state.
- break;
- case kInterArrivalStateDelay:
- if (loss_event_count_ >= kLossEventsBeforeUpgradingState) {
- // We have packet loss events. Assume fighting with TCP.
- current_state_ = kInterArrivalStateCompetingTcpFLow;
- } else {
- current_state_ = kInterArrivalStateCompetingFlow;
- }
- break;
- case kInterArrivalStateCompetingFlow:
- if (loss_event_count_ >= kLossEventsBeforeUpgradingState) {
- // We have packet loss events. Assume fighting with TCP.
- current_state_ = kInterArrivalStateCompetingTcpFLow;
- }
- break;
- case kInterArrivalStateCompetingTcpFLow:
- // Keep this state.
- break;
- }
-}
-
-void InterArrivalStateMachine::set_rtt(QuicTime::Delta rtt) {
- smoothed_rtt_ = rtt;
-}
-
-bool InterArrivalStateMachine::PacketLossEvent() {
- QuicTime current_time = clock_->ApproximateNow();
- if (current_time.Subtract(last_loss_event_) < smoothed_rtt_) {
- // Less than one RTT have passed; ignore this event.
- return false;
- }
- last_loss_event_ = current_time;
- loss_event_count_++;
- if (current_time.Subtract(last_delay_event_) >
- QuicTime::Delta::FromMilliseconds(kEventTimeoutMs)) {
- // Delay event have timed out.
- delay_event_count_ = 0;
- }
- return true;
-}
-
-bool InterArrivalStateMachine::IncreasingDelayEvent() {
- QuicTime current_time = clock_->ApproximateNow();
- if (current_time.Subtract(last_delay_event_) < smoothed_rtt_) {
- // Less than one RTT have passed; ignore this event.
- return false;
- }
- last_delay_event_ = current_time;
- delay_event_count_++;
- if (current_time.Subtract(last_loss_event_) >
- QuicTime::Delta::FromMilliseconds(kEventTimeoutMs)) {
- // Loss event have timed out.
- loss_event_count_ = 0;
- }
- return true;
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine.h b/chromium/net/quic/congestion_control/inter_arrival_state_machine.h
deleted file mode 100644
index 829aa29938a..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_state_machine.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// State machine for congestion control. The state is updated by calls from
-// other modules as they detect events, or decide on taking specific actions.
-// Events include things like packet loss, or growing delay, while decisions
-// include decisions to increase or decrease bitrates.
-// This class should be called for every event and decision made by the
-// congestion control, this class will coalesce all calls relative to the
-// smoothed RTT.
-
-#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_
-#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_
-
-#include "net/base/net_export.h"
-#include "net/quic/quic_clock.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-// State transition diagram.
-//
-// kInterArrivalStatePacketLoss
-// |
-// kInterArrivalStateStable
-// |
-// kInterArrivalStateDelay
-// | |
-// kInterArrivalStateCompetingFlow -> kInterArrivalStateCompetingTcpFLow
-
-enum NET_EXPORT_PRIVATE InterArrivalState {
- // We are on a network with a delay that is too small to be reliably detected,
- // such as a local ethernet.
- kInterArrivalStatePacketLoss = 1,
- // We are on an underutilized network operating with low delay and low loss.
- kInterArrivalStateStable = 2,
- // We are on a network where we can detect delay changes and suffer only
- // low loss. Nothing indicates that we are competing with another flow.
- kInterArrivalStateDelay = 3,
- // We are on a network where we can detect delay changes and suffer only
- // low loss. We have indications that we are compete with another flow.
- kInterArrivalStateCompetingFlow = 4,
- // We are on a network where we can detect delay changes, however we suffer
- // packet loss due to sharing the bottleneck link with another flow, which is
- // most likely a TCP flow.
- kInterArrivalStateCompetingTcpFLow = 5,
-};
-
-class NET_EXPORT_PRIVATE InterArrivalStateMachine {
- public:
- explicit InterArrivalStateMachine(const QuicClock* clock);
-
- InterArrivalState GetInterArrivalState();
-
- // Inter arrival congestion control decided to increase bitrate.
- void IncreaseBitrateDecision();
-
- // Inter arrival congestion control decided to decrease bitrate.
- void DecreaseBitrateDecision();
-
- // Estimated smoothed round trip time.
- // This should be called whenever the smoothed RTT estimate is updated.
- void set_rtt(QuicTime::Delta rtt);
-
- // This method is called when a packet loss was reported.
- bool PacketLossEvent();
-
- // This method is called when we believe that packet transit delay is
- // increasing, presumably due to a growing queue along the path.
- bool IncreasingDelayEvent();
-
- private:
- const QuicClock* clock_;
- InterArrivalState current_state_;
- QuicTime::Delta smoothed_rtt_;
-
- int decrease_event_count_;
- QuicTime last_decrease_event_;
-
- int increase_event_count_;
- QuicTime last_increase_event_;
-
- int loss_event_count_;
- QuicTime last_loss_event_;
-
- int delay_event_count_;
- QuicTime last_delay_event_;
-
- DISALLOW_COPY_AND_ASSIGN(InterArrivalStateMachine);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_
diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc b/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc
deleted file mode 100644
index 1a4bc8523ea..00000000000
--- a/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/congestion_control/inter_arrival_state_machine.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class InterArrivalStateMachineTest : public ::testing::Test {
- protected:
- InterArrivalStateMachineTest() {
- }
-
- virtual void SetUp() {
- state_machine_.reset(new InterArrivalStateMachine(&clock_));
- }
-
- MockClock clock_;
- scoped_ptr<InterArrivalStateMachine> state_machine_;
-};
-
-TEST_F(InterArrivalStateMachineTest, SimplePacketLoss) {
- QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100);
- state_machine_->set_rtt(rtt);
- state_machine_->IncreaseBitrateDecision();
-
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateStable,
- state_machine_->GetInterArrivalState());
-
- // Make sure we switch to state packet loss.
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStatePacketLoss,
- state_machine_->GetInterArrivalState());
-
- // Make sure we stay in state packet loss.
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStatePacketLoss,
- state_machine_->GetInterArrivalState());
-
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStatePacketLoss,
- state_machine_->GetInterArrivalState());
-}
-
-TEST_F(InterArrivalStateMachineTest, SimpleDelay) {
- QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100);
- state_machine_->set_rtt(rtt);
- state_machine_->IncreaseBitrateDecision();
-
- clock_.AdvanceTime(rtt);
- state_machine_->IncreasingDelayEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateStable,
- state_machine_->GetInterArrivalState());
-
- // Make sure we switch to state delay.
- clock_.AdvanceTime(rtt);
- state_machine_->IncreasingDelayEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateDelay,
- state_machine_->GetInterArrivalState());
-
- clock_.AdvanceTime(rtt);
- state_machine_->IncreasingDelayEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateDelay,
- state_machine_->GetInterArrivalState());
-
- // Make sure we switch to state competing flow(s).
- clock_.AdvanceTime(rtt);
- state_machine_->IncreasingDelayEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingFlow,
- state_machine_->GetInterArrivalState());
-
- // Make sure we stay in state competing flow(s).
- clock_.AdvanceTime(rtt);
- state_machine_->IncreasingDelayEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingFlow,
- state_machine_->GetInterArrivalState());
-
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingFlow,
- state_machine_->GetInterArrivalState());
-
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingFlow,
- state_machine_->GetInterArrivalState());
-
- // Make sure we switch to state competing TCP flow(s).
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingTcpFLow,
- state_machine_->GetInterArrivalState());
-
- // Make sure we stay in state competing TCP flow(s).
- clock_.AdvanceTime(rtt);
- state_machine_->PacketLossEvent();
- state_machine_->DecreaseBitrateDecision();
- EXPECT_EQ(kInterArrivalStateCompetingTcpFLow,
- state_machine_->GetInterArrivalState());
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/leaky_bucket.cc b/chromium/net/quic/congestion_control/leaky_bucket.cc
index 06c1f87f3ae..f3972f62eac 100644
--- a/chromium/net/quic/congestion_control/leaky_bucket.cc
+++ b/chromium/net/quic/congestion_control/leaky_bucket.cc
@@ -24,11 +24,15 @@ void LeakyBucket::Add(QuicTime now, QuicByteCount bytes) {
bytes_ += bytes;
}
-QuicTime::Delta LeakyBucket::TimeRemaining(QuicTime now) {
- Update(now);
- return QuicTime::Delta::FromMicroseconds(
+QuicTime::Delta LeakyBucket::TimeRemaining(QuicTime now) const {
+ QuicTime::Delta time_since_last_update = now.Subtract(time_last_updated_);
+ QuicTime::Delta send_delay = QuicTime::Delta::FromMicroseconds(
(bytes_ * base::Time::kMicrosecondsPerSecond) /
draining_rate_.ToBytesPerSecond());
+ if (send_delay < time_since_last_update) {
+ return QuicTime::Delta::Zero();
+ }
+ return send_delay.Subtract(time_since_last_update);
}
QuicByteCount LeakyBucket::BytesPending(QuicTime now) {
diff --git a/chromium/net/quic/congestion_control/leaky_bucket.h b/chromium/net/quic/congestion_control/leaky_bucket.h
index 63ef8102a39..eb4cdb0c636 100644
--- a/chromium/net/quic/congestion_control/leaky_bucket.h
+++ b/chromium/net/quic/congestion_control/leaky_bucket.h
@@ -13,7 +13,6 @@
#include "base/basictypes.h"
#include "net/base/net_export.h"
#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_clock.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
@@ -30,7 +29,7 @@ class NET_EXPORT_PRIVATE LeakyBucket {
void Add(QuicTime now, QuicByteCount bytes);
// Time until the buffer is empty.
- QuicTime::Delta TimeRemaining(QuicTime now);
+ QuicTime::Delta TimeRemaining(QuicTime now) const;
// Number of bytes in the buffer.
QuicByteCount BytesPending(QuicTime now);
diff --git a/chromium/net/quic/congestion_control/loss_detection_interface.cc b/chromium/net/quic/congestion_control/loss_detection_interface.cc
new file mode 100644
index 00000000000..035848c9c01
--- /dev/null
+++ b/chromium/net/quic/congestion_control/loss_detection_interface.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/loss_detection_interface.h"
+
+#include "net/quic/congestion_control/tcp_loss_algorithm.h"
+#include "net/quic/congestion_control/time_loss_algorithm.h"
+
+namespace net {
+
+// Factory for loss detection algorithm.
+LossDetectionInterface* LossDetectionInterface::Create(
+ LossDetectionType loss_type) {
+ switch (loss_type) {
+ case kNack:
+ return new TCPLossAlgorithm();
+ case kTime:
+ return new TimeLossAlgorithm();
+ }
+ LOG(DFATAL) << "Unknown loss detection algorithm:" << loss_type;
+ return NULL;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/loss_detection_interface.h b/chromium/net/quic/congestion_control/loss_detection_interface.h
new file mode 100644
index 00000000000..5aaa51d46f6
--- /dev/null
+++ b/chromium/net/quic/congestion_control/loss_detection_interface.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The pure virtual class for send side loss detection algorithm.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
+#define NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class QuicUnackedPacketMap;
+class RttStats;
+
+class NET_EXPORT_PRIVATE LossDetectionInterface {
+ public:
+ // Creates a TCP loss detector.
+ static LossDetectionInterface* Create(LossDetectionType loss_type);
+
+ virtual ~LossDetectionInterface() {}
+
+ virtual LossDetectionType GetLossDetectionType() const = 0;
+
+ // Called when a new ack arrives or the loss alarm fires.
+ virtual SequenceNumberSet DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) = 0;
+
+ // Get the time the LossDetectionAlgorithm wants to re-evaluate losses.
+ // Returns QuicTime::Zero if no alarm needs to be set.
+ virtual QuicTime GetLossTimeout() const = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
diff --git a/chromium/net/quic/congestion_control/paced_sender.cc b/chromium/net/quic/congestion_control/paced_sender.cc
deleted file mode 100644
index 9a5cc448962..00000000000
--- a/chromium/net/quic/congestion_control/paced_sender.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/congestion_control/paced_sender.h"
-
-#include <algorithm>
-
-#include "net/quic/quic_protocol.h"
-
-namespace net {
-
-// To prevent too aggressive pacing we allow the following packet burst size.
-const int64 kMinPacketBurstSize = 2;
-// Max estimated time between calls to TimeUntilSend and
-// AvailableCongestionWindow.
-const int64 kMaxSchedulingDelayUs = 2000;
-
-PacedSender::PacedSender(QuicBandwidth estimate, QuicByteCount max_segment_size)
- : leaky_bucket_(estimate),
- pace_(estimate),
- max_segment_size_(kDefaultMaxPacketSize) {
-}
-
-void PacedSender::set_max_segment_size(QuicByteCount max_segment_size) {
- max_segment_size_ = max_segment_size;
-}
-
-void PacedSender::UpdateBandwidthEstimate(QuicTime now,
- QuicBandwidth estimate) {
- leaky_bucket_.SetDrainingRate(now, estimate);
- pace_ = estimate;
-}
-
-void PacedSender::OnPacketSent(QuicTime now, QuicByteCount bytes) {
- leaky_bucket_.Add(now, bytes);
-}
-
-QuicTime::Delta PacedSender::TimeUntilSend(QuicTime now,
- QuicTime::Delta time_until_send) {
- if (time_until_send.ToMicroseconds() >= kMaxSchedulingDelayUs) {
- return time_until_send;
- }
- // Pace the data.
- QuicByteCount pacing_window = pace_.ToBytesPerPeriod(
- QuicTime::Delta::FromMicroseconds(kMaxSchedulingDelayUs));
- QuicByteCount min_window_size = kMinPacketBurstSize * max_segment_size_;
- pacing_window = std::max(pacing_window, min_window_size);
-
- if (pacing_window > leaky_bucket_.BytesPending(now)) {
- // We have not filled our pacing window yet.
- return time_until_send;
- }
- return leaky_bucket_.TimeRemaining(now);
-}
-
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/paced_sender.h b/chromium/net/quic/congestion_control/paced_sender.h
deleted file mode 100644
index 6d7c42919d0..00000000000
--- a/chromium/net/quic/congestion_control/paced_sender.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Helper class that limits the congestion window to pace the packets.
-
-#ifndef NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_
-#define NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_
-
-#include "base/basictypes.h"
-#include "net/base/net_export.h"
-#include "net/quic/congestion_control/leaky_bucket.h"
-#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_time.h"
-
-namespace net {
-
-class NET_EXPORT_PRIVATE PacedSender {
- public:
- PacedSender(QuicBandwidth bandwidth_estimate, QuicByteCount max_segment_size);
-
- void set_max_segment_size(QuicByteCount max_segment_size);
-
- // The estimated bandidth from the congestion algorithm changed.
- void UpdateBandwidthEstimate(QuicTime now, QuicBandwidth bandwidth_estimate);
-
- // A packet of size bytes was sent.
- void OnPacketSent(QuicTime now, QuicByteCount bytes);
-
- // Return time until we can send based on the pacing.
- QuicTime::Delta TimeUntilSend(QuicTime now, QuicTime::Delta time_until_send);
-
- private:
- // Helper object to track the rate data can leave the buffer for pacing.
- LeakyBucket leaky_bucket_;
- QuicBandwidth pace_;
- QuicByteCount max_segment_size_;
-
- DISALLOW_COPY_AND_ASSIGN(PacedSender);
-};
-
-} // namespace net
-
-#endif // NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_
diff --git a/chromium/net/quic/congestion_control/paced_sender_test.cc b/chromium/net/quic/congestion_control/paced_sender_test.cc
deleted file mode 100644
index fa42c2ce579..00000000000
--- a/chromium/net/quic/congestion_control/paced_sender_test.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/congestion_control/paced_sender.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/test_tools/mock_clock.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-const int kHundredKBytesPerS = 100;
-
-class PacedSenderTest : public ::testing::Test {
- protected:
- PacedSenderTest()
- : zero_time_(QuicTime::Delta::Zero()),
- paced_sender_(new PacedSender(
- QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS),
- kDefaultMaxPacketSize)) {
- }
-
- const QuicTime::Delta zero_time_;
- MockClock clock_;
- scoped_ptr<PacedSender> paced_sender_;
-};
-
-TEST_F(PacedSenderTest, Basic) {
- paced_sender_->UpdateBandwidthEstimate(clock_.Now(),
- QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS * 10));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_EQ(static_cast<int64>(kDefaultMaxPacketSize * 2),
- paced_sender_->TimeUntilSend(
- clock_.Now(), zero_time_).ToMicroseconds());
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
-}
-
-TEST_F(PacedSenderTest, LowRate) {
- paced_sender_->UpdateBandwidthEstimate(clock_.Now(),
- QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_EQ(static_cast<int64>(kDefaultMaxPacketSize * 20),
- paced_sender_->TimeUntilSend(
- clock_.Now(), zero_time_).ToMicroseconds());
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
-}
-
-TEST_F(PacedSenderTest, HighRate) {
- QuicBandwidth bandwidth_estimate = QuicBandwidth::FromKBytesPerSecond(
- kHundredKBytesPerS * 100);
- paced_sender_->UpdateBandwidthEstimate(clock_.Now(), bandwidth_estimate);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
- for (int i = 0; i < 16; ++i) {
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_TRUE(paced_sender_->TimeUntilSend(
- clock_.Now(), zero_time_).IsZero());
- }
- paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize);
- EXPECT_EQ(2040, paced_sender_->TimeUntilSend(
- clock_.Now(), zero_time_).ToMicroseconds());
- clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20400));
- EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero());
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/pacing_sender.cc b/chromium/net/quic/congestion_control/pacing_sender.cc
index a20e7522750..97138f767d0 100644
--- a/chromium/net/quic/congestion_control/pacing_sender.cc
+++ b/chromium/net/quic/congestion_control/pacing_sender.cc
@@ -10,50 +10,44 @@ PacingSender::PacingSender(SendAlgorithmInterface* sender,
QuicTime::Delta alarm_granularity)
: sender_(sender),
alarm_granularity_(alarm_granularity),
+ last_delayed_packet_sent_time_(QuicTime::Zero()),
next_packet_send_time_(QuicTime::Zero()),
was_last_send_delayed_(false),
- max_segment_size_(kDefaultMaxPacketSize) {
+ has_valid_rtt_(false) {
}
PacingSender::~PacingSender() {}
-void PacingSender::SetMaxPacketSize(QuicByteCount max_packet_size) {
- max_segment_size_ = max_packet_size;
- sender_->SetMaxPacketSize(max_packet_size);
-}
-
void PacingSender::SetFromConfig(const QuicConfig& config, bool is_server) {
sender_->SetFromConfig(config, is_server);
}
void PacingSender::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SendAlgorithmInterface::SentPacketsMap& sent_packets) {
+ QuicTime feedback_receive_time) {
sender_->OnIncomingQuicCongestionFeedbackFrame(
- feedback, feedback_receive_time, sent_packets);
-}
-
-void PacingSender::OnPacketAcked(
- QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) {
- sender_->OnPacketAcked(acked_sequence_number, acked_bytes, rtt);
+ feedback, feedback_receive_time);
}
-void PacingSender::OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime ack_receive_time) {
- sender_->OnPacketLost(sequence_number, ack_receive_time);
+void PacingSender::OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) {
+ if (rtt_updated) {
+ has_valid_rtt_ = true;
+ }
+ sender_->OnCongestionEvent(
+ rtt_updated, bytes_in_flight, acked_packets, lost_packets);
}
bool PacingSender::OnPacketSent(
QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData has_retransmittable_data) {
- // Only pace data packets.
- if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA) {
+ // Only pace data packets once we have an updated RTT.
+ if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA && has_valid_rtt_) {
// The next packet should be sent as soon as the current packets has
// been transferred. We pace at twice the rate of the underlying
// sender's bandwidth estimate to help ensure that pacing doesn't become
@@ -61,29 +55,50 @@ bool PacingSender::OnPacketSent(
const float kPacingAggression = 2;
QuicTime::Delta delay =
BandwidthEstimate().Scale(kPacingAggression).TransferTime(bytes);
- next_packet_send_time_ = next_packet_send_time_.Add(delay);
+ // If the last send was delayed, and the alarm took a long time to get
+ // invoked, allow the connection to make up for lost time.
+ if (was_last_send_delayed_) {
+ next_packet_send_time_ = next_packet_send_time_.Add(delay);
+ // The send was application limited if it takes longer than the
+ // pacing delay between sent packets.
+ const bool application_limited =
+ last_delayed_packet_sent_time_.IsInitialized() &&
+ sent_time > last_delayed_packet_sent_time_.Add(delay);
+ const bool making_up_for_lost_time = next_packet_send_time_ <= sent_time;
+ // As long as we're making up time and not application limited,
+ // continue to consider the packets delayed, allowing the packets to be
+ // sent immediately.
+ if (making_up_for_lost_time && !application_limited) {
+ last_delayed_packet_sent_time_ = sent_time;
+ } else {
+ was_last_send_delayed_ = false;
+ last_delayed_packet_sent_time_ = QuicTime::Zero();
+ }
+ } else {
+ next_packet_send_time_ =
+ QuicTime::Max(next_packet_send_time_.Add(delay),
+ sent_time.Add(delay).Subtract(alarm_granularity_));
+ }
}
- return sender_->OnPacketSent(sent_time, sequence_number, bytes,
- transmission_type, has_retransmittable_data);
-}
-
-void PacingSender::OnRetransmissionTimeout() {
- sender_->OnRetransmissionTimeout();
+ return sender_->OnPacketSent(sent_time, bytes_in_flight, sequence_number,
+ bytes, has_retransmittable_data);
}
-void PacingSender::OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) {
- sender_->OnPacketAbandoned(sequence_number, abandoned_bytes);
+void PacingSender::OnRetransmissionTimeout(bool packets_retransmitted) {
+ sender_->OnRetransmissionTimeout(packets_retransmitted);
}
QuicTime::Delta PacingSender::TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) {
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const {
QuicTime::Delta time_until_send =
- sender_->TimeUntilSend(now, transmission_type,
- has_retransmittable_data, handshake);
+ sender_->TimeUntilSend(now, bytes_in_flight, has_retransmittable_data);
+ if (!has_valid_rtt_) {
+ // Don't pace if we don't have an updated RTT estimate.
+ return time_until_send;
+ }
+
if (!time_until_send.IsZero()) {
DCHECK(time_until_send.IsInfinite());
// The underlying sender prevents sending.
@@ -96,25 +111,14 @@ QuicTime::Delta PacingSender::TimeUntilSend(
return QuicTime::Delta::Zero();
}
- if (!was_last_send_delayed_ &&
- (!next_packet_send_time_.IsInitialized() ||
- now > next_packet_send_time_.Add(alarm_granularity_))) {
- // An alarm did not go off late, instead the application is "slow"
- // delivering data. In this case, we restrict the amount of lost time
- // that we can make up for.
- next_packet_send_time_ = now.Subtract(alarm_granularity_);
- }
-
- // If the end of the epoch is far enough in the future, delay the send.
+ // If the next send time is within the alarm granularity, send immediately.
if (next_packet_send_time_ > now.Add(alarm_granularity_)) {
- was_last_send_delayed_ = true;
DVLOG(1) << "Delaying packet: "
<< next_packet_send_time_.Subtract(now).ToMicroseconds();
+ was_last_send_delayed_ = true;
return next_packet_send_time_.Subtract(now);
}
- // Sent it immediately. The epoch end will be adjusted in OnPacketSent.
- was_last_send_delayed_ = false;
DVLOG(1) << "Sending packet now";
return QuicTime::Delta::Zero();
}
@@ -123,10 +127,6 @@ QuicBandwidth PacingSender::BandwidthEstimate() const {
return sender_->BandwidthEstimate();
}
-QuicTime::Delta PacingSender::SmoothedRtt() const {
- return sender_->SmoothedRtt();
-}
-
QuicTime::Delta PacingSender::RetransmissionDelay() const {
return sender_->RetransmissionDelay();
}
diff --git a/chromium/net/quic/congestion_control/pacing_sender.h b/chromium/net/quic/congestion_control/pacing_sender.h
index 92970b93ce9..c74177125d2 100644
--- a/chromium/net/quic/congestion_control/pacing_sender.h
+++ b/chromium/net/quic/congestion_control/pacing_sender.h
@@ -17,7 +17,6 @@
#include "base/memory/scoped_ptr.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_bandwidth.h"
-#include "net/quic/quic_clock.h"
#include "net/quic/quic_config.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
@@ -32,42 +31,35 @@ class NET_EXPORT_PRIVATE PacingSender : public SendAlgorithmInterface {
// SendAlgorithmInterface methods.
virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE;
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE;
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SendAlgorithmInterface::SentPacketsMap& sent_packets) OVERRIDE;
- virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) OVERRIDE;
- virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime ack_receive_time) OVERRIDE;
+ QuicTime feedback_receive_time) OVERRIDE;
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) OVERRIDE;
virtual bool OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData is_retransmittable) OVERRIDE;
- virtual void OnRetransmissionTimeout() OVERRIDE;
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) OVERRIDE;
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE;
virtual QuicTime::Delta TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) OVERRIDE;
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const OVERRIDE;
virtual QuicBandwidth BandwidthEstimate() const OVERRIDE;
- virtual QuicTime::Delta SmoothedRtt() const OVERRIDE;
virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE;
virtual QuicByteCount GetCongestionWindow() const OVERRIDE;
private:
- QuicTime::Delta GetTransferTime(QuicByteCount bytes);
-
scoped_ptr<SendAlgorithmInterface> sender_; // Underlying sender.
QuicTime::Delta alarm_granularity_;
+ // Send time of the last packet considered delayed.
+ QuicTime last_delayed_packet_sent_time_;
QuicTime next_packet_send_time_; // When can the next packet be sent.
- bool was_last_send_delayed_; // True when the last send was delayed.
- QuicByteCount max_segment_size_;
+ mutable bool was_last_send_delayed_; // True when the last send was delayed.
+ bool has_valid_rtt_; // True if we have at least one RTT update.
DISALLOW_COPY_AND_ASSIGN(PacingSender);
};
diff --git a/chromium/net/quic/congestion_control/pacing_sender_test.cc b/chromium/net/quic/congestion_control/pacing_sender_test.cc
index dfc78b84ece..2dc696098ff 100644
--- a/chromium/net/quic/congestion_control/pacing_sender_test.cc
+++ b/chromium/net/quic/congestion_control/pacing_sender_test.cc
@@ -13,10 +13,13 @@
using testing::Return;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace test {
+const QuicByteCount kBytesInFlight = 1024;
+
class PacingSenderTest : public ::testing::Test {
protected:
PacingSenderTest()
@@ -35,23 +38,24 @@ class PacingSenderTest : public ::testing::Test {
void CheckPacketIsSentImmediately() {
// In order for the packet to be sendable, the underlying sender must
// permit it to be sent immediately.
- EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE))
- .WillOnce(Return(zero_time_));
- // Verify that the packet can be sent immediately.
- EXPECT_EQ(zero_time_,
- pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE));
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(zero_time_));
+ // Verify that the packet can be sent immediately.
+ EXPECT_EQ(zero_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA));
+ }
// Actually send the packet.
EXPECT_CALL(*mock_sender_,
- OnPacketSent(clock_.Now(), sequence_number_, kMaxPacketSize,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
- pacing_sender_->OnPacketSent(clock_.Now(), sequence_number_++,
- kMaxPacketSize, NOT_RETRANSMISSION,
+ OnPacketSent(clock_.Now(), kBytesInFlight, sequence_number_,
+ kMaxPacketSize, HAS_RETRANSMITTABLE_DATA));
+ pacing_sender_->OnPacketSent(clock_.Now(), kBytesInFlight,
+ sequence_number_++, kMaxPacketSize,
HAS_RETRANSMITTABLE_DATA);
}
@@ -59,38 +63,38 @@ class PacingSenderTest : public ::testing::Test {
// In order for the ack to be sendable, the underlying sender must
// permit it to be sent immediately.
EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION,
- NO_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE))
+ 0,
+ NO_RETRANSMITTABLE_DATA))
.WillOnce(Return(zero_time_));
// Verify that the ACK can be sent immediately.
EXPECT_EQ(zero_time_,
- pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- NO_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE));
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ NO_RETRANSMITTABLE_DATA));
// Actually send the packet.
EXPECT_CALL(*mock_sender_,
- OnPacketSent(clock_.Now(), sequence_number_, kMaxPacketSize,
- NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA));
- pacing_sender_->OnPacketSent(clock_.Now(), sequence_number_++,
- kMaxPacketSize, NOT_RETRANSMISSION,
+ OnPacketSent(clock_.Now(), 0, sequence_number_,
+ kMaxPacketSize, NO_RETRANSMITTABLE_DATA));
+ pacing_sender_->OnPacketSent(clock_.Now(), 0,
+ sequence_number_++, kMaxPacketSize,
NO_RETRANSMITTABLE_DATA);
}
void CheckPacketIsDelayed(QuicTime::Delta delay) {
// In order for the packet to be sendable, the underlying sender must
// permit it to be sent immediately.
- EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE))
- .WillOnce(Return(zero_time_));
- // Verify that the packet is delayed.
- EXPECT_EQ(delay.ToMicroseconds(),
- pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE).ToMicroseconds());
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(zero_time_));
+ // Verify that the packet is delayed.
+ EXPECT_EQ(delay.ToMicroseconds(),
+ pacing_sender_->TimeUntilSend(
+ clock_.Now(), kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA).ToMicroseconds());
+ }
}
const QuicTime::Delta zero_time_;
@@ -102,27 +106,29 @@ class PacingSenderTest : public ::testing::Test {
};
TEST_F(PacingSenderTest, NoSend) {
- EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE))
- .WillOnce(Return(infinite_time_));
- EXPECT_EQ(infinite_time_,
- pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE));
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(infinite_time_));
+ EXPECT_EQ(infinite_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA));
+ }
}
TEST_F(PacingSenderTest, SendNow) {
- EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE))
- .WillOnce(Return(zero_time_));
- EXPECT_EQ(zero_time_,
- pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE));
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(zero_time_));
+ EXPECT_EQ(zero_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA));
+ }
}
TEST_F(PacingSenderTest, VariousSending) {
@@ -132,6 +138,16 @@ TEST_F(PacingSenderTest, VariousSending) {
.WillRepeatedly(Return(QuicBandwidth::FromBytesAndTimeDelta(
kMaxPacketSize, QuicTime::Delta::FromMilliseconds(2))));
+ // Send a whole pile of packets, and verify that they are not paced.
+ for (int i = 0 ; i < 1000; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ // Now update the RTT and verify that packets are actually paced.
+ EXPECT_CALL(*mock_sender_, OnCongestionEvent(true, kBytesInFlight, _, _));
+ SendAlgorithmInterface::CongestionMap empty_map;
+ pacing_sender_->OnCongestionEvent(true, kBytesInFlight, empty_map, empty_map);
+
CheckPacketIsSentImmediately();
CheckPacketIsSentImmediately();
CheckPacketIsSentImmediately();
@@ -155,6 +171,29 @@ TEST_F(PacingSenderTest, VariousSending) {
CheckPacketIsSentImmediately();
CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+ // Wake up really late.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up really late again, but application pause partway through.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
// Wake up too early.
CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
diff --git a/chromium/net/quic/congestion_control/quic_max_sized_map.h b/chromium/net/quic/congestion_control/quic_max_sized_map.h
deleted file mode 100644
index a4ed7769d61..00000000000
--- a/chromium/net/quic/congestion_control/quic_max_sized_map.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Simple max sized map. Automatically deletes the oldest element when the
-// max limit is reached.
-// Note: the ConstIterator will NOT be valid after an Insert or RemoveAll.
-#ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_
-#define NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_
-
-#include <stdlib.h>
-
-#include <list>
-#include <map>
-
-#include "base/basictypes.h"
-
-namespace net {
-
-template <class Key, class Value>
-class QuicMaxSizedMap {
- public:
- typedef typename std::multimap<Key, Value>::const_iterator ConstIterator;
-
- explicit QuicMaxSizedMap(size_t max_numer_of_items)
- : max_numer_of_items_(max_numer_of_items) {
- }
-
- size_t MaxSize() const {
- return max_numer_of_items_;
- }
-
- size_t Size() const {
- return table_.size();
- }
-
- void Insert(const Key& k, const Value& value) {
- if (Size() == MaxSize()) {
- ListIterator list_it = insert_order_.begin();
- table_.erase(*list_it);
- insert_order_.pop_front();
- }
- TableIterator it = table_.insert(std::pair<Key, Value>(k, value));
- insert_order_.push_back(it);
- }
-
- void RemoveAll() {
- table_.clear();
- insert_order_.clear();
- }
-
- // STL style const_iterator support.
- ConstIterator Find(const Key& k) const {
- return table_.find(k);
- }
-
- ConstIterator Begin() const {
- return ConstIterator(table_.begin());
- }
-
- ConstIterator End() const {
- return ConstIterator(table_.end());
- }
-
- private:
- typedef typename std::multimap<Key, Value>::iterator TableIterator;
- typedef typename std::list<TableIterator>::iterator ListIterator;
-
- const size_t max_numer_of_items_;
- std::multimap<Key, Value> table_;
- std::list<TableIterator> insert_order_;
-
- DISALLOW_COPY_AND_ASSIGN(QuicMaxSizedMap);
-};
-
-} // namespace net
-#endif // NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_
diff --git a/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc b/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc
deleted file mode 100644
index 89c05cccbaf..00000000000
--- a/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "net/quic/congestion_control/quic_max_sized_map.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-namespace test {
-
-class QuicMaxSizedMapTest : public ::testing::Test {
-};
-
-TEST_F(QuicMaxSizedMapTest, Basic) {
- QuicMaxSizedMap<int, int> test_map(100);
- EXPECT_EQ(100u, test_map.MaxSize());
- EXPECT_EQ(0u, test_map.Size());
- test_map.Insert(1, 2);
- test_map.Insert(1, 3);
- EXPECT_EQ(100u, test_map.MaxSize());
- EXPECT_EQ(2u, test_map.Size());
- test_map.RemoveAll();
- EXPECT_EQ(100u, test_map.MaxSize());
- EXPECT_EQ(0u, test_map.Size());
-}
-
-TEST_F(QuicMaxSizedMapTest, Find) {
- QuicMaxSizedMap<int, int> test_map(100);
- test_map.Insert(1, 2);
- test_map.Insert(1, 3);
- test_map.Insert(2, 4);
- test_map.Insert(3, 5);
- QuicMaxSizedMap<int, int>::ConstIterator it = test_map.Find(2);
- EXPECT_TRUE(it != test_map.End());
- EXPECT_EQ(4, it->second);
- it = test_map.Find(1);
- EXPECT_TRUE(it != test_map.End());
- EXPECT_EQ(2, it->second);
- ++it;
- EXPECT_TRUE(it != test_map.End());
- EXPECT_EQ(3, it->second);
-}
-
-TEST_F(QuicMaxSizedMapTest, Sort) {
- QuicMaxSizedMap<int, int> test_map(100);
- test_map.Insert(9, 9);
- test_map.Insert(8, 8);
- test_map.Insert(7, 7);
- test_map.Insert(6, 6);
- test_map.Insert(2, 2);
- test_map.Insert(4, 4);
- test_map.Insert(5, 5);
- test_map.Insert(3, 3);
- test_map.Insert(0, 0);
- test_map.Insert(1, 1);
- QuicMaxSizedMap<int, int>::ConstIterator it = test_map.Begin();
- for (int i = 0; i < 10; ++i, ++it) {
- EXPECT_TRUE(it != test_map.End());
- EXPECT_EQ(i, it->first);
- EXPECT_EQ(i, it->second);
- }
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/congestion_control/receive_algorithm_interface.cc b/chromium/net/quic/congestion_control/receive_algorithm_interface.cc
index a49f64f160a..5384bdcddf7 100644
--- a/chromium/net/quic/congestion_control/receive_algorithm_interface.cc
+++ b/chromium/net/quic/congestion_control/receive_algorithm_interface.cc
@@ -15,8 +15,12 @@ ReceiveAlgorithmInterface* ReceiveAlgorithmInterface::Create(
switch (type) {
case kTCP:
return new TcpReceiver();
+ case kTCPBBR:
+ LOG(DFATAL) << "TCPBBR is not yet supported.";
+ return NULL;
case kInterArrival:
- break; // TODO(pwestin) Implement.
+ LOG(DFATAL) << "InterArrivalSendAlgorithm no longer supported.";
+ return NULL;
case kFixRate:
return new FixRateReceiver();
}
diff --git a/chromium/net/quic/congestion_control/receive_algorithm_interface.h b/chromium/net/quic/congestion_control/receive_algorithm_interface.h
index 17e7793749a..cd96b022db2 100644
--- a/chromium/net/quic/congestion_control/receive_algorithm_interface.h
+++ b/chromium/net/quic/congestion_control/receive_algorithm_interface.h
@@ -30,12 +30,9 @@ class NET_EXPORT_PRIVATE ReceiveAlgorithmInterface {
// bytes: is the packet size in bytes including IP headers.
// sequence_number: is the unique sequence number from the QUIC packet header.
// timestamp: is the sent timestamp from the QUIC packet header.
- // revived: is set if the packet is lost and then recovered with help of FEC
- // (Forward Error Correction) packet(s).
virtual void RecordIncomingPacket(QuicByteCount bytes,
QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool revived) = 0;
+ QuicTime timestamp) = 0;
};
} // namespace net
diff --git a/chromium/net/quic/congestion_control/rtt_stats.cc b/chromium/net/quic/congestion_control/rtt_stats.cc
new file mode 100644
index 00000000000..745deb58cb3
--- /dev/null
+++ b/chromium/net/quic/congestion_control/rtt_stats.cc
@@ -0,0 +1,129 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/rtt_stats.h"
+
+namespace net {
+
+namespace {
+
+// Default initial rtt used before any samples are received.
+const int kInitialRttMs = 100;
+const float kAlpha = 0.125f;
+const float kOneMinusAlpha = (1 - kAlpha);
+const float kBeta = 0.25f;
+const float kOneMinusBeta = (1 - kBeta);
+const float kHalfWindow = 0.5f;
+const float kQuarterWindow = 0.25f;
+
+} // namespace
+
+RttStats::RttStats()
+ : latest_rtt_(QuicTime::Delta::Zero()),
+ min_rtt_(QuicTime::Delta::Zero()),
+ smoothed_rtt_(QuicTime::Delta::Zero()),
+ mean_deviation_(QuicTime::Delta::Zero()),
+ initial_rtt_us_(kInitialRttMs * base::Time::kMicrosecondsPerMillisecond),
+ num_min_rtt_samples_remaining_(0),
+ recent_min_rtt_window_(QuicTime::Delta::Infinite()) {}
+
+bool RttStats::HasUpdates() const {
+ return !smoothed_rtt_.IsZero();
+}
+
+void RttStats::SampleNewRecentMinRtt(uint32 num_samples) {
+ num_min_rtt_samples_remaining_ = num_samples;
+ new_min_rtt_ = RttSample();
+}
+
+// Updates the RTT based on a new sample.
+void RttStats::UpdateRtt(QuicTime::Delta send_delta,
+ QuicTime::Delta ack_delay,
+ QuicTime now) {
+ QuicTime::Delta rtt_sample(QuicTime::Delta::Zero());
+ if (send_delta > ack_delay) {
+ rtt_sample = send_delta.Subtract(ack_delay);
+ } else if (!HasUpdates()) {
+ // Even though we received information from the peer suggesting
+ // an invalid (negative) RTT, we can use the send delta as an
+ // approximation until we get a better estimate.
+ rtt_sample = send_delta;
+ }
+
+ if (rtt_sample.IsInfinite() || rtt_sample.IsZero()) {
+ DVLOG(1) << "Ignoring rtt, because it's "
+ << (rtt_sample.IsZero() ? "Zero" : "Infinite");
+ return;
+ }
+ // RTT can't be negative.
+ DCHECK_LT(0, rtt_sample.ToMicroseconds());
+
+ latest_rtt_ = rtt_sample;
+ // First time call or link delay decreases.
+ if (min_rtt_.IsZero() || min_rtt_ > rtt_sample) {
+ min_rtt_ = rtt_sample;
+ }
+ UpdateRecentMinRtt(rtt_sample, now);
+ // First time call.
+ if (!HasUpdates()) {
+ smoothed_rtt_ = rtt_sample;
+ mean_deviation_ = QuicTime::Delta::FromMicroseconds(
+ rtt_sample.ToMicroseconds() / 2);
+ } else {
+ mean_deviation_ = QuicTime::Delta::FromMicroseconds(
+ kOneMinusBeta * mean_deviation_.ToMicroseconds() +
+ kBeta * std::abs(smoothed_rtt_.Subtract(rtt_sample).ToMicroseconds()));
+ smoothed_rtt_ = smoothed_rtt_.Multiply(kOneMinusAlpha).Add(
+ rtt_sample.Multiply(kAlpha));
+ DVLOG(1) << "Cubic; smoothed_rtt(us):" << smoothed_rtt_.ToMicroseconds()
+ << " mean_deviation(us):" << mean_deviation_.ToMicroseconds();
+ }
+}
+
+void RttStats::UpdateRecentMinRtt(QuicTime::Delta rtt_sample, QuicTime now) {
+ // Recent min_rtt update.
+ if (num_min_rtt_samples_remaining_ > 0) {
+ --num_min_rtt_samples_remaining_;
+ if (new_min_rtt_.rtt.IsZero() || rtt_sample <= new_min_rtt_.rtt) {
+ new_min_rtt_ = RttSample(rtt_sample, now);
+ }
+ if (num_min_rtt_samples_remaining_ == 0) {
+ quarter_window_rtt_ = half_window_rtt_ = recent_min_rtt_ = new_min_rtt_;
+ }
+ }
+
+ // Update the three recent rtt samples.
+ if (recent_min_rtt_.rtt.IsZero() || rtt_sample <= recent_min_rtt_.rtt) {
+ recent_min_rtt_ = RttSample(rtt_sample, now);
+ quarter_window_rtt_ = half_window_rtt_ = recent_min_rtt_;
+ } else if (rtt_sample <= half_window_rtt_.rtt) {
+ half_window_rtt_ = RttSample(rtt_sample, now);
+ quarter_window_rtt_ = half_window_rtt_;
+ } else if (rtt_sample <= quarter_window_rtt_.rtt) {
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ }
+
+ // Expire old min rtt samples.
+ if (recent_min_rtt_.time < now.Subtract(recent_min_rtt_window_)) {
+ recent_min_rtt_ = half_window_rtt_;
+ half_window_rtt_ = quarter_window_rtt_;
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ } else if (half_window_rtt_.time <
+ now.Subtract(recent_min_rtt_window_.Multiply(kHalfWindow))) {
+ half_window_rtt_ = quarter_window_rtt_;
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ } else if (quarter_window_rtt_.time <
+ now.Subtract(recent_min_rtt_window_.Multiply(kQuarterWindow))) {
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ }
+}
+
+QuicTime::Delta RttStats::SmoothedRtt() const {
+ if (!HasUpdates()) {
+ return QuicTime::Delta::FromMicroseconds(initial_rtt_us_);
+ }
+ return smoothed_rtt_;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/rtt_stats.h b/chromium/net/quic/congestion_control/rtt_stats.h
new file mode 100644
index 00000000000..ee8d1809086
--- /dev/null
+++ b/chromium/net/quic/congestion_control/rtt_stats.h
@@ -0,0 +1,112 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A convenience class to store rtt samples and calculate smoothed rtt.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_
+#define NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+namespace test {
+class RttStatsPeer;
+} // namespace test
+
+class NET_EXPORT_PRIVATE RttStats {
+ public:
+ RttStats();
+
+ // Returns true if any RTT measurements have been made.
+ bool HasUpdates() const;
+
+ // Updates the RTT from an incoming ack which is received |send_delta| after
+ // the packet is sent and the peer reports the ack being delayed |ack_delay|.
+ void UpdateRtt(QuicTime::Delta send_delta,
+ QuicTime::Delta ack_delay,
+ QuicTime now);
+
+ // Forces RttStats to sample a new recent min rtt within the next
+ // |num_samples| UpdateRtt calls.
+ void SampleNewRecentMinRtt(uint32 num_samples);
+
+ QuicTime::Delta SmoothedRtt() const;
+
+ int64 initial_rtt_us() const {
+ return initial_rtt_us_;
+ }
+
+ // Sets an initial RTT to be used for SmoothedRtt before any RTT updates.
+ void set_initial_rtt_us(int64 initial_rtt_us) {
+ initial_rtt_us_ = initial_rtt_us;
+ }
+
+ QuicTime::Delta latest_rtt() const {
+ return latest_rtt_;
+ }
+
+ // Returns the min_rtt for the entire connection.
+ QuicTime::Delta min_rtt() const {
+ return min_rtt_;
+ }
+
+ // Returns the min_rtt since SampleNewRecentMinRtt has been called, or the
+ // min_rtt for the entire connection if SampleNewMinRtt was never called.
+ QuicTime::Delta recent_min_rtt() const {
+ return recent_min_rtt_.rtt;
+ }
+
+ QuicTime::Delta mean_deviation() const {
+ return mean_deviation_;
+ }
+
+ // Sets how old a recent min rtt sample can be.
+ void set_recent_min_rtt_window(QuicTime::Delta recent_min_rtt_window) {
+ recent_min_rtt_window_ = recent_min_rtt_window;
+ }
+
+ private:
+ friend class test::RttStatsPeer;
+
+ // Used to track a sampled RTT window.
+ struct RttSample {
+ RttSample() : rtt(QuicTime::Delta::Zero()), time(QuicTime::Zero()) { }
+ RttSample(QuicTime::Delta rtt, QuicTime time) : rtt(rtt), time(time) { }
+
+ QuicTime::Delta rtt;
+ QuicTime time; // Time the rtt sample was recorded.
+ };
+
+ // Implements the resampling algorithm and the windowed min rtt algorithm.
+ void UpdateRecentMinRtt(QuicTime::Delta rtt_sample, QuicTime now);
+
+ QuicTime::Delta latest_rtt_;
+ QuicTime::Delta min_rtt_;
+ QuicTime::Delta smoothed_rtt_;
+ // Mean RTT deviation during this session.
+ // Approximation of standard deviation, the error is roughly 1.25 times
+ // larger than the standard deviation, for a normally distributed signal.
+ QuicTime::Delta mean_deviation_;
+ int64 initial_rtt_us_;
+
+ RttSample new_min_rtt_;
+ uint32 num_min_rtt_samples_remaining_;
+
+ // State variables for Kathleen Nichols MinRTT algorithm.
+ QuicTime::Delta recent_min_rtt_window_;
+ RttSample recent_min_rtt_; // a in the windowed algorithm.
+ RttSample half_window_rtt_; // b in the sampled algorithm.
+ RttSample quarter_window_rtt_; // c in the sampled algorithm.
+
+ DISALLOW_COPY_AND_ASSIGN(RttStats);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_
diff --git a/chromium/net/quic/congestion_control/rtt_stats_test.cc b/chromium/net/quic/congestion_control/rtt_stats_test.cc
new file mode 100644
index 00000000000..f1c0fc1ce98
--- /dev/null
+++ b/chromium/net/quic/congestion_control/rtt_stats_test.cc
@@ -0,0 +1,160 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/rtt_stats.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class RttStatsPeer {
+ public:
+ static QuicTime::Delta GetHalfWindowRtt(const RttStats* rtt_stats) {
+ return rtt_stats->half_window_rtt_.rtt;
+ }
+
+ static QuicTime::Delta GetQuarterWindowRtt(const RttStats* rtt_stats) {
+ return rtt_stats->quarter_window_rtt_.rtt;
+ }
+};
+
+class RttStatsTest : public ::testing::Test {
+ protected:
+ RttStats rtt_stats_;
+};
+
+TEST_F(RttStatsTest, MinRtt) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100),
+ rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(10),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(10)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(20)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(30)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(40)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+}
+
+TEST_F(RttStatsTest, RecentMinRtt) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(10),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+
+ rtt_stats_.SampleNewRecentMinRtt(4);
+ for (int i = 0; i < 3; ++i) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ rtt_stats_.recent_min_rtt());
+ }
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50), rtt_stats_.recent_min_rtt());
+}
+
+TEST_F(RttStatsTest, WindowedRecentMinRtt) {
+ // Set the window to 99ms, so 25ms is more than a quarter rtt.
+ rtt_stats_.set_recent_min_rtt_window(QuicTime::Delta::FromMilliseconds(99));
+
+ QuicTime now = QuicTime::Zero();
+ QuicTime::Delta rtt_sample = QuicTime::Delta::FromMilliseconds(10);
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+
+ // Gradually increase the rtt samples and ensure the recent_min_rtt starts
+ // rising.
+ for (int i = 0; i < 8; ++i) {
+ now = now.Add(QuicTime::Delta::FromMilliseconds(25));
+ rtt_sample = rtt_sample.Add(QuicTime::Delta::FromMilliseconds(10));
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(10)),
+ RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ if (i < 3) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ rtt_stats_.recent_min_rtt());
+ } else if (i < 5) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(30),
+ rtt_stats_.recent_min_rtt());
+ } else if (i < 7) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50),
+ rtt_stats_.recent_min_rtt());
+ } else {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70),
+ rtt_stats_.recent_min_rtt());
+ }
+ }
+
+ // A new quarter rtt low sets that, but nothing else.
+ rtt_sample = rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(5));
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(5)),
+ RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70),
+ rtt_stats_.recent_min_rtt());
+
+ // A new half rtt low sets that and the quarter rtt low.
+ rtt_sample = rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(15));
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70),
+ rtt_stats_.recent_min_rtt());
+
+ // A new full window loss sets the recent_min_rtt, but not min_rtt.
+ rtt_sample = QuicTime::Delta::FromMilliseconds(65);
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, rtt_stats_.recent_min_rtt());
+
+ // A new all time low sets both the min_rtt and the recent_min_rtt.
+ rtt_sample = QuicTime::Delta::FromMilliseconds(5);
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+
+ EXPECT_EQ(rtt_sample, rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, rtt_stats_.recent_min_rtt());
+}
+
+
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/send_algorithm_interface.cc b/chromium/net/quic/congestion_control/send_algorithm_interface.cc
index 493d84e88ef..58b92bce081 100644
--- a/chromium/net/quic/congestion_control/send_algorithm_interface.cc
+++ b/chromium/net/quic/congestion_control/send_algorithm_interface.cc
@@ -12,17 +12,26 @@ namespace net {
const bool kUseReno = false;
+class RttStats;
+
// Factory for send side congestion control algorithm.
SendAlgorithmInterface* SendAlgorithmInterface::Create(
const QuicClock* clock,
- CongestionFeedbackType type) {
+ const RttStats* rtt_stats,
+ CongestionFeedbackType type,
+ QuicConnectionStats* stats) {
switch (type) {
case kTCP:
- return new TcpCubicSender(clock, kUseReno, kMaxTcpCongestionWindow);
+ return new TcpCubicSender(clock, rtt_stats, kUseReno,
+ kMaxTcpCongestionWindow, stats);
case kInterArrival:
- break; // TODO(pwestin) Implement.
+ LOG(DFATAL) << "InterArrivalSendAlgorithm no longer supported.";
+ return NULL;
case kFixRate:
- return new FixRateSender(clock);
+ return new FixRateSender(rtt_stats);
+ case kTCPBBR:
+ LOG(DFATAL) << "BbrTcpSender is not supported.";
+ return NULL;
}
return NULL;
}
diff --git a/chromium/net/quic/congestion_control/send_algorithm_interface.h b/chromium/net/quic/congestion_control/send_algorithm_interface.h
index 87a5f9c03fc..6b1c75597f7 100644
--- a/chromium/net/quic/congestion_control/send_algorithm_interface.h
+++ b/chromium/net/quic/congestion_control/send_algorithm_interface.h
@@ -15,101 +15,68 @@
#include "net/quic/quic_bandwidth.h"
#include "net/quic/quic_clock.h"
#include "net/quic/quic_config.h"
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
namespace net {
+class RttStats;
+
class NET_EXPORT_PRIVATE SendAlgorithmInterface {
public:
- class SentPacket {
- public:
- SentPacket(QuicByteCount bytes,
- QuicTime timestamp,
- HasRetransmittableData has_retransmittable_data)
- : bytes_sent_(bytes),
- send_timestamp_(timestamp),
- has_retransmittable_data_(has_retransmittable_data),
- nack_count_(0) {
- }
- QuicByteCount bytes_sent() const { return bytes_sent_; }
- const QuicTime& send_timestamp() const { return send_timestamp_; }
- HasRetransmittableData has_retransmittable_data() const {
- return has_retransmittable_data_;
- }
- size_t nack_count() const { return nack_count_; }
-
- void Nack(size_t min_nacks) {
- nack_count_ = std::max(min_nacks, nack_count_ + 1);
- }
-
- private:
- QuicByteCount bytes_sent_;
- QuicTime send_timestamp_;
- HasRetransmittableData has_retransmittable_data_;
- size_t nack_count_;
- };
-
- typedef std::map<QuicPacketSequenceNumber, SentPacket*> SentPacketsMap;
+ typedef std::map<QuicPacketSequenceNumber, TransmissionInfo> CongestionMap;
static SendAlgorithmInterface* Create(const QuicClock* clock,
- CongestionFeedbackType type);
+ const RttStats* rtt_stats,
+ CongestionFeedbackType type,
+ QuicConnectionStats* stats);
virtual ~SendAlgorithmInterface() {}
virtual void SetFromConfig(const QuicConfig& config, bool is_server) = 0;
- // Sets the maximum size of packets that will be sent.
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size) = 0;
-
// Called when we receive congestion feedback from remote peer.
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& sent_packets) = 0;
-
- // Called for each received ACK, with sequence number from remote peer.
- virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) = 0;
-
- // Indicates a loss event of one packet. |sequence_number| is the
- // sequence number of the lost packet.
- virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime ack_receive_time) = 0;
-
- // Inform that we sent x bytes to the wire, and if that was a retransmission.
- // Returns true if the packet should be tracked by the congestion manager,
- // false otherwise. This is used by implementations such as tcp_cubic_sender
- // that do not count outgoing ACK packets against the congestion window.
+ QuicTime feedback_receive_time) = 0;
+
+ // Indicates an update to the congestion state, caused either by an incoming
+ // ack or loss event timeout. |rtt_updated| indicates whether a new
+ // latest_rtt sample has been taken, |byte_in_flight| the bytes in flight
+ // prior to the congestion event. |acked_packets| and |lost_packets| are
+ // any packets considered acked or lost as a result of the congestion event.
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) = 0;
+
+ // Inform that we sent |bytes| to the wire, and if the packet is
+ // retransmittable. Returns true if the packet should be tracked by the
+ // congestion manager and included in bytes_in_flight, false otherwise.
+ // |bytes_in_flight| is the number of bytes in flight before the packet was
+ // sent.
// Note: this function must be called for every packet sent to the wire.
virtual bool OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData is_retransmittable) = 0;
- // Called when the retransmission timeout fires.
- virtual void OnRetransmissionTimeout() = 0;
-
- // Called when a packet is timed out.
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) = 0;
+ // Called when the retransmission timeout fires. Neither OnPacketAbandoned
+ // nor OnPacketLost will be called for these packets.
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) = 0;
// Calculate the time until we can send the next packet.
virtual QuicTime::Delta TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) = 0;
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const = 0;
// What's the current estimated bandwidth in bytes per second.
// Returns 0 when it does not have an estimate.
virtual QuicBandwidth BandwidthEstimate() const = 0;
- // TODO(satyamshekhar): Monitor MinRtt.
- virtual QuicTime::Delta SmoothedRtt() const = 0;
-
// Get the send algorithm specific retransmission delay, called RTO in TCP,
// Note 1: the caller is responsible for sanity checking this value.
// Note 2: this will return zero if we don't have enough data for an estimate.
diff --git a/chromium/net/quic/congestion_control/send_algorithm_simulator.cc b/chromium/net/quic/congestion_control/send_algorithm_simulator.cc
new file mode 100644
index 00000000000..d539013c10e
--- /dev/null
+++ b/chromium/net/quic/congestion_control/send_algorithm_simulator.cc
@@ -0,0 +1,273 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/send_algorithm_simulator.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "net/quic/crypto/quic_random.h"
+
+using std::list;
+using std::max;
+using std::min;
+
+namespace net {
+
+namespace {
+
+const QuicByteCount kPacketSize = 1200;
+
+} // namespace
+
+SendAlgorithmSimulator::SendAlgorithmSimulator(
+ SendAlgorithmInterface* send_algorithm,
+ MockClock* clock,
+ RttStats* rtt_stats,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta rtt)
+ : send_algorithm_(send_algorithm),
+ clock_(clock),
+ rtt_stats_(rtt_stats),
+ next_sent_(1),
+ last_acked_(0),
+ next_acked_(1),
+ lose_next_ack_(false),
+ bytes_in_flight_(0),
+ forward_loss_rate_(0),
+ reverse_loss_rate_(0),
+ loss_correlation_(0),
+ bandwidth_(bandwidth),
+ rtt_(rtt),
+ buffer_size_(1000000),
+ max_cwnd_(0),
+ min_cwnd_(100000),
+ max_cwnd_drop_(0),
+ last_cwnd_(0) {
+ uint32 seed = base::RandInt(0, std::numeric_limits<int32>::max());
+ DVLOG(1) << "Seeding SendAlgorithmSimulator with " << seed;
+ simple_random_.set_seed(seed);
+}
+
+SendAlgorithmSimulator::~SendAlgorithmSimulator() {}
+
+// Sends the specified number of bytes as quickly as possible and returns the
+// average bandwidth in bytes per second. The time elapsed is based on
+// waiting for all acks to arrive.
+QuicBandwidth SendAlgorithmSimulator::SendBytes(size_t num_bytes) {
+ const QuicTime start_time = clock_->Now();
+ size_t bytes_acked = 0;
+ while (bytes_acked < num_bytes) {
+ DVLOG(1) << "bytes_acked:" << bytes_acked << " bytes_in_flight_:"
+ << bytes_in_flight_ << " CWND(bytes):"
+ << send_algorithm_->GetCongestionWindow();
+ // Determine the times of next send and of the next ack arrival.
+ QuicTime::Delta send_delta = send_algorithm_->TimeUntilSend(
+ clock_->Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA);
+ // If we've already sent enough bytes, wait for them to be acked.
+ if (bytes_acked + bytes_in_flight_ >= num_bytes) {
+ send_delta = QuicTime::Delta::Infinite();
+ }
+ QuicTime::Delta ack_delta = NextAckDelta();
+ // If both times are infinite, fire a TLP.
+ if (ack_delta.IsInfinite() && send_delta.IsInfinite()) {
+ DVLOG(1) << "Both times are infinite, simulating a TLP.";
+ // TODO(ianswett): Use a more sophisticated TLP timer.
+ clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ SendDataNow();
+ } else if (ack_delta < send_delta) {
+ DVLOG(1) << "Handling ack, advancing time:"
+ << ack_delta.ToMicroseconds() << "us";
+ // Ack data all the data up to ack time and lose any missing sequence
+ // numbers.
+ clock_->AdvanceTime(ack_delta);
+ bytes_acked += HandlePendingAck();
+ } else {
+ DVLOG(1) << "Sending, advancing time:"
+ << send_delta.ToMicroseconds() << "us";
+ clock_->AdvanceTime(send_delta);
+ SendDataNow();
+ }
+ RecordStats();
+ }
+ return QuicBandwidth::FromBytesAndTimeDelta(
+ num_bytes, clock_->Now().Subtract(start_time));
+}
+
+// NextAck takes into account packet loss in both forward and reverse
+// direction, as well as correlated losses. And it assumes the receiver acks
+// every other packet when there is no loss.
+QuicTime::Delta SendAlgorithmSimulator::NextAckDelta() {
+ if (sent_packets_.empty() || AllPacketsLost()) {
+ DVLOG(1) << "No outstanding packets to cause acks. sent_packets_.size():"
+ << sent_packets_.size();
+ return QuicTime::Delta::Infinite();
+ }
+
+ // If necessary, determine next_acked_.
+ // This is only done once to ensure multiple calls return the same time.
+ FindNextAcked();
+
+ // If only one packet is acked, simulate a delayed ack.
+ if (next_acked_ - last_acked_ == 1) {
+ return sent_packets_.front().ack_time.Add(
+ QuicTime::Delta::FromMilliseconds(100)).Subtract(clock_->Now());
+ }
+ for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+ it != sent_packets_.end(); ++it) {
+ if (next_acked_ == it->sequence_number) {
+ return it->ack_time.Subtract(clock_->Now());
+ }
+ }
+ LOG(DFATAL) << "Error, next_acked_: " << next_acked_
+ << " should have been found in sent_packets_";
+ return QuicTime::Delta::Infinite();
+}
+
+bool SendAlgorithmSimulator::AllPacketsLost() {
+ for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+ it != sent_packets_.end(); ++it) {
+ if (it->ack_time.IsInitialized()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void SendAlgorithmSimulator::FindNextAcked() {
+ // TODO(ianswett): Add a simpler mode which acks every packet.
+ bool packets_lost = false;
+ if (next_acked_ == last_acked_) {
+ // Determine if the next ack is lost only once, to ensure determinism.
+ lose_next_ack_ =
+ reverse_loss_rate_ * kuint64max > simple_random_.RandUint64();
+ }
+ bool two_acks_remaining = lose_next_ack_;
+ next_acked_ = last_acked_;
+ // Remove any packets that are simulated as lost.
+ for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+ it != sent_packets_.end(); ++it) {
+ // Lost packets don't trigger an ack.
+ if (it->ack_time == QuicTime::Zero()) {
+ packets_lost = true;
+ continue;
+ }
+ // Buffer dropped packets are skipped automatically, but still end up
+ // being lost and cause acks to be sent immediately.
+ if (next_acked_ < it->sequence_number - 1) {
+ packets_lost = true;
+ }
+ next_acked_ = it->sequence_number;
+ if (packets_lost || (next_acked_ - last_acked_) % 2 == 0) {
+ if (two_acks_remaining) {
+ two_acks_remaining = false;
+ } else {
+ break;
+ }
+ }
+ }
+ DVLOG(1) << "FindNextAcked found next_acked_:" << next_acked_
+ << " last_acked:" << last_acked_;
+}
+
+int SendAlgorithmSimulator::HandlePendingAck() {
+ DCHECK_LT(last_acked_, next_acked_);
+ SendAlgorithmInterface::CongestionMap acked_packets;
+ SendAlgorithmInterface::CongestionMap lost_packets;
+ // Some entries may be missing from the sent_packets_ array, if they were
+ // dropped due to buffer overruns.
+ SentPacket largest_observed = sent_packets_.front();
+ while (last_acked_ < next_acked_) {
+ ++last_acked_;
+ TransmissionInfo info = TransmissionInfo();
+ info.bytes_sent = kPacketSize;
+ info.in_flight = true;
+ // If it's missing from the array, it's a loss.
+ if (sent_packets_.front().sequence_number > last_acked_) {
+ DVLOG(1) << "Lost packet:" << last_acked_
+ << " dropped by buffer overflow.";
+ lost_packets[last_acked_] = info;
+ continue;
+ }
+ if (sent_packets_.front().ack_time.IsInitialized()) {
+ acked_packets[last_acked_] = info;
+ } else {
+ lost_packets[last_acked_] = info;
+ }
+ // Remove all packets from the front to next_acked_.
+ largest_observed = sent_packets_.front();
+ sent_packets_.pop_front();
+ }
+
+ DCHECK(largest_observed.ack_time.IsInitialized());
+ rtt_stats_->UpdateRtt(
+ largest_observed.ack_time.Subtract(largest_observed.send_time),
+ QuicTime::Delta::Zero(),
+ clock_->Now());
+ send_algorithm_->OnCongestionEvent(
+ true, bytes_in_flight_, acked_packets, lost_packets);
+ DCHECK_LE(kPacketSize * (acked_packets.size() + lost_packets.size()),
+ bytes_in_flight_);
+ bytes_in_flight_ -=
+ kPacketSize * (acked_packets.size() + lost_packets.size());
+ return acked_packets.size() * kPacketSize;
+}
+
+void SendAlgorithmSimulator::SendDataNow() {
+ DVLOG(1) << "Sending packet:" << next_sent_ << " bytes_in_flight:"
+ << bytes_in_flight_;
+ send_algorithm_->OnPacketSent(
+ clock_->Now(), bytes_in_flight_,
+ next_sent_, kPacketSize, HAS_RETRANSMITTABLE_DATA);
+ // Lose the packet immediately if the buffer is full.
+ if (sent_packets_.size() * kPacketSize < buffer_size_) {
+ // TODO(ianswett): This buffer simulation is an approximation.
+ // An ack time of zero means loss.
+ bool packet_lost =
+ forward_loss_rate_ * kuint64max > simple_random_.RandUint64();
+ // Handle correlated loss.
+ if (!sent_packets_.empty() &&
+ !sent_packets_.back().ack_time.IsInitialized() &&
+ loss_correlation_ * kuint64max > simple_random_.RandUint64()) {
+ packet_lost = true;
+ }
+
+ QuicTime ack_time = clock_->Now().Add(rtt_);
+ // If the number of bytes in flight are less than the bdp, there's
+ // no buffering delay. Bytes lost from the buffer are not counted.
+ QuicByteCount bdp = bandwidth_.ToBytesPerPeriod(rtt_);
+ if (sent_packets_.size() * kPacketSize > bdp) {
+ QuicByteCount qsize = sent_packets_.size() * kPacketSize - bdp;
+ ack_time = ack_time.Add(bandwidth_.TransferTime(qsize));
+ }
+ // If the packet is lost, give it an ack time of Zero.
+ sent_packets_.push_back(SentPacket(
+ next_sent_, clock_->Now(), packet_lost ? QuicTime::Zero() : ack_time));
+ }
+ ++next_sent_;
+ bytes_in_flight_ += kPacketSize;
+}
+
+void SendAlgorithmSimulator::RecordStats() {
+ QuicByteCount cwnd = send_algorithm_->GetCongestionWindow();
+ max_cwnd_ = max(max_cwnd_, cwnd);
+ min_cwnd_ = min(min_cwnd_, cwnd);
+ if (last_cwnd_ > cwnd) {
+ max_cwnd_drop_ = max(max_cwnd_drop_, last_cwnd_ - cwnd);
+ }
+ last_cwnd_ = cwnd;
+}
+
+// Advance the time by |delta| without sending anything.
+void SendAlgorithmSimulator::AdvanceTime(QuicTime::Delta delta) {
+ clock_->AdvanceTime(delta);
+}
+
+// Elapsed time from the start of the connection.
+QuicTime SendAlgorithmSimulator::ElapsedTime() {
+ return clock_->Now();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/send_algorithm_simulator.h b/chromium/net/quic/congestion_control/send_algorithm_simulator.h
new file mode 100644
index 00000000000..691b0c82dbd
--- /dev/null
+++ b/chromium/net/quic/congestion_control/send_algorithm_simulator.h
@@ -0,0 +1,133 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A test only class to enable simulations of send algorithms.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
+#define NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
+namespace net {
+
+class SendAlgorithmSimulator {
+ public:
+ struct SentPacket {
+ SentPacket(QuicPacketSequenceNumber sequence_number,
+ QuicTime send_time,
+ QuicTime ack_time)
+ : sequence_number(sequence_number),
+ send_time(send_time),
+ ack_time(ack_time) {}
+ QuicPacketSequenceNumber sequence_number;
+ QuicTime send_time;
+ QuicTime ack_time;
+ };
+
+ // |rtt_stats| should be the same RttStats used by the |send_algorithm|.
+ SendAlgorithmSimulator(SendAlgorithmInterface* send_algorithm,
+ MockClock* clock_,
+ RttStats* rtt_stats,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta rtt);
+ ~SendAlgorithmSimulator();
+
+ void set_forward_loss_rate(float loss_rate) {
+ DCHECK_LT(loss_rate, 1.0f);
+ forward_loss_rate_ = loss_rate;
+ }
+
+ void set_reverse_loss_rate(float loss_rate) {
+ DCHECK_LT(loss_rate, 1.0f);
+ reverse_loss_rate_ = loss_rate;
+ }
+
+ void set_loss_correlation(float loss_correlation) {
+ DCHECK_LT(loss_correlation, 1.0f);
+ loss_correlation_ = loss_correlation;
+ }
+
+ void set_buffer_size(size_t buffer_size_bytes) {
+ buffer_size_ = buffer_size_bytes;
+ }
+
+ // Sends the specified number of bytes as quickly as possible and returns the
+ // average bandwidth in bytes per second. The time elapsed is based on
+ // waiting for all acks to arrive.
+ QuicBandwidth SendBytes(size_t num_bytes);
+
+ const RttStats* rtt_stats() const { return rtt_stats_; }
+
+ QuicByteCount max_cwnd() const { return max_cwnd_; }
+ QuicByteCount min_cwnd() const { return min_cwnd_; }
+ QuicByteCount max_cwnd_drop() const { return max_cwnd_drop_; }
+ QuicByteCount last_cwnd() const { return last_cwnd_; }
+
+ private:
+ // NextAckTime takes into account packet loss in both forward and reverse
+ // direction, as well as delayed ack behavior.
+ QuicTime::Delta NextAckDelta();
+
+ // Whether all packets in sent_packets_ are lost.
+ bool AllPacketsLost();
+
+ // Sets the next acked.
+ void FindNextAcked();
+
+ // Process all the acks that should have arrived by the current time, and
+ // lose any packets that are missing. Returns the number of bytes acked.
+ int HandlePendingAck();
+
+ void SendDataNow();
+ void RecordStats();
+
+ // Advance the time by |delta| without sending anything.
+ void AdvanceTime(QuicTime::Delta delta);
+
+ // Elapsed time from the start of the connection.
+ QuicTime ElapsedTime();
+
+ SendAlgorithmInterface* send_algorithm_;
+ MockClock* clock_;
+ RttStats* rtt_stats_;
+ // Next packet sequence number to send.
+ QuicPacketSequenceNumber next_sent_;
+ // Last packet sequence number acked.
+ QuicPacketSequenceNumber last_acked_;
+ // Packet sequence number to ack up to.
+ QuicPacketSequenceNumber next_acked_;
+ // Whether the next ack should be lost.
+ bool lose_next_ack_;
+ QuicByteCount bytes_in_flight_;
+ // The times acks are expected, assuming acks are not lost and every packet
+ // is acked.
+ std::list<SentPacket> sent_packets_;
+
+ test::SimpleRandom simple_random_;
+ float forward_loss_rate_; // Loss rate on the forward path.
+ float reverse_loss_rate_; // Loss rate on the reverse path.
+ float loss_correlation_; // Likelihood the subsequent packet is lost.
+ QuicBandwidth bandwidth_;
+ QuicTime::Delta rtt_;
+ size_t buffer_size_; // In bytes.
+
+ // Stats collected for understanding the congestion control.
+ QuicByteCount max_cwnd_;
+ QuicByteCount min_cwnd_;
+ QuicByteCount max_cwnd_drop_;
+ QuicByteCount last_cwnd_;
+
+ DISALLOW_COPY_AND_ASSIGN(SendAlgorithmSimulator);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender.cc b/chromium/net/quic/congestion_control/tcp_cubic_sender.cc
index 50fc3937b99..19f07d40e55 100644
--- a/chromium/net/quic/congestion_control/tcp_cubic_sender.cc
+++ b/chromium/net/quic/congestion_control/tcp_cubic_sender.cc
@@ -7,8 +7,10 @@
#include <algorithm>
#include "base/metrics/histogram.h"
+#include "net/quic/congestion_control/rtt_stats.h"
using std::max;
+using std::min;
namespace net {
@@ -17,311 +19,306 @@ namespace {
// The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a
// fast retransmission. The cwnd after a timeout is still 1.
const QuicTcpCongestionWindow kMinimumCongestionWindow = 2;
-const int64 kHybridStartLowWindow = 16;
const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS;
const QuicByteCount kDefaultReceiveWindow = 64000;
const int64 kInitialCongestionWindow = 10;
const int kMaxBurstLength = 3;
-// Constants used for RTT calculation.
-const int kInitialRttMs = 60; // At a typical RTT 60 ms.
-const float kAlpha = 0.125f;
-const float kOneMinusAlpha = (1 - kAlpha);
-const float kBeta = 0.25f;
-const float kOneMinusBeta = (1 - kBeta);
}; // namespace
TcpCubicSender::TcpCubicSender(
const QuicClock* clock,
+ const RttStats* rtt_stats,
bool reno,
- QuicTcpCongestionWindow max_tcp_congestion_window)
+ QuicTcpCongestionWindow max_tcp_congestion_window,
+ QuicConnectionStats* stats)
: hybrid_slow_start_(clock),
- cubic_(clock),
+ cubic_(clock, stats),
+ rtt_stats_(rtt_stats),
+ stats_(stats),
reno_(reno),
congestion_window_count_(0),
receive_window_(kDefaultReceiveWindow),
- last_received_accumulated_number_of_lost_packets_(0),
- bytes_in_flight_(0),
- update_end_sequence_number_(true),
- end_sequence_number_(0),
+ prr_out_(0),
+ prr_delivered_(0),
+ ack_count_since_loss_(0),
+ bytes_in_flight_before_loss_(0),
largest_sent_sequence_number_(0),
largest_acked_sequence_number_(0),
largest_sent_at_last_cutback_(0),
congestion_window_(kInitialCongestionWindow),
slowstart_threshold_(max_tcp_congestion_window),
- max_tcp_congestion_window_(max_tcp_congestion_window),
- delay_min_(QuicTime::Delta::Zero()),
- smoothed_rtt_(QuicTime::Delta::Zero()),
- mean_deviation_(QuicTime::Delta::Zero()) {
+ last_cutback_exited_slowstart_(false),
+ max_tcp_congestion_window_(max_tcp_congestion_window) {
}
TcpCubicSender::~TcpCubicSender() {
UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_);
}
-void TcpCubicSender::SetMaxPacketSize(QuicByteCount /*max_packet_size*/) {
+bool TcpCubicSender::InSlowStart() const {
+ return congestion_window_ < slowstart_threshold_;
}
void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) {
- if (is_server) {
+ if (is_server && config.HasReceivedInitialCongestionWindow()) {
// Set the initial window size.
- congestion_window_ = config.server_initial_congestion_window();
+ congestion_window_ = min(kMaxInitialWindow,
+ config.ReceivedInitialCongestionWindow());
}
}
void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& /*sent_packets*/) {
- if (last_received_accumulated_number_of_lost_packets_ !=
- feedback.tcp.accumulated_number_of_lost_packets) {
- int recovered_lost_packets =
- last_received_accumulated_number_of_lost_packets_ -
- feedback.tcp.accumulated_number_of_lost_packets;
- last_received_accumulated_number_of_lost_packets_ =
- feedback.tcp.accumulated_number_of_lost_packets;
- if (recovered_lost_packets > 0) {
- // Assume the loss could be as late as the last acked packet.
- OnPacketLost(largest_acked_sequence_number_, feedback_receive_time);
- }
- }
+ QuicTime feedback_receive_time) {
receive_window_ = feedback.tcp.receive_window;
}
+void TcpCubicSender::OnCongestionEvent(
+ bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) {
+ if (rtt_updated && InSlowStart() &&
+ hybrid_slow_start_.ShouldExitSlowStart(rtt_stats_->latest_rtt(),
+ rtt_stats_->min_rtt(),
+ congestion_window_)) {
+ slowstart_threshold_ = congestion_window_;
+ }
+ for (CongestionMap::const_iterator it = lost_packets.begin();
+ it != lost_packets.end(); ++it) {
+ OnPacketLost(it->first, bytes_in_flight);
+ }
+ for (CongestionMap::const_iterator it = acked_packets.begin();
+ it != acked_packets.end(); ++it) {
+ OnPacketAcked(it->first, it->second.bytes_sent, bytes_in_flight);
+ }
+}
+
void TcpCubicSender::OnPacketAcked(
QuicPacketSequenceNumber acked_sequence_number,
QuicByteCount acked_bytes,
- QuicTime::Delta rtt) {
- DCHECK_GE(bytes_in_flight_, acked_bytes);
- bytes_in_flight_ -= acked_bytes;
+ QuicByteCount bytes_in_flight) {
largest_acked_sequence_number_ = max(acked_sequence_number,
largest_acked_sequence_number_);
- CongestionAvoidance(acked_sequence_number);
- AckAccounting(rtt);
- if (end_sequence_number_ == acked_sequence_number) {
- DVLOG(1) << "Start update end sequence number @" << acked_sequence_number;
- update_end_sequence_number_ = true;
+ if (InRecovery()) {
+ PrrOnPacketAcked(acked_bytes);
+ return;
}
+ MaybeIncreaseCwnd(acked_sequence_number, bytes_in_flight);
+ // TODO(ianswett): Should this even be called when not in slow start?
+ hybrid_slow_start_.OnPacketAcked(acked_sequence_number, InSlowStart());
}
void TcpCubicSender::OnPacketLost(QuicPacketSequenceNumber sequence_number,
- QuicTime /*ack_receive_time*/) {
+ QuicByteCount bytes_in_flight) {
// TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets
// already sent should be treated as a single loss event, since it's expected.
if (sequence_number <= largest_sent_at_last_cutback_) {
+ if (last_cutback_exited_slowstart_) {
+ ++stats_->slowstart_packets_lost;
+ }
DVLOG(1) << "Ignoring loss for largest_missing:" << sequence_number
- << " because it was sent prior to the last CWND cutback.";
+ << " because it was sent prior to the last CWND cutback.";
return;
}
+ ++stats_->tcp_loss_events;
+ last_cutback_exited_slowstart_ = InSlowStart();
+ if (InSlowStart()) {
+ ++stats_->slowstart_packets_lost;
+ }
+ PrrOnPacketLost(bytes_in_flight);
- // In a normal TCP we would need to know the lowest missing packet to detect
- // if we receive 3 missing packets. Here we get a missing packet for which we
- // enter TCP Fast Retransmit immediately.
if (reno_) {
congestion_window_ = congestion_window_ >> 1;
- slowstart_threshold_ = congestion_window_;
} else {
congestion_window_ =
cubic_.CongestionWindowAfterPacketLoss(congestion_window_);
- slowstart_threshold_ = congestion_window_;
}
- // Enforce TCP's minimimum congestion window of 2*MSS.
+ slowstart_threshold_ = congestion_window_;
+ // Enforce TCP's minimum congestion window of 2*MSS.
if (congestion_window_ < kMinimumCongestionWindow) {
congestion_window_ = kMinimumCongestionWindow;
}
largest_sent_at_last_cutback_ = largest_sent_sequence_number_;
- DVLOG(1) << "Incoming loss; congestion window:" << congestion_window_;
+ // reset packet count from congestion avoidance mode. We start
+ // counting again when we're out of recovery.
+ congestion_window_count_ = 0;
+ DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
}
bool TcpCubicSender::OnPacketSent(QuicTime /*sent_time*/,
+ QuicByteCount /*bytes_in_flight*/,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData is_retransmittable) {
// Only update bytes_in_flight_ for data packets.
if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) {
return false;
}
- bytes_in_flight_ += bytes;
+ prr_out_ += bytes;
if (largest_sent_sequence_number_ < sequence_number) {
// TODO(rch): Ensure that packets are really sent in order.
// DCHECK_LT(largest_sent_sequence_number_, sequence_number);
largest_sent_sequence_number_ = sequence_number;
}
- if (transmission_type == NOT_RETRANSMISSION && update_end_sequence_number_) {
- end_sequence_number_ = sequence_number;
- if (AvailableSendWindow() == 0) {
- update_end_sequence_number_ = false;
- DVLOG(1) << "Stop update end sequence number @" << sequence_number;
- }
- }
+ hybrid_slow_start_.OnPacketSent(sequence_number);
return true;
}
-void TcpCubicSender::OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) {
- DCHECK_GE(bytes_in_flight_, abandoned_bytes);
- bytes_in_flight_ -= abandoned_bytes;
-}
-
QuicTime::Delta TcpCubicSender::TimeUntilSend(
QuicTime /* now */,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) {
- if (transmission_type == NACK_RETRANSMISSION ||
- has_retransmittable_data == NO_RETRANSMITTABLE_DATA ||
- handshake == IS_HANDSHAKE) {
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const {
+ if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) {
// For TCP we can always send an ACK immediately.
- // We also immediately send any handshake packet (CHLO, etc.). We provide
- // this special dispensation for handshake messages in QUIC, although the
- // concept is not present in TCP.
return QuicTime::Delta::Zero();
}
- if (AvailableSendWindow() == 0) {
- return QuicTime::Delta::Infinite();
+ if (InRecovery()) {
+ return PrrTimeUntilSend(bytes_in_flight);
}
- return QuicTime::Delta::Zero();
-}
-
-QuicByteCount TcpCubicSender::AvailableSendWindow() {
- if (bytes_in_flight_ > SendWindow()) {
- return 0;
+ if (SendWindow() > bytes_in_flight) {
+ return QuicTime::Delta::Zero();
}
- return SendWindow() - bytes_in_flight_;
+ return QuicTime::Delta::Infinite();
}
-QuicByteCount TcpCubicSender::SendWindow() {
+QuicByteCount TcpCubicSender::SendWindow() const {
// What's the current send window in bytes.
- return std::min(receive_window_, GetCongestionWindow());
+ return min(receive_window_, GetCongestionWindow());
}
QuicBandwidth TcpCubicSender::BandwidthEstimate() const {
return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(),
- SmoothedRtt());
-}
-
-QuicTime::Delta TcpCubicSender::SmoothedRtt() const {
- if (smoothed_rtt_.IsZero()) {
- return QuicTime::Delta::FromMilliseconds(kInitialRttMs);
- }
- return smoothed_rtt_;
+ rtt_stats_->SmoothedRtt());
}
QuicTime::Delta TcpCubicSender::RetransmissionDelay() const {
+ if (!rtt_stats_->HasUpdates()) {
+ return QuicTime::Delta::Zero();
+ }
return QuicTime::Delta::FromMicroseconds(
- smoothed_rtt_.ToMicroseconds() + 4 * mean_deviation_.ToMicroseconds());
+ rtt_stats_->SmoothedRtt().ToMicroseconds() +
+ 4 * rtt_stats_->mean_deviation().ToMicroseconds());
}
QuicByteCount TcpCubicSender::GetCongestionWindow() const {
return congestion_window_ * kMaxSegmentSize;
}
-void TcpCubicSender::Reset() {
- delay_min_ = QuicTime::Delta::Zero();
- hybrid_slow_start_.Restart();
-}
-
-bool TcpCubicSender::IsCwndLimited() const {
+bool TcpCubicSender::IsCwndLimited(QuicByteCount bytes_in_flight) const {
const QuicByteCount congestion_window_bytes = congestion_window_ *
kMaxSegmentSize;
- if (bytes_in_flight_ >= congestion_window_bytes) {
+ if (bytes_in_flight >= congestion_window_bytes) {
return true;
}
- const QuicByteCount tcp_max_burst = kMaxBurstLength * kMaxSegmentSize;
- const QuicByteCount left = congestion_window_bytes - bytes_in_flight_;
- return left <= tcp_max_burst;
+ const QuicByteCount max_burst = kMaxBurstLength * kMaxSegmentSize;
+ const QuicByteCount available_bytes =
+ congestion_window_bytes - bytes_in_flight;
+ const bool slow_start_limited = InSlowStart() &&
+ bytes_in_flight > congestion_window_bytes / 2;
+ return slow_start_limited || available_bytes <= max_burst;
+}
+
+bool TcpCubicSender::InRecovery() const {
+ return largest_acked_sequence_number_ <= largest_sent_at_last_cutback_ &&
+ largest_acked_sequence_number_ != 0;
}
// Called when we receive an ack. Normal TCP tracks how many packets one ack
// represents, but quic has a separate ack for each packet.
-void TcpCubicSender::CongestionAvoidance(QuicPacketSequenceNumber ack) {
- if (!IsCwndLimited()) {
+void TcpCubicSender::MaybeIncreaseCwnd(
+ QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount bytes_in_flight) {
+ LOG_IF(DFATAL, InRecovery()) << "Never increase the CWND during recovery.";
+ if (!IsCwndLimited(bytes_in_flight)) {
// We don't update the congestion window unless we are close to using the
// window we have available.
return;
}
- if (congestion_window_ < slowstart_threshold_) {
- // Slow start.
- if (hybrid_slow_start_.EndOfRound(ack)) {
- hybrid_slow_start_.Reset(end_sequence_number_);
- }
+ if (InSlowStart()) {
// congestion_window_cnt is the number of acks since last change of snd_cwnd
if (congestion_window_ < max_tcp_congestion_window_) {
// TCP slow start, exponential growth, increase by one for each ACK.
- congestion_window_++;
+ ++congestion_window_;
}
- DVLOG(1) << "Slow start; congestion window:" << congestion_window_;
- } else {
- if (congestion_window_ < max_tcp_congestion_window_) {
- if (reno_) {
- // Classic Reno congestion avoidance provided for testing.
- if (congestion_window_count_ >= congestion_window_) {
- congestion_window_++;
- congestion_window_count_ = 0;
- } else {
- congestion_window_count_++;
- }
- DVLOG(1) << "Reno; congestion window:" << congestion_window_;
- } else {
- congestion_window_ = std::min(
- max_tcp_congestion_window_,
- cubic_.CongestionWindowAfterAck(congestion_window_, delay_min_));
- DVLOG(1) << "Cubic; congestion window:" << congestion_window_;
- }
+ DVLOG(1) << "Slow start; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
+ return;
+ }
+ if (congestion_window_ >= max_tcp_congestion_window_) {
+ return;
+ }
+ // Congestion avoidance
+ if (reno_) {
+ // Classic Reno congestion avoidance provided for testing.
+
+ ++congestion_window_count_;
+ if (congestion_window_count_ >= congestion_window_) {
+ ++congestion_window_;
+ congestion_window_count_ = 0;
}
+
+ DVLOG(1) << "Reno; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_
+ << " congestion window count: " << congestion_window_count_;
+ } else {
+ congestion_window_ = min(max_tcp_congestion_window_,
+ cubic_.CongestionWindowAfterAck(
+ congestion_window_, rtt_stats_->min_rtt()));
+ DVLOG(1) << "Cubic; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
}
}
-void TcpCubicSender::OnRetransmissionTimeout() {
- cubic_.Reset();
- congestion_window_ = kMinimumCongestionWindow;
+void TcpCubicSender::OnRetransmissionTimeout(bool packets_retransmitted) {
+ largest_sent_at_last_cutback_ = 0;
+ if (packets_retransmitted) {
+ cubic_.Reset();
+ hybrid_slow_start_.Restart();
+ congestion_window_ = kMinimumCongestionWindow;
+ }
}
-void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) {
- if (rtt.IsInfinite() || rtt.IsZero()) {
- DVLOG(1) << "Ignoring rtt, because it's "
- << (rtt.IsZero() ? "Zero" : "Infinite");
- return;
- }
- // RTT can't be negative.
- DCHECK_LT(0, rtt.ToMicroseconds());
+void TcpCubicSender::PrrOnPacketLost(QuicByteCount bytes_in_flight) {
+ prr_out_ = 0;
+ bytes_in_flight_before_loss_ = bytes_in_flight;
+ prr_delivered_ = 0;
+ ack_count_since_loss_ = 0;
+}
- // TODO(pwestin): Discard delay samples right after fast recovery,
- // during 1 second?.
+void TcpCubicSender::PrrOnPacketAcked(QuicByteCount acked_bytes) {
+ prr_delivered_ += acked_bytes;
+ ++ack_count_since_loss_;
+}
- // First time call or link delay decreases.
- if (delay_min_.IsZero() || delay_min_ > rtt) {
- delay_min_ = rtt;
- }
- // First time call.
- if (smoothed_rtt_.IsZero()) {
- smoothed_rtt_ = rtt;
- mean_deviation_ = QuicTime::Delta::FromMicroseconds(
- rtt.ToMicroseconds() / 2);
- } else {
- mean_deviation_ = QuicTime::Delta::FromMicroseconds(
- kOneMinusBeta * mean_deviation_.ToMicroseconds() +
- kBeta * abs(smoothed_rtt_.ToMicroseconds() - rtt.ToMicroseconds()));
- smoothed_rtt_ = QuicTime::Delta::FromMicroseconds(
- kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() +
- kAlpha * rtt.ToMicroseconds());
- DVLOG(1) << "Cubic; smoothed_rtt_:" << smoothed_rtt_.ToMicroseconds()
- << " mean_deviation_:" << mean_deviation_.ToMicroseconds();
+QuicTime::Delta TcpCubicSender::PrrTimeUntilSend(
+ QuicByteCount bytes_in_flight) const {
+ DCHECK(InRecovery());
+ // Return QuicTime::Zero In order to ensure limited transmit always works.
+ if (prr_out_ == 0) {
+ return QuicTime::Delta::Zero();
}
-
- // Hybrid start triggers when cwnd is larger than some threshold.
- if (congestion_window_ <= slowstart_threshold_ &&
- congestion_window_ >= kHybridStartLowWindow) {
- if (!hybrid_slow_start_.started()) {
- // Time to start the hybrid slow start.
- hybrid_slow_start_.Reset(end_sequence_number_);
- }
- hybrid_slow_start_.Update(rtt, delay_min_);
- if (hybrid_slow_start_.Exit()) {
- slowstart_threshold_ = congestion_window_;
+ if (SendWindow() > bytes_in_flight) {
+ // During PRR-SSRB, limit outgoing packets to 1 extra MSS per ack, instead
+ // of sending the entire available window. This prevents burst retransmits
+ // when more packets are lost than the CWND reduction.
+ // limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS
+ if (prr_delivered_ + ack_count_since_loss_ * kMaxSegmentSize <= prr_out_) {
+ return QuicTime::Delta::Infinite();
}
+ return QuicTime::Delta::Zero();
+ }
+ // Implement Proportional Rate Reduction (RFC6937)
+ // Checks a simplified version of the PRR formula that doesn't use division:
+ // AvailableSendWindow =
+ // CEIL(prr_delivered * ssthresh / BytesInFlightAtLoss) - prr_sent
+ if (prr_delivered_ * slowstart_threshold_ * kMaxSegmentSize >
+ prr_out_ * bytes_in_flight_before_loss_) {
+ return QuicTime::Delta::Zero();
}
+ return QuicTime::Delta::Infinite();
}
} // namespace net
diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender.h b/chromium/net/quic/congestion_control/tcp_cubic_sender.h
index f026fd56b3f..f9234c92dcc 100644
--- a/chromium/net/quic/congestion_control/tcp_cubic_sender.h
+++ b/chromium/net/quic/congestion_control/tcp_cubic_sender.h
@@ -15,13 +15,13 @@
#include "net/quic/congestion_control/hybrid_slow_start.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
namespace net {
-// Default maximum packet size used in Linux TCP implementations.
-const QuicByteCount kDefaultTCPMSS = 1460;
+class RttStats;
namespace test {
class TcpCubicSenderPeer;
@@ -31,37 +31,34 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
public:
// Reno option and max_tcp_congestion_window are provided for testing.
TcpCubicSender(const QuicClock* clock,
+ const RttStats* rtt_stats,
bool reno,
- QuicTcpCongestionWindow max_tcp_congestion_window);
+ QuicTcpCongestionWindow max_tcp_congestion_window,
+ QuicConnectionStats* stats);
virtual ~TcpCubicSender();
+ bool InSlowStart() const;
+
// Start implementation of SendAlgorithmInterface.
virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE;
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE;
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback,
- QuicTime feedback_receive_time,
- const SentPacketsMap& sent_packets) OVERRIDE;
- virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
- QuicByteCount acked_bytes,
- QuicTime::Delta rtt) OVERRIDE;
- virtual void OnPacketLost(QuicPacketSequenceNumber largest_loss,
- QuicTime ack_receive_time) OVERRIDE;
+ QuicTime feedback_receive_time) OVERRIDE;
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets) OVERRIDE;
virtual bool OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- TransmissionType transmission_type,
HasRetransmittableData is_retransmittable) OVERRIDE;
- virtual void OnRetransmissionTimeout() OVERRIDE;
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes) OVERRIDE;
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE;
virtual QuicTime::Delta TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData has_retransmittable_data,
- IsHandshake handshake) OVERRIDE;
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const OVERRIDE;
virtual QuicBandwidth BandwidthEstimate() const OVERRIDE;
- virtual QuicTime::Delta SmoothedRtt() const OVERRIDE;
virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE;
virtual QuicByteCount GetCongestionWindow() const OVERRIDE;
// End implementation of SendAlgorithmInterface.
@@ -69,16 +66,28 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
private:
friend class test::TcpCubicSenderPeer;
- QuicByteCount AvailableSendWindow();
- QuicByteCount SendWindow();
- void Reset();
- void AckAccounting(QuicTime::Delta rtt);
- void CongestionAvoidance(QuicPacketSequenceNumber ack);
- bool IsCwndLimited() const;
- void OnTimeOut();
+ // TODO(ianswett): Remove these and migrate to OnCongestionEvent.
+ void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount acked_bytes,
+ QuicByteCount bytes_in_flight);
+ void OnPacketLost(QuicPacketSequenceNumber largest_loss,
+ QuicByteCount bytes_in_flight);
+
+ QuicByteCount SendWindow() const;
+ void MaybeIncreaseCwnd(QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount bytes_in_flight);
+ bool IsCwndLimited(QuicByteCount bytes_in_flight) const;
+ bool InRecovery() const;
+ // Methods for isolating PRR from the rest of TCP Cubic.
+ void PrrOnPacketLost(QuicByteCount bytes_in_flight);
+ void PrrOnPacketAcked(QuicByteCount acked_bytes);
+ QuicTime::Delta PrrTimeUntilSend(QuicByteCount bytes_in_flight) const;
+
HybridSlowStart hybrid_slow_start_;
Cubic cubic_;
+ const RttStats* rtt_stats_;
+ QuicConnectionStats* stats_;
// Reno provided for testing.
const bool reno_;
@@ -89,15 +98,13 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
// Receiver side advertised window.
QuicByteCount receive_window_;
- // Receiver side advertised packet loss.
- int last_received_accumulated_number_of_lost_packets_;
-
- // Bytes in flight, aka bytes on the wire.
- QuicByteCount bytes_in_flight_;
+ // Bytes sent and acked since the last loss event. Used for PRR.
+ QuicByteCount prr_out_;
+ QuicByteCount prr_delivered_;
+ size_t ack_count_since_loss_;
- // We need to keep track of the end sequence number of each RTT "burst".
- bool update_end_sequence_number_;
- QuicPacketSequenceNumber end_sequence_number_;
+ // The congestion window before the last loss event.
+ QuicByteCount bytes_in_flight_before_loss_;
// Track the largest packet that has been sent.
QuicPacketSequenceNumber largest_sent_sequence_number_;
@@ -111,23 +118,16 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
// Congestion window in packets.
QuicTcpCongestionWindow congestion_window_;
- // Slow start congestion window in packets.
+ // Slow start congestion window in packets, aka ssthresh.
QuicTcpCongestionWindow slowstart_threshold_;
+ // Whether the last loss event caused us to exit slowstart.
+ // Used for stats collection of slowstart_packets_lost
+ bool last_cutback_exited_slowstart_;
+
// Maximum number of outstanding packets for tcp.
QuicTcpCongestionWindow max_tcp_congestion_window_;
- // Min RTT during this session.
- QuicTime::Delta delay_min_;
-
- // Smoothed RTT during this session.
- QuicTime::Delta smoothed_rtt_;
-
- // Mean RTT deviation during this session.
- // Approximation of standard deviation, the error is roughly 1.25 times
- // larger than the standard deviation, for a normally distributed signal.
- QuicTime::Delta mean_deviation_;
-
DISALLOW_COPY_AND_ASSIGN(TcpCubicSender);
};
diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc
index 20dd6a6d316..c5d991a2a30 100644
--- a/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc
+++ b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc
@@ -2,14 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/quic/congestion_control/tcp_cubic_sender.h"
+#include <algorithm>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/congestion_control/tcp_cubic_sender.h"
#include "net/quic/congestion_control/tcp_receiver.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_config_peer.h"
#include "testing/gtest/include/gtest/gtest.h"
+using std::min;
+
namespace net {
namespace test {
@@ -23,107 +29,176 @@ class TcpCubicSenderPeer : public TcpCubicSender {
TcpCubicSenderPeer(const QuicClock* clock,
bool reno,
QuicTcpCongestionWindow max_tcp_congestion_window)
- : TcpCubicSender(clock, reno, max_tcp_congestion_window) {
+ : TcpCubicSender(
+ clock, &rtt_stats_, reno, max_tcp_congestion_window, &stats_) {
}
QuicTcpCongestionWindow congestion_window() {
return congestion_window_;
}
- using TcpCubicSender::AvailableSendWindow;
+ const HybridSlowStart& hybrid_slow_start() const {
+ return hybrid_slow_start_;
+ }
+
+ RttStats rtt_stats_;
+ QuicConnectionStats stats_;
+
using TcpCubicSender::SendWindow;
- using TcpCubicSender::AckAccounting;
};
class TcpCubicSenderTest : public ::testing::Test {
protected:
TcpCubicSenderTest()
- : rtt_(QuicTime::Delta::FromMilliseconds(60)),
- one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
sender_(new TcpCubicSenderPeer(&clock_, true,
kDefaultMaxCongestionWindowTCP)),
receiver_(new TcpReceiver()),
sequence_number_(1),
- acked_sequence_number_(0) {
+ acked_sequence_number_(0),
+ bytes_in_flight_(0) {
+ standard_packet_.bytes_sent = kDefaultTCPMSS;
}
- void SendAvailableSendWindow() {
- QuicByteCount bytes_to_send = sender_->AvailableSendWindow();
- while (bytes_to_send > 0) {
- QuicByteCount bytes_in_packet = std::min(kDefaultTCPMSS, bytes_to_send);
- sender_->OnPacketSent(clock_.Now(), sequence_number_++, bytes_in_packet,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- bytes_to_send -= bytes_in_packet;
- if (bytes_to_send > 0) {
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- }
+ int SendAvailableSendWindow() {
+ // Send as long as TimeUntilSend returns Zero.
+ int packets_sent = 0;
+ bool can_send = sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero();
+ while (can_send) {
+ sender_->OnPacketSent(clock_.Now(), bytes_in_flight_, sequence_number_++,
+ kDefaultTCPMSS, HAS_RETRANSMITTABLE_DATA);
+ ++packets_sent;
+ bytes_in_flight_ += kDefaultTCPMSS;
+ can_send = sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero();
}
+ return packets_sent;
}
+
// Normal is that TCP acks every other segment.
void AckNPackets(int n) {
+ sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(60),
+ QuicTime::Delta::Zero(),
+ clock_.Now());
+ SendAlgorithmInterface::CongestionMap acked_packets;
+ SendAlgorithmInterface::CongestionMap lost_packets;
+ for (int i = 0; i < n; ++i) {
+ ++acked_sequence_number_;
+ acked_packets[acked_sequence_number_] = standard_packet_;
+ }
+ sender_->OnCongestionEvent(
+ true, bytes_in_flight_, acked_packets, lost_packets);
+ bytes_in_flight_ -= n * kDefaultTCPMSS;
+ clock_.AdvanceTime(one_ms_);
+ }
+
+ void LoseNPackets(int n) {
+ SendAlgorithmInterface::CongestionMap acked_packets;
+ SendAlgorithmInterface::CongestionMap lost_packets;
for (int i = 0; i < n; ++i) {
- acked_sequence_number_++;
- sender_->OnPacketAcked(acked_sequence_number_, kDefaultTCPMSS, rtt_);
+ ++acked_sequence_number_;
+ lost_packets[acked_sequence_number_] = standard_packet_;
}
- clock_.AdvanceTime(one_ms_); // 1 millisecond.
+ sender_->OnCongestionEvent(
+ false, bytes_in_flight_, acked_packets, lost_packets);
+ bytes_in_flight_ -= n * kDefaultTCPMSS;
+ }
+
+ // Does not increment acked_sequence_number_.
+ void LosePacket(QuicPacketSequenceNumber sequence_number) {
+ SendAlgorithmInterface::CongestionMap acked_packets;
+ SendAlgorithmInterface::CongestionMap lost_packets;
+ lost_packets[sequence_number] = standard_packet_;
+ sender_->OnCongestionEvent(
+ false, bytes_in_flight_, acked_packets, lost_packets);
+ bytes_in_flight_ -= kDefaultTCPMSS;
}
- const QuicTime::Delta rtt_;
const QuicTime::Delta one_ms_;
MockClock clock_;
- SendAlgorithmInterface::SentPacketsMap not_used_;
scoped_ptr<TcpCubicSenderPeer> sender_;
scoped_ptr<TcpReceiver> receiver_;
QuicPacketSequenceNumber sequence_number_;
QuicPacketSequenceNumber acked_sequence_number_;
+ QuicByteCount bytes_in_flight_;
+ TransmissionInfo standard_packet_;
};
TEST_F(TcpCubicSenderTest, SimpleSender) {
QuicCongestionFeedbackFrame feedback;
// At startup make sure we are at the default.
- EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableSendWindow());
EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
// At startup make sure we can send.
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
// Make sure we can send.
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
// And that window is un-affected.
- EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableSendWindow());
EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
- // A retransmit should always return 0.
+ // Fill the send window with data, then verify that we can't send.
+ SendAvailableSendWindow();
+ EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(),
+ sender_->GetCongestionWindow(),
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+}
+
+TEST_F(TcpCubicSenderTest, ApplicationLimitedSlowStart) {
+ // Send exactly 10 packets and ensure the CWND ends at 14 packets.
+ const int kNumberOfAcks = 5;
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
+ // Make sure we can send.
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NACK_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ SendAvailableSendWindow();
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ AckNPackets(2);
+ }
+ QuicByteCount bytes_to_send = sender_->SendWindow();
+ // It's expected 2 acks will arrive when the bytes_in_flight are greater than
+ // half the CWND.
+ EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * 2,
+ bytes_to_send);
}
TEST_F(TcpCubicSenderTest, ExponentialSlowStart) {
- const int kNumberOfAck = 20;
+ const int kNumberOfAcks = 20;
QuicCongestionFeedbackFrame feedback;
// At startup make sure we can send.
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
// Make sure we can send.
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
- for (int n = 0; n < kNumberOfAck; ++n) {
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
}
QuicByteCount bytes_to_send = sender_->SendWindow();
- EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * kNumberOfAck,
+ EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * kNumberOfAcks,
bytes_to_send);
}
@@ -133,123 +208,243 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) {
// Ack2Packets in one round.
// Since we start at 10 packet first round will be 5 second round 10 etc
// Hence we should pass 30 at 65 = 5 + 10 + 20 + 30
- const int kNumberOfAck = 65;
+ const int kNumberOfAcks = 65;
QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
- // Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
- for (int n = 0; n < kNumberOfAck; ++n) {
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
}
QuicByteCount expected_send_window =
- kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAck);
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
- // We should now have fallen out of slow start.
- SendAvailableSendWindow();
- AckNPackets(2);
- expected_send_window += kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ // We should now have fallen out of slow start.
// Testing Reno phase.
- // We should need 141(65*2+1+10) ACK:ed packets before increasing window by
+ // We should need 140(65*2+10) ACK:ed packets before increasing window by
// one.
- for (int m = 0; m < 70; ++m) {
+ for (int i = 0; i < 69; ++i) {
SendAvailableSendWindow();
AckNPackets(2);
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
}
SendAvailableSendWindow();
AckNPackets(2);
expected_send_window += kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Now RTO and ensure slow start gets reset.
+ EXPECT_TRUE(sender_->hybrid_slow_start().started());
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_FALSE(sender_->hybrid_slow_start().started());
}
TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) {
// Make sure that we fall out of slow start when we encounter a packet loss.
- const int kNumberOfAck = 10;
QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
- // Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
- for (int i = 0; i < kNumberOfAck; ++i) {
+ const int kNumberOfAcks = 10;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
}
SendAvailableSendWindow();
QuicByteCount expected_send_window = kDefaultWindowTCP +
- (kDefaultTCPMSS * 2 * kNumberOfAck);
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
-
- sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now());
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
- // Make sure that we should not send right now.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite());
+ // Lose a packet to exit slow start.
+ LoseNPackets(1);
// We should now have fallen out of slow start.
- // We expect window to be cut in half.
+ // We expect window to be cut in half by Reno.
expected_send_window /= 2;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
// Testing Reno phase.
// We need to ack half of the pending packet before we can send again.
- int number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
+ size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
AckNPackets(number_of_packets_in_window);
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
- EXPECT_EQ(0u, sender_->AvailableSendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // We need to ack every packet in the window before we exit recovery.
+ for (size_t i = 0; i < number_of_packets_in_window; ++i) {
+ AckNPackets(1);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // We need to ack another window before we increase CWND by 1.
+ for (size_t i = 0; i < number_of_packets_in_window - 2; ++i) {
+ AckNPackets(1);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
AckNPackets(1);
expected_send_window += kDefaultTCPMSS;
- number_of_packets_in_window++;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Now RTO and ensure slow start gets reset.
+ EXPECT_TRUE(sender_->hybrid_slow_start().started());
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_FALSE(sender_->hybrid_slow_start().started());
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartPacketLossPRR) {
+ // Test based on the first example in RFC6937.
+ // Make sure that we fall out of slow start when we encounter a packet loss.
+ QuicCongestionFeedbackFrame feedback;
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
- // We should need number_of_packets_in_window ACK:ed packets before
- // increasing window by one.
- for (int k = 0; k < number_of_packets_in_window; ++k) {
+ // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
SendAvailableSendWindow();
- AckNPackets(1);
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ AckNPackets(2);
}
SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start.
+ // We expect window to be cut in half by Reno.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Testing TCP proportional rate reduction.
+ // We should send one packet for every two received acks over the remaining
+ // 18 outstanding packets.
+ size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
+ // The number of packets before we exit recovery is the original CWND minus
+ // the packet that has been lost and the one which triggered the loss.
+ size_t remaining_packets_in_recovery = number_of_packets_in_window * 2 - 1;
+ for (size_t i = 0; i < remaining_packets_in_recovery - 1; i += 2) {
+ AckNPackets(2);
+ EXPECT_TRUE(sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero());
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // We need to ack another window before we increase CWND by 1.
+ for (size_t i = 0; i < number_of_packets_in_window; ++i) {
+ AckNPackets(1);
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
AckNPackets(1);
expected_send_window += kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartBurstPacketLossPRR) {
+ // Test based on the second example in RFC6937, though we also implement
+ // forward acknowledgements, so the first two incoming acks will trigger
+ // PRR immediately.
+ // Make sure that we fall out of slow start when we encounter a packet loss.
+ QuicCongestionFeedbackFrame feedback;
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
+
+ // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Ack a packet with a 15 packet gap, losing 13 of them due to FACK.
+ LoseNPackets(13);
+ // Immediately after the loss, ensure at least one packet can be sent.
+ // Losses without subsequent acks can occur with timer based loss detection.
+ EXPECT_TRUE(sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(1);
+
+ // We should now have fallen out of slow start.
+ // We expect window to be cut in half by Reno.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Only 2 packets should be allowed to be sent, per PRR-SSRB
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // Ack the next packet, which triggers another loss.
+ LoseNPackets(1);
+ AckNPackets(1);
+
+ // Send 2 packets to simulate PRR-SSRB.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // Ack the next packet, which triggers another loss.
+ LoseNPackets(1);
+ AckNPackets(1);
+
+ // Send 2 packets to simulate PRR-SSRB.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ AckNPackets(1);
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ AckNPackets(1);
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // The window should not have changed.
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Exit recovery and return to sending at the new rate.
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ AckNPackets(1);
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ }
}
TEST_F(TcpCubicSenderTest, RTOCongestionWindow) {
EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow());
// Expect the window to decrease to the minimum once the RTO fires.
- sender_->OnRetransmissionTimeout();
+ sender_->OnRetransmissionTimeout(true);
EXPECT_EQ(2 * kDefaultTCPMSS, sender_->SendWindow());
}
+TEST_F(TcpCubicSenderTest, RTOCongestionWindowNoRetransmission) {
+ EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow());
+
+ // Expect the window to remain unchanged if the RTO fires but no
+ // packets are retransmitted.
+ sender_->OnRetransmissionTimeout(false);
+ EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow());
+}
+
TEST_F(TcpCubicSenderTest, RetransmissionDelay) {
const int64 kRttMs = 10;
const int64 kDeviationMs = 3;
EXPECT_EQ(QuicTime::Delta::Zero(), sender_->RetransmissionDelay());
- sender_->AckAccounting(QuicTime::Delta::FromMilliseconds(kRttMs));
+ sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(kRttMs),
+ QuicTime::Delta::Zero(), clock_.Now());
// Initial value is to set the median deviation to half of the initial
// rtt, the median in then multiplied by a factor of 4 and finally the
@@ -260,75 +455,63 @@ TEST_F(TcpCubicSenderTest, RetransmissionDelay) {
for (int i = 0; i < 100; ++i) {
// Run to make sure that we converge.
- sender_->AckAccounting(
- QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs));
- sender_->AckAccounting(
- QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs));
+ sender_->rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs),
+ QuicTime::Delta::Zero(), clock_.Now());
+ sender_->rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs),
+ QuicTime::Delta::Zero(), clock_.Now());
}
expected_delay = QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs * 4);
- EXPECT_NEAR(kRttMs, sender_->SmoothedRtt().ToMilliseconds(), 1);
+ EXPECT_NEAR(kRttMs, sender_->rtt_stats_.SmoothedRtt().ToMilliseconds(), 1);
EXPECT_NEAR(expected_delay.ToMilliseconds(),
sender_->RetransmissionDelay().ToMilliseconds(),
1);
EXPECT_EQ(static_cast<int64>(
sender_->GetCongestionWindow() * kNumMicrosPerSecond /
- sender_->SmoothedRtt().ToMicroseconds()),
+ sender_->rtt_stats_.SmoothedRtt().ToMicroseconds()),
sender_->BandwidthEstimate().ToBytesPerSecond());
}
TEST_F(TcpCubicSenderTest, SlowStartMaxSendWindow) {
const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50;
- const int kNumberOfAck = 100;
+ const int kNumberOfAcks = 100;
sender_.reset(
new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP));
QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
- // Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
- for (int i = 0; i < kNumberOfAck; ++i) {
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
}
QuicByteCount expected_send_window =
kMaxCongestionWindowTCP * kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
}
TEST_F(TcpCubicSenderTest, TcpRenoMaxCongestionWindow) {
const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50;
- const int kNumberOfAck = 1000;
+ const int kNumberOfAcks = 1000;
sender_.reset(
new TcpCubicSenderPeer(&clock_, true, kMaxCongestionWindowTCP));
QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
- // Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
SendAvailableSendWindow();
AckNPackets(2);
// Make sure we fall out of slow start.
- sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now());
+ LoseNPackets(1);
- for (int i = 0; i < kNumberOfAck; ++i) {
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
@@ -336,33 +519,28 @@ TEST_F(TcpCubicSenderTest, TcpRenoMaxCongestionWindow) {
QuicByteCount expected_send_window =
kMaxCongestionWindowTCP * kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
}
TEST_F(TcpCubicSenderTest, TcpCubicMaxCongestionWindow) {
const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50;
- const int kNumberOfAck = 1000;
+ // Set to 10000 to compensate for small cubic alpha.
+ const int kNumberOfAcks = 10000;
+
sender_.reset(
new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP));
QuicCongestionFeedbackFrame feedback;
- // At startup make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
// Get default QuicCongestionFeedbackFrame from receiver.
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
- sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
- not_used_);
- // Make sure we can send.
- EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
SendAvailableSendWindow();
AckNPackets(2);
// Make sure we fall out of slow start.
- sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now());
+ LoseNPackets(1);
- for (int i = 0; i < kNumberOfAck; ++i) {
+ for (int i = 0; i < kNumberOfAcks; ++i) {
// Send our full send window.
SendAvailableSendWindow();
AckNPackets(2);
@@ -370,52 +548,93 @@ TEST_F(TcpCubicSenderTest, TcpCubicMaxCongestionWindow) {
QuicByteCount expected_send_window =
kMaxCongestionWindowTCP * kDefaultTCPMSS;
- EXPECT_EQ(expected_send_window, sender_->SendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
}
TEST_F(TcpCubicSenderTest, MultipleLossesInOneWindow) {
SendAvailableSendWindow();
const QuicByteCount initial_window = sender_->GetCongestionWindow();
- sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now());
+ LosePacket(acked_sequence_number_ + 1);
const QuicByteCount post_loss_window = sender_->GetCongestionWindow();
EXPECT_GT(initial_window, post_loss_window);
- sender_->OnPacketLost(acked_sequence_number_ + 3, clock_.Now());
+ LosePacket(acked_sequence_number_ + 3);
EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow());
- sender_->OnPacketLost(sequence_number_ - 1, clock_.Now());
+ LosePacket(sequence_number_ - 1);
EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow());
// Lose a later packet and ensure the window decreases.
- sender_->OnPacketLost(sequence_number_, clock_.Now());
+ LosePacket(sequence_number_);
EXPECT_GT(post_loss_window, sender_->GetCongestionWindow());
}
-TEST_F(TcpCubicSenderTest, SendWindowNotAffectedByAcks) {
- QuicByteCount send_window = sender_->AvailableSendWindow();
-
- // Send a packet with no retransmittable data, and ensure that the congestion
- // window doesn't change.
- QuicByteCount bytes_in_packet = std::min(kDefaultTCPMSS, send_window);
- sender_->OnPacketSent(clock_.Now(), sequence_number_++, bytes_in_packet,
- NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA);
- EXPECT_EQ(send_window, sender_->AvailableSendWindow());
-
- // Send a data packet with retransmittable data, and ensure that the
- // congestion window has shrunk.
- sender_->OnPacketSent(clock_.Now(), sequence_number_++, bytes_in_packet,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- EXPECT_GT(send_window, sender_->AvailableSendWindow());
+TEST_F(TcpCubicSenderTest, DontTrackAckPackets) {
+ // Send a packet with no retransmittable data, and ensure it's not tracked.
+ EXPECT_FALSE(sender_->OnPacketSent(clock_.Now(), bytes_in_flight_,
+ sequence_number_++, kDefaultTCPMSS,
+ NO_RETRANSMITTABLE_DATA));
+
+ // Send a data packet with retransmittable data, and ensure it is tracked.
+ EXPECT_TRUE(sender_->OnPacketSent(clock_.Now(), bytes_in_flight_,
+ sequence_number_++, kDefaultTCPMSS,
+ HAS_RETRANSMITTABLE_DATA));
}
TEST_F(TcpCubicSenderTest, ConfigureMaxInitialWindow) {
QuicTcpCongestionWindow congestion_window = sender_->congestion_window();
QuicConfig config;
- config.set_server_initial_congestion_window(2 * congestion_window,
- 2 * congestion_window);
- EXPECT_EQ(2 * congestion_window, config.server_initial_congestion_window());
+ QuicConfigPeer::SetReceivedInitialWindow(&config, 2 * congestion_window);
sender_->SetFromConfig(config, true);
EXPECT_EQ(2 * congestion_window, sender_->congestion_window());
}
+TEST_F(TcpCubicSenderTest, CongestionAvoidanceAtEndOfRecovery) {
+ // Make sure that we fall out of slow start when we encounter a packet loss.
+ QuicCongestionFeedbackFrame feedback;
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
+ // Ack 10 packets in 5 acks to raise the CWND to 20.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start, and window should be cut in
+ // half by Reno. New cwnd should be 10.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // No congestion window growth should occur in recovery phase, i.e.,
+ // until the currently outstanding 20 packets are acked.
+ for (int i = 0; i < 10; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // Out of recovery now. Congestion window should not grow during RTT.
+ for (int i = 0; i < 4; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // Next ack should cause congestion window to grow by 1MSS.
+ AckNPackets(2);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/congestion_control/tcp_loss_algorithm.cc b/chromium/net/quic/congestion_control/tcp_loss_algorithm.cc
new file mode 100644
index 00000000000..8dad3f18429
--- /dev/null
+++ b/chromium/net/quic/congestion_control/tcp_loss_algorithm.cc
@@ -0,0 +1,79 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/tcp_loss_algorithm.h"
+
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+namespace {
+
+// TCP retransmits after 3 nacks.
+static const size_t kNumberOfNacksBeforeRetransmission = 3;
+
+// How many RTTs the algorithm waits before determining a packet is lost due
+// to early retransmission.
+static const double kEarlyRetransmitLossDelayMultiplier = 1.25;
+
+}
+
+TCPLossAlgorithm::TCPLossAlgorithm()
+ : loss_detection_timeout_(QuicTime::Zero()) { }
+
+LossDetectionType TCPLossAlgorithm::GetLossDetectionType() const {
+ return kNack;
+}
+
+// Uses nack counts to decide when packets are lost.
+SequenceNumberSet TCPLossAlgorithm::DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) {
+ SequenceNumberSet lost_packets;
+ loss_detection_timeout_ = QuicTime::Zero();
+ QuicTime::Delta loss_delay =
+ rtt_stats.SmoothedRtt().Multiply(kEarlyRetransmitLossDelayMultiplier);
+
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets.begin();
+ it != unacked_packets.end() && it->first <= largest_observed; ++it) {
+ if (!it->second.in_flight) {
+ continue;
+ }
+
+ LOG_IF(DFATAL, it->second.nack_count == 0)
+ << "All packets less than largest observed should have been nacked.";
+ if (it->second.nack_count >= kNumberOfNacksBeforeRetransmission) {
+ lost_packets.insert(it->first);
+ continue;
+ }
+
+ // Only early retransmit(RFC5827) when the last packet gets acked and
+ // there are retransmittable packets in flight.
+ // This also implements a timer-protected variant of FACK.
+ if (it->second.retransmittable_frames &&
+ unacked_packets.largest_sent_packet() == largest_observed) {
+ // Early retransmit marks the packet as lost once 1.25RTTs have passed
+ // since the packet was sent and otherwise sets an alarm.
+ if (time >= it->second.sent_time.Add(loss_delay)) {
+ lost_packets.insert(it->first);
+ } else {
+ // Set the timeout for the earliest retransmittable packet where early
+ // retransmit applies.
+ loss_detection_timeout_ = it->second.sent_time.Add(loss_delay);
+ break;
+ }
+ }
+ }
+
+ return lost_packets;
+}
+
+QuicTime TCPLossAlgorithm::GetLossTimeout() const {
+ return loss_detection_timeout_;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/tcp_loss_algorithm.h b/chromium/net/quic/congestion_control/tcp_loss_algorithm.h
new file mode 100644
index 00000000000..5f0bcfb523f
--- /dev/null
+++ b/chromium/net/quic/congestion_control/tcp_loss_algorithm.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_
+#define NET_QUIC_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_
+
+#include <algorithm>
+#include <map>
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/quic_unacked_packet_map.h"
+
+namespace net {
+
+// Class which implement's TCP's approach of detecting loss when 3 nacks have
+// been received for a packet. Also implements TCP's early retransmit(RFC5827).
+class NET_EXPORT_PRIVATE TCPLossAlgorithm : public LossDetectionInterface {
+ public:
+ TCPLossAlgorithm();
+ virtual ~TCPLossAlgorithm() {}
+
+ virtual LossDetectionType GetLossDetectionType() const OVERRIDE;
+
+ // Uses nack counts to decide when packets are lost.
+ virtual SequenceNumberSet DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) OVERRIDE;
+
+ // Returns a non-zero value when the early retransmit timer is active.
+ virtual QuicTime GetLossTimeout() const OVERRIDE;
+
+ private:
+ QuicTime loss_detection_timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(TCPLossAlgorithm);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_
diff --git a/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc b/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc
new file mode 100644
index 00000000000..eccda4a81c5
--- /dev/null
+++ b/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc
@@ -0,0 +1,183 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/congestion_control/tcp_loss_algorithm.h"
+#include "net/quic/quic_unacked_packet_map.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class TcpLossAlgorithmTest : public ::testing::Test {
+ protected:
+ TcpLossAlgorithmTest()
+ : unacked_packets_() {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ clock_.Now());
+ }
+
+ void SendDataPacket(QuicPacketSequenceNumber sequence_number) {
+ SerializedPacket packet(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER,
+ NULL, 0, new RetransmittableFrames());
+ unacked_packets_.AddPacket(packet);
+ unacked_packets_.SetSent(sequence_number, clock_.Now(), 1000, true);
+ }
+
+ void VerifyLosses(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber* losses_expected,
+ size_t num_losses) {
+ SequenceNumberSet lost_packets =
+ loss_algorithm_.DetectLostPackets(
+ unacked_packets_, clock_.Now(), largest_observed, rtt_stats_);
+ EXPECT_EQ(num_losses, lost_packets.size());
+ for (size_t i = 0; i < num_losses; ++i) {
+ EXPECT_TRUE(ContainsKey(lost_packets, losses_expected[i]));
+ }
+ }
+
+ QuicUnackedPacketMap unacked_packets_;
+ TCPLossAlgorithm loss_algorithm_;
+ RttStats rtt_stats_;
+ MockClock clock_;
+};
+
+TEST_F(TcpLossAlgorithmTest, NackRetransmit1Packet) {
+ const size_t kNumSentPackets = 5;
+ // Transmit 5 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ // No loss on one ack.
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.NackPacket(1, 1);
+ VerifyLosses(2, NULL, 0);
+ // No loss on two acks.
+ unacked_packets_.RemoveFromInFlight(3);
+ unacked_packets_.NackPacket(1, 2);
+ VerifyLosses(3, NULL, 0);
+ // Loss on three acks.
+ unacked_packets_.RemoveFromInFlight(4);
+ unacked_packets_.NackPacket(1, 3);
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(4, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// A stretch ack is an ack that covers more than 1 packet of previously
+// unacknowledged data.
+TEST_F(TcpLossAlgorithmTest, NackRetransmit1PacketWith1StretchAck) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ // Nack the first packet 3 times in a single StretchAck.
+ unacked_packets_.NackPacket(1, 3);
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.RemoveFromInFlight(3);
+ unacked_packets_.RemoveFromInFlight(4);
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(4, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// Ack a packet 3 packets ahead, causing a retransmit.
+TEST_F(TcpLossAlgorithmTest, NackRetransmit1PacketSingleAck) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ // Nack the first packet 3 times in an AckFrame with three missing packets.
+ unacked_packets_.NackPacket(1, 3);
+ unacked_packets_.NackPacket(2, 2);
+ unacked_packets_.NackPacket(3, 1);
+ unacked_packets_.RemoveFromInFlight(4);
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(4, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TcpLossAlgorithmTest, EarlyRetransmit1Packet) {
+ const size_t kNumSentPackets = 2;
+ // Transmit 2 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ // Early retransmit when the final packet gets acked and the first is nacked.
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.NackPacket(1, 1);
+ VerifyLosses(2, NULL, 0);
+ EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt().Multiply(1.25)),
+ loss_algorithm_.GetLossTimeout());
+
+ clock_.AdvanceTime(rtt_stats_.latest_rtt().Multiply(1.25));
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(2, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TcpLossAlgorithmTest, EarlyRetransmitAllPackets) {
+ const size_t kNumSentPackets = 5;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ // Advance the time 1/4 RTT between 3 and 4.
+ if (i == 3) {
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ }
+ }
+
+ // Early retransmit when the final packet gets acked and 1.25 RTTs have
+ // elapsed since the packets were sent.
+ unacked_packets_.RemoveFromInFlight(kNumSentPackets);
+ // This simulates a single ack following multiple missing packets with FACK.
+ for (size_t i = 1; i < kNumSentPackets; ++i) {
+ unacked_packets_.NackPacket(i, kNumSentPackets - i);
+ }
+ QuicPacketSequenceNumber lost[] = { 1, 2 };
+ VerifyLosses(kNumSentPackets, lost, arraysize(lost));
+ // The time has already advanced 1/4 an RTT, so ensure the timeout is set
+ // 1.25 RTTs after the earliest pending packet(3), not the last(4).
+ EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt()),
+ loss_algorithm_.GetLossTimeout());
+
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt());
+ QuicPacketSequenceNumber lost2[] = { 1, 2, 3 };
+ VerifyLosses(kNumSentPackets, lost2, arraysize(lost2));
+ EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt().Multiply(0.25)),
+ loss_algorithm_.GetLossTimeout());
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ QuicPacketSequenceNumber lost3[] = { 1, 2, 3, 4 };
+ VerifyLosses(kNumSentPackets, lost3, arraysize(lost3));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TcpLossAlgorithmTest, DontEarlyRetransmitNeuteredPacket) {
+ const size_t kNumSentPackets = 2;
+ // Transmit 2 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ // Neuter packet 1.
+ unacked_packets_.RemoveRetransmittability(1);
+
+ // Early retransmit when the final packet gets acked and the first is nacked.
+ unacked_packets_.IncreaseLargestObserved(2);
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.NackPacket(1, 1);
+ VerifyLosses(2, NULL, 0);
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/tcp_receiver.cc b/chromium/net/quic/congestion_control/tcp_receiver.cc
index ecff130b003..a8c5489b604 100644
--- a/chromium/net/quic/congestion_control/tcp_receiver.cc
+++ b/chromium/net/quic/congestion_control/tcp_receiver.cc
@@ -7,31 +7,24 @@
namespace net {
+// Originally 64K bytes, but increased it to 256K to support higher bitrates.
// static
-// Originally 64K bytes for TCP, setting it to 256K to support higher bitrates.
const QuicByteCount TcpReceiver::kReceiveWindowTCP = 256000;
TcpReceiver::TcpReceiver()
- : accumulated_number_of_recoverd_lost_packets_(0),
- receive_window_(kReceiveWindowTCP) {
+ : receive_window_(kReceiveWindowTCP) {
}
bool TcpReceiver::GenerateCongestionFeedback(
QuicCongestionFeedbackFrame* feedback) {
feedback->type = kTCP;
- feedback->tcp.accumulated_number_of_lost_packets =
- accumulated_number_of_recoverd_lost_packets_;
feedback->tcp.receive_window = receive_window_;
return true;
}
void TcpReceiver::RecordIncomingPacket(QuicByteCount bytes,
QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool revived) {
- if (revived) {
- ++accumulated_number_of_recoverd_lost_packets_;
- }
+ QuicTime timestamp) {
}
} // namespace net
diff --git a/chromium/net/quic/congestion_control/tcp_receiver.h b/chromium/net/quic/congestion_control/tcp_receiver.h
index 99cf93c4933..a5e5b1b7c7d 100644
--- a/chromium/net/quic/congestion_control/tcp_receiver.h
+++ b/chromium/net/quic/congestion_control/tcp_receiver.h
@@ -29,12 +29,9 @@ class NET_EXPORT_PRIVATE TcpReceiver : public ReceiveAlgorithmInterface {
virtual void RecordIncomingPacket(QuicByteCount bytes,
QuicPacketSequenceNumber sequence_number,
- QuicTime timestamp,
- bool revived) OVERRIDE;
+ QuicTime timestamp) OVERRIDE;
private:
- // We need to keep track of FEC recovered packets.
- int accumulated_number_of_recoverd_lost_packets_;
QuicByteCount receive_window_;
DISALLOW_COPY_AND_ASSIGN(TcpReceiver);
diff --git a/chromium/net/quic/congestion_control/tcp_receiver_test.cc b/chromium/net/quic/congestion_control/tcp_receiver_test.cc
index 305ff75eb8b..e074fdfe6c7 100644
--- a/chromium/net/quic/congestion_control/tcp_receiver_test.cc
+++ b/chromium/net/quic/congestion_control/tcp_receiver_test.cc
@@ -22,16 +22,14 @@ class QuicTcpReceiverTest : public ::testing::Test {
TEST_F(QuicTcpReceiverTest, SimpleReceiver) {
QuicCongestionFeedbackFrame feedback;
QuicTime timestamp(QuicTime::Zero());
- receiver_->RecordIncomingPacket(1, 1, timestamp, false);
+ receiver_->RecordIncomingPacket(1, 1, timestamp);
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
EXPECT_EQ(kTCP, feedback.type);
EXPECT_EQ(256000u, feedback.tcp.receive_window);
- EXPECT_EQ(0, feedback.tcp.accumulated_number_of_lost_packets);
- receiver_->RecordIncomingPacket(1, 2, timestamp, true);
+ receiver_->RecordIncomingPacket(1, 2, timestamp);
ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
EXPECT_EQ(kTCP, feedback.type);
EXPECT_EQ(256000u, feedback.tcp.receive_window);
- EXPECT_EQ(1, feedback.tcp.accumulated_number_of_lost_packets);
}
} // namespace test
diff --git a/chromium/net/quic/congestion_control/time_loss_algorithm.cc b/chromium/net/quic/congestion_control/time_loss_algorithm.cc
new file mode 100644
index 00000000000..d23deadf118
--- /dev/null
+++ b/chromium/net/quic/congestion_control/time_loss_algorithm.cc
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/time_loss_algorithm.h"
+
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+namespace {
+
+// The minimum delay before a packet will be considered lost,
+// regardless of SRTT. Half of the minimum TLP, since the loss algorithm only
+// triggers when a nack has been receieved for the packet.
+static const size_t kMinLossDelayMs = 5;
+
+// How many RTTs the algorithm waits before determining a packet is lost.
+static const double kLossDelayMultiplier = 1.25;
+
+} // namespace
+
+TimeLossAlgorithm::TimeLossAlgorithm()
+ : loss_detection_timeout_(QuicTime::Zero()) { }
+
+LossDetectionType TimeLossAlgorithm::GetLossDetectionType() const {
+ return kTime;
+}
+
+SequenceNumberSet TimeLossAlgorithm::DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) {
+ SequenceNumberSet lost_packets;
+ loss_detection_timeout_ = QuicTime::Zero();
+ QuicTime::Delta loss_delay = QuicTime::Delta::Max(
+ QuicTime::Delta::FromMilliseconds(kMinLossDelayMs),
+ QuicTime::Delta::Max(rtt_stats.SmoothedRtt(), rtt_stats.latest_rtt())
+ .Multiply(kLossDelayMultiplier));
+
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets.begin();
+ it != unacked_packets.end() && it->first <= largest_observed; ++it) {
+ if (!it->second.in_flight) {
+ continue;
+ }
+ LOG_IF(DFATAL, it->second.nack_count == 0)
+ << "All packets less than largest observed should have been nacked.";
+
+ // Packets are sent in order, so break when we haven't waited long enough
+ // to lose any more packets and leave the loss_time_ set for the timeout.
+ QuicTime when_lost = it->second.sent_time.Add(loss_delay);
+ if (time < when_lost) {
+ loss_detection_timeout_ = when_lost;
+ break;
+ }
+ lost_packets.insert(it->first);
+ }
+
+ return lost_packets;
+}
+
+// loss_time_ is updated in DetectLostPackets, which must be called every time
+// an ack is received or the timeout expires.
+QuicTime TimeLossAlgorithm::GetLossTimeout() const {
+ return loss_detection_timeout_;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/congestion_control/time_loss_algorithm.h b/chromium/net/quic/congestion_control/time_loss_algorithm.h
new file mode 100644
index 00000000000..ae37e1e3258
--- /dev/null
+++ b/chromium/net/quic/congestion_control/time_loss_algorithm.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_
+#define NET_QUIC_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_
+
+#include <algorithm>
+#include <map>
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/quic_unacked_packet_map.h"
+
+namespace net {
+
+// A loss detection algorithm which avoids spurious losses and retransmissions
+// by waiting 1.25 RTTs after a packet was sent instead of nack count.
+class NET_EXPORT_PRIVATE TimeLossAlgorithm : public LossDetectionInterface {
+ public:
+ TimeLossAlgorithm();
+ virtual ~TimeLossAlgorithm() {}
+
+ virtual LossDetectionType GetLossDetectionType() const OVERRIDE;
+
+ // Declares pending packets less than the largest observed lost when it has
+ // been 1.25 RTT since they were sent. Packets larger than the largest
+ // observed are retransmitted via TLP.
+ virtual SequenceNumberSet DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) OVERRIDE;
+
+ // Returns the time the next packet will be lost, or zero if there
+ // are no nacked pending packets outstanding.
+ // TODO(ianswett): Ideally the RTT variance and the RTT would be used to
+ // determine the time a packet is considered lost.
+ // TODO(ianswett): Consider using Max(1.25 * srtt, 1.125 * last_rtt).
+ virtual QuicTime GetLossTimeout() const OVERRIDE;
+
+ private:
+ QuicTime loss_detection_timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeLossAlgorithm);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_
diff --git a/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc b/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc
new file mode 100644
index 00000000000..bd03e608bcc
--- /dev/null
+++ b/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc
@@ -0,0 +1,138 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/congestion_control/time_loss_algorithm.h"
+#include "net/quic/quic_unacked_packet_map.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class TimeLossAlgorithmTest : public ::testing::Test {
+ protected:
+ TimeLossAlgorithmTest()
+ : unacked_packets_() {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ clock_.Now());
+ }
+
+ void SendDataPacket(QuicPacketSequenceNumber sequence_number) {
+ SerializedPacket packet(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER,
+ NULL, 0, new RetransmittableFrames());
+ unacked_packets_.AddPacket(packet);
+ unacked_packets_.SetSent(sequence_number, clock_.Now(), 1000, true);
+ }
+
+ void VerifyLosses(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber* losses_expected,
+ size_t num_losses) {
+ SequenceNumberSet lost_packets =
+ loss_algorithm_.DetectLostPackets(
+ unacked_packets_, clock_.Now(), largest_observed, rtt_stats_);
+ EXPECT_EQ(num_losses, lost_packets.size());
+ for (size_t i = 0; i < num_losses; ++i) {
+ EXPECT_TRUE(ContainsKey(lost_packets, losses_expected[i]));
+ }
+ }
+
+ QuicUnackedPacketMap unacked_packets_;
+ TimeLossAlgorithm loss_algorithm_;
+ RttStats rtt_stats_;
+ MockClock clock_;
+};
+
+TEST_F(TimeLossAlgorithmTest, NoLossFor500Nacks) {
+ const size_t kNumSentPackets = 5;
+ // Transmit 5 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ unacked_packets_.RemoveFromInFlight(2);
+ for (size_t i = 1; i < 500; ++i) {
+ unacked_packets_.NackPacket(1, i);
+ VerifyLosses(2, NULL, 0);
+ }
+ EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(1.25),
+ loss_algorithm_.GetLossTimeout().Subtract(clock_.Now()));
+}
+
+TEST_F(TimeLossAlgorithmTest, NoLossUntilTimeout) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at 1/10th an RTT interval.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.1));
+ }
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost until 1.25 RTTs pass.
+ unacked_packets_.NackPacket(1, 1);
+ unacked_packets_.RemoveFromInFlight(2);
+ VerifyLosses(2, NULL, 0);
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(0.25),
+ loss_algorithm_.GetLossTimeout().Subtract(clock_.Now()));
+ unacked_packets_.NackPacket(1, 5);
+ VerifyLosses(2, NULL, 0);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(2, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TimeLossAlgorithmTest, NoLossWithoutNack) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at 1/10th an RTT interval.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.1));
+ }
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost without a nack.
+ unacked_packets_.RemoveFromInFlight(1);
+ VerifyLosses(1, NULL, 0);
+ // The timer should still not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ VerifyLosses(1, NULL, 0);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt());
+ VerifyLosses(1, NULL, 0);
+
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TimeLossAlgorithmTest, MultipleLossesAtOnce) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at once and then go forward an RTT.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt());
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost until 1.25 RTTs pass.
+ for (size_t i = 1; i < kNumSentPackets; ++i) {
+ unacked_packets_.NackPacket(i, 1);
+ }
+ unacked_packets_.RemoveFromInFlight(10);
+ VerifyLosses(10, NULL, 0);
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(0.25),
+ loss_algorithm_.GetLossTimeout().Subtract(clock_.Now()));
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ QuicPacketSequenceNumber lost[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ VerifyLosses(10, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/crypto/aead_base_decrypter.h b/chromium/net/quic/crypto/aead_base_decrypter.h
new file mode 100644
index 00000000000..6257409f9fb
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_decrypter.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_
+#define NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_
+
+#include "base/compiler_specific.h"
+#include "net/quic/crypto/quic_decrypter.h"
+
+#if defined(USE_OPENSSL)
+#include "net/quic/crypto/scoped_evp_aead_ctx.h"
+#else
+#include <pkcs11t.h>
+#include <seccomon.h>
+typedef struct PK11SymKeyStr PK11SymKey;
+typedef SECStatus (*PK11_DecryptFunction)(
+ PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
+ unsigned char* out, unsigned int* outLen, unsigned int maxLen,
+ const unsigned char* enc, unsigned encLen);
+#endif
+
+namespace net {
+
+// AeadBaseDecrypter is the base class of AEAD QuicDecrypter subclasses.
+class NET_EXPORT_PRIVATE AeadBaseDecrypter : public QuicDecrypter {
+ public:
+#if defined(USE_OPENSSL)
+ AeadBaseDecrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#else
+ AeadBaseDecrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_DecryptFunction pk11_decrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#endif
+ virtual ~AeadBaseDecrypter();
+
+ // QuicDecrypter implementation
+ virtual bool SetKey(base::StringPiece key) OVERRIDE;
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
+ virtual bool Decrypt(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ base::StringPiece ciphertext,
+ unsigned char* output,
+ size_t* output_length) OVERRIDE;
+ virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
+ base::StringPiece ciphertext) OVERRIDE;
+ virtual base::StringPiece GetKey() const OVERRIDE;
+ virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+
+ protected:
+ // Make these constants available to the subclasses so that the subclasses
+ // can assert at compile time their key_size_ and nonce_prefix_size_ do not
+ // exceed the maximum.
+ static const size_t kMaxKeySize = 32;
+ static const size_t kMaxNoncePrefixSize = 4;
+
+#if !defined(USE_OPENSSL)
+ struct AeadParams {
+ unsigned int len;
+ union {
+ CK_GCM_PARAMS gcm_params;
+#if !defined(USE_NSS)
+ // USE_NSS means we are using system NSS rather than our copy of NSS.
+ // The system NSS <pkcs11n.h> header doesn't define this type yet.
+ CK_NSS_AEAD_PARAMS nss_aead_params;
+#endif
+ } data;
+ };
+
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const = 0;
+#endif // !defined(USE_OPENSSL)
+
+ private:
+#if defined(USE_OPENSSL)
+ const EVP_AEAD* const aead_alg_;
+#else
+ const CK_MECHANISM_TYPE aead_mechanism_;
+ const PK11_DecryptFunction pk11_decrypt_;
+#endif
+ const size_t key_size_;
+ const size_t auth_tag_size_;
+ const size_t nonce_prefix_size_;
+
+ // The key.
+ unsigned char key_[kMaxKeySize];
+ // The nonce prefix.
+ unsigned char nonce_prefix_[kMaxNoncePrefixSize];
+
+#if defined(USE_OPENSSL)
+ ScopedEVPAEADCtx ctx_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(AeadBaseDecrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_
diff --git a/chromium/net/quic/crypto/aead_base_decrypter_nss.cc b/chromium/net/quic/crypto/aead_base_decrypter_nss.cc
new file mode 100644
index 00000000000..bbf30244f87
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_decrypter_nss.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_decrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "crypto/scoped_nss_types.h"
+
+using base::StringPiece;
+
+namespace net {
+
+AeadBaseDecrypter::AeadBaseDecrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_DecryptFunction pk11_decrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_mechanism_(aead_mechanism),
+ pk11_decrypt_(pk11_decrypt),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseDecrypter::~AeadBaseDecrypter() {}
+
+bool AeadBaseDecrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::Decrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext,
+ uint8* output,
+ size_t* output_length) {
+ if (ciphertext.length() < auth_tag_size_ ||
+ nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+ // NSS 3.14.x incorrectly requires an output buffer at least as long as
+ // the ciphertext (NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id= 853674). Fortunately
+ // QuicDecrypter::Decrypt() specifies that |output| must be as long as
+ // |ciphertext| on entry.
+ size_t plaintext_size = ciphertext.length() - auth_tag_size_;
+
+ // Import key_ into NSS.
+ SECItem key_item;
+ key_item.type = siBuffer;
+ key_item.data = key_;
+ key_item.len = key_size_;
+ PK11SlotInfo* slot = PK11_GetInternalSlot();
+
+ // TODO(wtc): For an AES-GCM key, the correct value for |key_mechanism| is
+ // CKM_AES_GCM, but because of NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=853285, use CKM_AES_ECB as a
+ // workaround. Remove this when we require NSS 3.15.
+ CK_MECHANISM_TYPE key_mechanism = aead_mechanism_;
+ if (key_mechanism == CKM_AES_GCM) {
+ key_mechanism = CKM_AES_ECB;
+ }
+
+ // The exact value of the |origin| argument doesn't matter to NSS as long as
+ // it's not PK11_OriginFortezzaHack, so pass PK11_OriginUnwrap as a
+ // placeholder.
+ crypto::ScopedPK11SymKey aead_key(PK11_ImportSymKey(
+ slot, key_mechanism, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, NULL));
+ PK11_FreeSlot(slot);
+ slot = NULL;
+ if (!aead_key) {
+ DVLOG(1) << "PK11_ImportSymKey failed";
+ return false;
+ }
+
+ AeadParams aead_params = {0};
+ FillAeadParams(nonce, associated_data, auth_tag_size_, &aead_params);
+
+ SECItem param;
+ param.type = siBuffer;
+ param.data = reinterpret_cast<unsigned char*>(&aead_params.data);
+ param.len = aead_params.len;
+
+ unsigned int output_len;
+ if (pk11_decrypt_(aead_key.get(), aead_mechanism_, &param,
+ output, &output_len, ciphertext.length(),
+ reinterpret_cast<const unsigned char*>(ciphertext.data()),
+ ciphertext.length()) != SECSuccess) {
+ return false;
+ }
+
+ if (output_len != plaintext_size) {
+ DVLOG(1) << "Wrong output length";
+ return false;
+ }
+ *output_length = output_len;
+ return true;
+}
+
+QuicData* AeadBaseDecrypter::DecryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ if (ciphertext.length() < auth_tag_size_) {
+ return NULL;
+ }
+ size_t plaintext_size;
+ scoped_ptr<char[]> plaintext(new char[ciphertext.length()]);
+
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, ciphertext,
+ reinterpret_cast<uint8*>(plaintext.get()),
+ &plaintext_size)) {
+ return NULL;
+ }
+ return new QuicData(plaintext.release(), plaintext_size, true);
+}
+
+StringPiece AeadBaseDecrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseDecrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc b/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc
new file mode 100644
index 00000000000..2190bf6966e
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_decrypter.h"
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#include "base/memory/scoped_ptr.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+// Clear OpenSSL error stack.
+void ClearOpenSslErrors() {
+ while (ERR_get_error()) {}
+}
+
+// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
+// stack.
+void DLogOpenSslErrors() {
+#ifdef NDEBUG
+ ClearOpenSslErrors();
+#else
+ while (unsigned long error = ERR_get_error()) {
+ char buf[120];
+ ERR_error_string_n(error, buf, arraysize(buf));
+ DLOG(ERROR) << "OpenSSL error: " << buf;
+ }
+#endif
+}
+
+} // namespace
+
+AeadBaseDecrypter::AeadBaseDecrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_alg_(aead_alg),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseDecrypter::~AeadBaseDecrypter() {}
+
+bool AeadBaseDecrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+
+ EVP_AEAD_CTX_cleanup(ctx_.get());
+ if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_,
+ auth_tag_size_, NULL)) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::Decrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext,
+ uint8* output,
+ size_t* output_length) {
+ if (ciphertext.length() < auth_tag_size_ ||
+ nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+
+ ssize_t len = EVP_AEAD_CTX_open(
+ ctx_.get(), output, ciphertext.size(),
+ reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(),
+ reinterpret_cast<const uint8_t*>(ciphertext.data()), ciphertext.size(),
+ reinterpret_cast<const uint8_t*>(associated_data.data()),
+ associated_data.size());
+
+ if (len < 0) {
+ // Because QuicFramer does trial decryption, decryption errors are expected
+ // when encryption level changes. So we don't log decryption errors.
+ ClearOpenSslErrors();
+ return false;
+ }
+
+ *output_length = len;
+ return true;
+}
+
+QuicData* AeadBaseDecrypter::DecryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ if (ciphertext.length() < auth_tag_size_) {
+ return NULL;
+ }
+ size_t plaintext_size = ciphertext.length();
+ scoped_ptr<char[]> plaintext(new char[plaintext_size]);
+
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, ciphertext,
+ reinterpret_cast<uint8*>(plaintext.get()),
+ &plaintext_size)) {
+ return NULL;
+ }
+ return new QuicData(plaintext.release(), plaintext_size, true);
+}
+
+StringPiece AeadBaseDecrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseDecrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/aead_base_encrypter.h b/chromium/net/quic/crypto/aead_base_encrypter.h
new file mode 100644
index 00000000000..713813150ec
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_encrypter.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_
+#define NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_
+
+#include "base/compiler_specific.h"
+#include "net/quic/crypto/quic_encrypter.h"
+
+#if defined(USE_OPENSSL)
+#include "net/quic/crypto/scoped_evp_aead_ctx.h"
+#else
+#include <pkcs11t.h>
+#include <seccomon.h>
+typedef struct PK11SymKeyStr PK11SymKey;
+typedef SECStatus (*PK11_EncryptFunction)(
+ PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
+ unsigned char* out, unsigned int* outLen, unsigned int maxLen,
+ const unsigned char* data, unsigned int dataLen);
+#endif
+
+namespace net {
+
+// AeadBaseEncrypter is the base class of AEAD QuicEncrypter subclasses.
+class NET_EXPORT_PRIVATE AeadBaseEncrypter : public QuicEncrypter {
+ public:
+#if defined(USE_OPENSSL)
+ AeadBaseEncrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#else
+ AeadBaseEncrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_EncryptFunction pk11_encrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#endif
+ virtual ~AeadBaseEncrypter();
+
+ // QuicEncrypter implementation
+ virtual bool SetKey(base::StringPiece key) OVERRIDE;
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
+ virtual bool Encrypt(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ base::StringPiece plaintext,
+ unsigned char* output) OVERRIDE;
+ virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
+ base::StringPiece plaintext) OVERRIDE;
+ virtual size_t GetKeySize() const OVERRIDE;
+ virtual size_t GetNoncePrefixSize() const OVERRIDE;
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE;
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE;
+ virtual base::StringPiece GetKey() const OVERRIDE;
+ virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+
+ protected:
+ // Make these constants available to the subclasses so that the subclasses
+ // can assert at compile time their key_size_ and nonce_prefix_size_ do not
+ // exceed the maximum.
+ static const size_t kMaxKeySize = 32;
+ static const size_t kMaxNoncePrefixSize = 4;
+
+#if !defined(USE_OPENSSL)
+ struct AeadParams {
+ unsigned int len;
+ union {
+ CK_GCM_PARAMS gcm_params;
+#if !defined(USE_NSS)
+ // USE_NSS means we are using system NSS rather than our copy of NSS.
+ // The system NSS <pkcs11n.h> header doesn't define this type yet.
+ CK_NSS_AEAD_PARAMS nss_aead_params;
+#endif
+ } data;
+ };
+
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const = 0;
+#endif
+
+ private:
+#if defined(USE_OPENSSL)
+ const EVP_AEAD* const aead_alg_;
+#else
+ const CK_MECHANISM_TYPE aead_mechanism_;
+ const PK11_EncryptFunction pk11_encrypt_;
+#endif
+ const size_t key_size_;
+ const size_t auth_tag_size_;
+ const size_t nonce_prefix_size_;
+
+ // The key.
+ unsigned char key_[kMaxKeySize];
+ // The nonce prefix.
+ unsigned char nonce_prefix_[kMaxNoncePrefixSize];
+
+#if defined(USE_OPENSSL)
+ ScopedEVPAEADCtx ctx_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(AeadBaseEncrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_
diff --git a/chromium/net/quic/crypto/aead_base_encrypter_nss.cc b/chromium/net/quic/crypto/aead_base_encrypter_nss.cc
new file mode 100644
index 00000000000..fd4d888554c
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_encrypter_nss.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_encrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "crypto/scoped_nss_types.h"
+
+using base::StringPiece;
+
+namespace net {
+
+AeadBaseEncrypter::AeadBaseEncrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_EncryptFunction pk11_encrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_mechanism_(aead_mechanism),
+ pk11_encrypt_(pk11_encrypt),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseEncrypter::~AeadBaseEncrypter() {}
+
+bool AeadBaseEncrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::Encrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext,
+ unsigned char* output) {
+ if (nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+
+ // Import key_ into NSS.
+ SECItem key_item;
+ key_item.type = siBuffer;
+ key_item.data = key_;
+ key_item.len = key_size_;
+ PK11SlotInfo* slot = PK11_GetInternalSlot();
+
+ // TODO(wtc): For an AES-GCM key, the correct value for |key_mechanism| is
+ // CKM_AES_GCM, but because of NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=853285, use CKM_AES_ECB as a
+ // workaround. Remove this when we require NSS 3.15.
+ CK_MECHANISM_TYPE key_mechanism = aead_mechanism_;
+ if (key_mechanism == CKM_AES_GCM) {
+ key_mechanism = CKM_AES_ECB;
+ }
+
+ // The exact value of the |origin| argument doesn't matter to NSS as long as
+ // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a
+ // placeholder.
+ crypto::ScopedPK11SymKey aead_key(PK11_ImportSymKey(
+ slot, key_mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL));
+ PK11_FreeSlot(slot);
+ slot = NULL;
+ if (!aead_key) {
+ DVLOG(1) << "PK11_ImportSymKey failed";
+ return false;
+ }
+
+ AeadParams aead_params = {0};
+ FillAeadParams(nonce, associated_data, auth_tag_size_, &aead_params);
+
+ SECItem param;
+ param.type = siBuffer;
+ param.data = reinterpret_cast<unsigned char*>(&aead_params.data);
+ param.len = aead_params.len;
+
+ unsigned int output_len;
+ if (pk11_encrypt_(aead_key.get(), aead_mechanism_, &param,
+ output, &output_len, ciphertext_size,
+ reinterpret_cast<const unsigned char*>(plaintext.data()),
+ plaintext.size()) != SECSuccess) {
+ DVLOG(1) << "pk11_encrypt_ failed";
+ return false;
+ }
+
+ if (output_len != ciphertext_size) {
+ DVLOG(1) << "Wrong output length";
+ return false;
+ }
+
+ return true;
+}
+
+QuicData* AeadBaseEncrypter::EncryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+ scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
+ // same sequence number twice.
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return NULL;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+size_t AeadBaseEncrypter::GetKeySize() const { return key_size_; }
+
+size_t AeadBaseEncrypter::GetNoncePrefixSize() const {
+ return nonce_prefix_size_;
+}
+
+size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - auth_tag_size_;
+}
+
+size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + auth_tag_size_;
+}
+
+StringPiece AeadBaseEncrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseEncrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/aead_base_encrypter_openssl.cc b/chromium/net/quic/crypto/aead_base_encrypter_openssl.cc
new file mode 100644
index 00000000000..9f053abf932
--- /dev/null
+++ b/chromium/net/quic/crypto/aead_base_encrypter_openssl.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_encrypter.h"
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <string.h>
+
+#include "base/memory/scoped_ptr.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
+// stack.
+void DLogOpenSslErrors() {
+#ifdef NDEBUG
+ while (ERR_get_error()) {}
+#else
+ while (unsigned long error = ERR_get_error()) {
+ char buf[120];
+ ERR_error_string_n(error, buf, arraysize(buf));
+ DLOG(ERROR) << "OpenSSL error: " << buf;
+ }
+#endif
+}
+
+} // namespace
+
+AeadBaseEncrypter::AeadBaseEncrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_alg_(aead_alg),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseEncrypter::~AeadBaseEncrypter() {}
+
+bool AeadBaseEncrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+
+ EVP_AEAD_CTX_cleanup(ctx_.get());
+
+ if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_,
+ auth_tag_size_, NULL)) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseEncrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::Encrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext,
+ unsigned char* output) {
+ if (nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+
+ ssize_t len = EVP_AEAD_CTX_seal(
+ ctx_.get(), output, plaintext.size() + auth_tag_size_,
+ reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(),
+ reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size(),
+ reinterpret_cast<const uint8_t*>(associated_data.data()),
+ associated_data.size());
+
+ if (len < 0) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+QuicData* AeadBaseEncrypter::EncryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+ scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
+ // same sequence number twice.
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return NULL;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+size_t AeadBaseEncrypter::GetKeySize() const { return key_size_; }
+
+size_t AeadBaseEncrypter::GetNoncePrefixSize() const {
+ return nonce_prefix_size_;
+}
+
+size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - auth_tag_size_;
+}
+
+size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + auth_tag_size_;
+}
+
+StringPiece AeadBaseEncrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseEncrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h
index aba610f25df..0511c8b7268 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h
@@ -5,28 +5,17 @@
#ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
#define NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "net/quic/crypto/quic_decrypter.h"
-
-#if defined(USE_OPENSSL)
-#include "net/quic/crypto/scoped_evp_cipher_ctx.h"
-#endif
+#include "net/quic/crypto/aead_base_decrypter.h"
namespace net {
-namespace test {
-class Aes128Gcm12DecrypterPeer;
-} // namespace test
-
// An Aes128Gcm12Decrypter is a QuicDecrypter that implements the
// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
// calling QuicDecrypter::Create(kAESG).
//
// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
// of the nonce is four bytes.
-class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public QuicDecrypter {
+class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public AeadBaseDecrypter {
public:
enum {
// Authentication tags are truncated to 96 bits.
@@ -36,34 +25,17 @@ class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public QuicDecrypter {
Aes128Gcm12Decrypter();
virtual ~Aes128Gcm12Decrypter();
- // Returns true if the underlying crypto library supports AES GCM.
- static bool IsSupported();
-
- // QuicDecrypter implementation
- virtual bool SetKey(base::StringPiece key) OVERRIDE;
- virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
- virtual bool Decrypt(base::StringPiece nonce,
- base::StringPiece associated_data,
- base::StringPiece ciphertext,
- unsigned char* output,
- size_t* output_length) OVERRIDE;
- virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number,
- base::StringPiece associated_data,
- base::StringPiece ciphertext) OVERRIDE;
- virtual base::StringPiece GetKey() const OVERRIDE;
- virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseDecrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
private:
- // The 128-bit AES key.
- unsigned char key_[16];
- // The nonce prefix.
- unsigned char nonce_prefix_[4];
-
-#if defined(USE_OPENSSL)
- // TODO(rtenneti): when Chromium's version of OpenSSL has EVP_AEAD_CTX, merge
- // internal CL 53267501.
- ScopedEVPCipherCtx ctx_;
-#endif
+ DISALLOW_COPY_AND_ASSIGN(Aes128Gcm12Decrypter);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc
index e0b42378021..0ba82698a86 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc
@@ -4,12 +4,10 @@
#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
-#include <nss.h>
#include <pk11pub.h>
#include <secerr.h>
#include "base/lazy_instance.h"
-#include "base/memory/scoped_ptr.h"
#include "crypto/ghash.h"
#include "crypto/scoped_nss_types.h"
@@ -23,31 +21,8 @@ namespace net {
namespace {
-// The pkcs11t.h header in NSS versions older than 3.14 does not have the CTR
-// and GCM types, so define them here.
-#if !defined(CKM_AES_CTR)
-#define CKM_AES_CTR 0x00001086
-#define CKM_AES_GCM 0x00001087
-
-struct CK_AES_CTR_PARAMS {
- CK_ULONG ulCounterBits;
- CK_BYTE cb[16];
-};
-
-struct CK_GCM_PARAMS {
- CK_BYTE_PTR pIv;
- CK_ULONG ulIvLen;
- CK_BYTE_PTR pAAD;
- CK_ULONG ulAADLen;
- CK_ULONG ulTagBits;
-};
-#endif // CKM_AES_CTR
-
-typedef SECStatus
-(*PK11_DecryptFunction)(
- PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
- unsigned char* out, unsigned int* outLen, unsigned int maxLen,
- const unsigned char* enc, unsigned encLen);
+const size_t kKeySize = 16;
+const size_t kNoncePrefixSize = 4;
// On Linux, dynamically link against the system version of libnss3.so. In
// order to continue working on systems without up-to-date versions of NSS,
@@ -61,10 +36,6 @@ class GcmSupportChecker {
return pk11_decrypt_func_;
}
- static CK_MECHANISM_TYPE aes_key_mechanism() {
- return aes_key_mechanism_;
- }
-
private:
friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>;
@@ -80,33 +51,19 @@ class GcmSupportChecker {
// AES-GCM directly. This was introduced in NSS 3.15.
pk11_decrypt_func_ = (PK11_DecryptFunction)dlsym(RTLD_DEFAULT,
"PK11_Decrypt");
- if (pk11_decrypt_func_ == NULL) {
- aes_key_mechanism_ = CKM_AES_ECB;
- }
#endif
}
// |pk11_decrypt_func_| stores the runtime symbol resolution of PK11_Decrypt.
static PK11_DecryptFunction pk11_decrypt_func_;
-
- // The correct value for |aes_key_mechanism_| is CKM_AES_GCM, but because of
- // NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=853285 (to be fixed in
- // NSS 3.15), use CKM_AES_ECB for NSS versions older than 3.15.
- static CK_MECHANISM_TYPE aes_key_mechanism_;
};
// static
PK11_DecryptFunction GcmSupportChecker::pk11_decrypt_func_ = NULL;
-// static
-CK_MECHANISM_TYPE GcmSupportChecker::aes_key_mechanism_ = CKM_AES_GCM;
-
base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker =
LAZY_INSTANCE_INITIALIZER;
-const size_t kNoncePrefixSize = 4;
-const size_t kAESNonceSize = 12;
-
// Calls PK11_Decrypt if it's available. Otherwise, emulates CKM_AES_GCM using
// CKM_AES_CTR and the GaloisHash class.
SECStatus My_Decrypt(PK11SymKey* key,
@@ -250,134 +207,30 @@ SECStatus My_Decrypt(PK11SymKey* key,
} // namespace
-Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() {
+Aes128Gcm12Decrypter::Aes128Gcm12Decrypter()
+ : AeadBaseDecrypter(CKM_AES_GCM, My_Decrypt, kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
ignore_result(g_gcm_support_checker.Get());
}
Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}
-// static
-bool Aes128Gcm12Decrypter::IsSupported() {
- // NSS 3.15 supports CKM_AES_GCM directly.
- // NSS 3.14 supports CKM_AES_CTR, which can be used to emulate CKM_AES_GCM.
- // Versions earlier than NSS 3.14 are not supported.
- return NSS_VersionCheck("3.14") != PR_FALSE;
-}
-
-bool Aes128Gcm12Decrypter::SetKey(StringPiece key) {
- DCHECK_EQ(key.size(), sizeof(key_));
- if (key.size() != sizeof(key_)) {
- return false;
- }
- memcpy(key_, key.data(), key.size());
- return true;
-}
-
-bool Aes128Gcm12Decrypter::SetNoncePrefix(StringPiece nonce_prefix) {
- DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize);
- if (nonce_prefix.size() != kNoncePrefixSize) {
- return false;
- }
- COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length);
- memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
- return true;
-}
-
-bool Aes128Gcm12Decrypter::Decrypt(StringPiece nonce,
- StringPiece associated_data,
- StringPiece ciphertext,
- uint8* output,
- size_t* output_length) {
- if (ciphertext.length() < kAuthTagSize ||
- nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) {
- return false;
- }
- // NSS 3.14.x incorrectly requires an output buffer at least as long as
- // the ciphertext (NSS bug
- // https://bugzilla.mozilla.org/show_bug.cgi?id= 853674). Fortunately
- // QuicDecrypter::Decrypt() specifies that |output| must be as long as
- // |ciphertext| on entry.
- size_t plaintext_size = ciphertext.length() - kAuthTagSize;
-
- // Import key_ into NSS.
- SECItem key_item;
- key_item.type = siBuffer;
- key_item.data = key_;
- key_item.len = sizeof(key_);
- PK11SlotInfo* slot = PK11_GetInternalSlot();
- // The exact value of the |origin| argument doesn't matter to NSS as long as
- // it's not PK11_OriginFortezzaHack, so pass PK11_OriginUnwrap as a
- // placeholder.
- crypto::ScopedPK11SymKey aes_key(PK11_ImportSymKey(
- slot, GcmSupportChecker::aes_key_mechanism(), PK11_OriginUnwrap,
- CKA_DECRYPT, &key_item, NULL));
- PK11_FreeSlot(slot);
- slot = NULL;
- if (!aes_key) {
- DVLOG(1) << "PK11_ImportSymKey failed";
- return false;
- }
-
- CK_GCM_PARAMS gcm_params = {0};
- gcm_params.pIv =
+void Aes128Gcm12Decrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.gcm_params);
+ CK_GCM_PARAMS* gcm_params = &aead_params->data.gcm_params;
+ gcm_params->pIv =
reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
- gcm_params.ulIvLen = nonce.size();
- gcm_params.pAAD =
+ gcm_params->ulIvLen = nonce.size();
+ gcm_params->pAAD =
reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
- gcm_params.ulAADLen = associated_data.size();
- gcm_params.ulTagBits = kAuthTagSize * 8;
-
- SECItem param;
- param.type = siBuffer;
- param.data = reinterpret_cast<unsigned char*>(&gcm_params);
- param.len = sizeof(gcm_params);
-
- unsigned int output_len;
- if (My_Decrypt(aes_key.get(), CKM_AES_GCM, &param,
- output, &output_len, ciphertext.length(),
- reinterpret_cast<const unsigned char*>(ciphertext.data()),
- ciphertext.length()) != SECSuccess) {
- return false;
- }
-
- if (output_len != plaintext_size) {
- DVLOG(1) << "Wrong output length";
- return false;
- }
- *output_length = output_len;
- return true;
-}
-
-QuicData* Aes128Gcm12Decrypter::DecryptPacket(
- QuicPacketSequenceNumber sequence_number,
- StringPiece associated_data,
- StringPiece ciphertext) {
- if (ciphertext.length() < kAuthTagSize) {
- return NULL;
- }
- size_t plaintext_size;
- scoped_ptr<char[]> plaintext(new char[ciphertext.length()]);
-
- uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
- COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
- memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
- memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number));
- if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)),
- associated_data, ciphertext,
- reinterpret_cast<uint8*>(plaintext.get()),
- &plaintext_size)) {
- return NULL;
- }
- return new QuicData(plaintext.release(), plaintext_size, true);
-}
-
-StringPiece Aes128Gcm12Decrypter::GetKey() const {
- return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_));
-}
-
-StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const {
- return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
- kNoncePrefixSize);
+ gcm_params->ulAADLen = associated_data.size();
+ gcm_params->ulTagBits = auth_tag_size * 8;
}
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc
index 56317fdd3c4..109d2dae85d 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc
@@ -6,142 +6,23 @@
#include <openssl/evp.h>
-#include "base/memory/scoped_ptr.h"
-
-using base::StringPiece;
-
namespace net {
-const size_t kNoncePrefixSize = 4;
-const size_t kAESNonceSize = 12;
-
-Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() {}
-
-Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}
-
-// static
-bool Aes128Gcm12Decrypter::IsSupported() { return true; }
-
-bool Aes128Gcm12Decrypter::SetKey(StringPiece key) {
- DCHECK_EQ(key.size(), sizeof(key_));
- if (key.size() != sizeof(key_)) {
- return false;
- }
- memcpy(key_, key.data(), key.size());
-
- // Set the cipher type and the key.
- if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_,
- NULL) == 0) {
- return false;
- }
-
- // Set the IV (nonce) length.
- if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize,
- NULL) == 0) {
- return false;
- }
-
- return true;
-}
-
-bool Aes128Gcm12Decrypter::SetNoncePrefix(StringPiece nonce_prefix) {
- DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize);
- if (nonce_prefix.size() != kNoncePrefixSize) {
- return false;
- }
- COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length);
- memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
- return true;
-}
+namespace {
-bool Aes128Gcm12Decrypter::Decrypt(StringPiece nonce,
- StringPiece associated_data,
- StringPiece ciphertext,
- uint8* output,
- size_t* output_length) {
- if (ciphertext.length() < kAuthTagSize ||
- nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) {
- return false;
- }
- const size_t plaintext_size = ciphertext.length() - kAuthTagSize;
-
- // Set the IV (nonce).
- if (EVP_DecryptInit_ex(
- ctx_.get(), NULL, NULL, NULL,
- reinterpret_cast<const uint8*>(nonce.data())) == 0) {
- return false;
- }
-
- // Set the authentication tag.
- if (EVP_CIPHER_CTX_ctrl(
- ctx_.get(), EVP_CTRL_GCM_SET_TAG, kAuthTagSize,
- const_cast<char*>(ciphertext.data()) + plaintext_size) == 0) {
- return false;
- }
-
- // If we pass a NULL, zero-length associated data to OpenSSL then it breaks.
- // Thus we only set non-empty associated data.
- if (!associated_data.empty()) {
- // Set the associated data. The second argument (output buffer) must be
- // NULL.
- int unused_len;
- if (EVP_DecryptUpdate(
- ctx_.get(), NULL, &unused_len,
- reinterpret_cast<const uint8*>(associated_data.data()),
- associated_data.size()) == 0) {
- return false;
- }
- }
-
- int len;
- if (EVP_DecryptUpdate(
- ctx_.get(), output, &len,
- reinterpret_cast<const uint8*>(ciphertext.data()),
- plaintext_size) == 0) {
- return false;
- }
- output += len;
-
- if (EVP_DecryptFinal_ex(ctx_.get(), output, &len) == 0) {
- return false;
- }
- output += len;
-
- *output_length = plaintext_size;
-
- return true;
-}
-
-QuicData* Aes128Gcm12Decrypter::DecryptPacket(
- QuicPacketSequenceNumber sequence_number,
- StringPiece associated_data,
- StringPiece ciphertext) {
- if (ciphertext.length() < kAuthTagSize) {
- return NULL;
- }
- size_t plaintext_size;
- scoped_ptr<char[]> plaintext(new char[ciphertext.length()]);
+const size_t kKeySize = 16;
+const size_t kNoncePrefixSize = 4;
- uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
- COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
- memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
- memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number));
- if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)),
- associated_data, ciphertext,
- reinterpret_cast<uint8*>(plaintext.get()),
- &plaintext_size)) {
- return NULL;
- }
- return new QuicData(plaintext.release(), plaintext_size, true);
-}
+} // namespace
-StringPiece Aes128Gcm12Decrypter::GetKey() const {
- return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_));
+Aes128Gcm12Decrypter::Aes128Gcm12Decrypter()
+ : AeadBaseDecrypter(EVP_aead_aes_128_gcm(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
}
-StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const {
- return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
- kNoncePrefixSize);
-}
+Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc
index dd37085894c..5a719a05a41 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc
@@ -268,69 +268,65 @@ QuicData* DecryptWithNonce(Aes128Gcm12Decrypter* decrypter,
}
TEST(Aes128Gcm12DecrypterTest, Decrypt) {
- if (!Aes128Gcm12Decrypter::IsSupported()) {
- LOG(INFO) << "AES GCM not supported. Test skipped.";
- return;
- }
-
- string key;
- string iv;
- string ct;
- string aad;
- string tag;
- string pt;
-
for (size_t i = 0; i < arraysize(test_group_array); i++) {
SCOPED_TRACE(i);
- const TestVector* test_vector = test_group_array[i];
+ const TestVector* test_vectors = test_group_array[i];
const TestGroupInfo& test_info = test_group_info[i];
- for (size_t j = 0; test_vector[j].key != NULL; j++) {
+ for (size_t j = 0; test_vectors[j].key != NULL; j++) {
// If not present then decryption is expected to fail.
- bool has_pt = test_vector[j].pt;
+ bool has_pt = test_vectors[j].pt;
// Decode the test vector.
- ASSERT_TRUE(DecodeHexString(test_vector[j].key, &key));
- ASSERT_TRUE(DecodeHexString(test_vector[j].iv, &iv));
- ASSERT_TRUE(DecodeHexString(test_vector[j].ct, &ct));
- ASSERT_TRUE(DecodeHexString(test_vector[j].aad, &aad));
- ASSERT_TRUE(DecodeHexString(test_vector[j].tag, &tag));
- if (has_pt)
- ASSERT_TRUE(DecodeHexString(test_vector[j].pt, &pt));
+ string key;
+ string iv;
+ string ct;
+ string aad;
+ string tag;
+ string pt;
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].ct, &ct));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].tag, &tag));
+ if (has_pt) {
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].pt, &pt));
+ }
// The test vector's lengths should look sane. Note that the lengths
// in |test_info| are in bits.
- EXPECT_EQ(test_info.key_len, key.size() * 8);
- EXPECT_EQ(test_info.iv_len, iv.size() * 8);
- EXPECT_EQ(test_info.pt_len, pt.size() * 8);
- EXPECT_EQ(test_info.aad_len, aad.size() * 8);
- EXPECT_EQ(test_info.tag_len, tag.size() * 8);
- if (has_pt)
- EXPECT_EQ(test_info.pt_len, pt.size() * 8);
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+ if (has_pt) {
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ }
// The test vectors have 16 byte authenticators but this code only uses
// the first 12.
ASSERT_LE(static_cast<size_t>(Aes128Gcm12Decrypter::kAuthTagSize),
- tag.size());
- string ciphertext =
- ct + tag.substr(0, Aes128Gcm12Decrypter::kAuthTagSize);
+ tag.length());
+ tag.resize(Aes128Gcm12Decrypter::kAuthTagSize);
+ string ciphertext = ct + tag;
Aes128Gcm12Decrypter decrypter;
ASSERT_TRUE(decrypter.SetKey(key));
scoped_ptr<QuicData> decrypted(DecryptWithNonce(
&decrypter, iv,
- // OpenSSL fails if NULL is set as the AAD, as opposed to a
- // zero-length, non-NULL pointer.
- aad.size() ? aad : StringPiece(), ciphertext));
+ // This deliberately tests that the decrypter can handle an AAD that
+ // is set to NULL, as opposed to a zero-length, non-NULL pointer.
+ aad.length() ? aad : StringPiece(), ciphertext));
if (!decrypted.get()) {
EXPECT_FALSE(has_pt);
continue;
}
EXPECT_TRUE(has_pt);
- ASSERT_EQ(pt.size(), decrypted->length());
+ ASSERT_EQ(pt.length(), decrypted->length());
test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
- pt.size(), pt.data(), pt.size());
+ pt.length(), pt.data(), pt.length());
}
}
}
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h
index a2d1dc9a088..1d8f32149bf 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h
@@ -5,28 +5,17 @@
#ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
#define NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "net/quic/crypto/quic_encrypter.h"
-
-#if defined(USE_OPENSSL)
-#include "net/quic/crypto/scoped_evp_cipher_ctx.h"
-#endif
+#include "net/quic/crypto/aead_base_encrypter.h"
namespace net {
-namespace test {
-class Aes128Gcm12EncrypterPeer;
-} // namespace test
-
// An Aes128Gcm12Encrypter is a QuicEncrypter that implements the
// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
// calling QuicEncrypter::Create(kAESG).
//
// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
// of the nonce is four bytes.
-class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public QuicEncrypter {
+class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public AeadBaseEncrypter {
public:
enum {
// Authentication tags are truncated to 96 bits.
@@ -36,34 +25,17 @@ class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public QuicEncrypter {
Aes128Gcm12Encrypter();
virtual ~Aes128Gcm12Encrypter();
- // QuicEncrypter implementation
- virtual bool SetKey(base::StringPiece key) OVERRIDE;
- virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
- virtual bool Encrypt(base::StringPiece nonce,
- base::StringPiece associated_data,
- base::StringPiece plaintext,
- unsigned char* output) OVERRIDE;
- virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number,
- base::StringPiece associated_data,
- base::StringPiece plaintext) OVERRIDE;
- virtual size_t GetKeySize() const OVERRIDE;
- virtual size_t GetNoncePrefixSize() const OVERRIDE;
- virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE;
- virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE;
- virtual base::StringPiece GetKey() const OVERRIDE;
- virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseEncrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
private:
- // The 128-bit AES key.
- unsigned char key_[16];
- // The nonce prefix.
- unsigned char nonce_prefix_[4];
-
-#if defined(USE_OPENSSL)
- // TODO(rtenneti): when Chromium's version of OpenSSL has EVP_AEAD_CTX, merge
- // internal CL 53267501.
- ScopedEVPCipherCtx ctx_;
-#endif
+ DISALLOW_COPY_AND_ASSIGN(Aes128Gcm12Encrypter);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
index 2cd072af461..09901a47672 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
@@ -4,12 +4,10 @@
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
-#include <nss.h>
#include <pk11pub.h>
#include <secerr.h>
#include "base/lazy_instance.h"
-#include "base/memory/scoped_ptr.h"
#include "crypto/ghash.h"
#include "crypto/scoped_nss_types.h"
@@ -23,31 +21,8 @@ namespace net {
namespace {
-// The pkcs11t.h header in NSS versions older than 3.14 does not have the CTR
-// and GCM types, so define them here.
-#if !defined(CKM_AES_CTR)
-#define CKM_AES_CTR 0x00001086
-#define CKM_AES_GCM 0x00001087
-
-struct CK_AES_CTR_PARAMS {
- CK_ULONG ulCounterBits;
- CK_BYTE cb[16];
-};
-
-struct CK_GCM_PARAMS {
- CK_BYTE_PTR pIv;
- CK_ULONG ulIvLen;
- CK_BYTE_PTR pAAD;
- CK_ULONG ulAADLen;
- CK_ULONG ulTagBits;
-};
-#endif // CKM_AES_CTR
-
-typedef SECStatus
-(*PK11_EncryptFunction)(
- PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
- unsigned char* out, unsigned int* outLen, unsigned int maxLen,
- const unsigned char* data, unsigned int dataLen);
+const size_t kKeySize = 16;
+const size_t kNoncePrefixSize = 4;
// On Linux, dynamically link against the system version of libnss3.so. In
// order to continue working on systems without up-to-date versions of NSS,
@@ -61,10 +36,6 @@ class GcmSupportChecker {
return pk11_encrypt_func_;
}
- static CK_MECHANISM_TYPE aes_key_mechanism() {
- return aes_key_mechanism_;
- }
-
private:
friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>;
@@ -80,34 +51,19 @@ class GcmSupportChecker {
// AES-GCM directly. This was introduced in NSS 3.15.
pk11_encrypt_func_ = (PK11_EncryptFunction)dlsym(RTLD_DEFAULT,
"PK11_Encrypt");
- if (pk11_encrypt_func_ == NULL) {
- aes_key_mechanism_ = CKM_AES_ECB;
- }
#endif
}
// |pk11_encrypt_func_| stores the runtime symbol resolution of PK11_Encrypt.
static PK11_EncryptFunction pk11_encrypt_func_;
-
- // The correct value for |aes_key_mechanism_| is CKM_AES_GCM, but because of
- // NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=853285 (to be fixed in
- // NSS 3.15), use CKM_AES_ECB for NSS versions older than 3.15.
- static CK_MECHANISM_TYPE aes_key_mechanism_;
};
// static
PK11_EncryptFunction GcmSupportChecker::pk11_encrypt_func_ = NULL;
-// static
-CK_MECHANISM_TYPE GcmSupportChecker::aes_key_mechanism_ = CKM_AES_GCM;
-
base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker =
LAZY_INSTANCE_INITIALIZER;
-const size_t kKeySize = 16;
-const size_t kNoncePrefixSize = 4;
-const size_t kAESNonceSize = 12;
-
// Calls PK11_Encrypt if it's available. Otherwise, emulates CKM_AES_GCM using
// CKM_AES_CTR and the GaloisHash class.
SECStatus My_Encrypt(PK11SymKey* key,
@@ -250,136 +206,30 @@ SECStatus My_Encrypt(PK11SymKey* key,
} // namespace
-Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() {
+Aes128Gcm12Encrypter::Aes128Gcm12Encrypter()
+ : AeadBaseEncrypter(CKM_AES_GCM, My_Encrypt, kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
ignore_result(g_gcm_support_checker.Get());
}
Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
-bool Aes128Gcm12Encrypter::SetKey(StringPiece key) {
- DCHECK_EQ(key.size(), sizeof(key_));
- if (key.size() != sizeof(key_)) {
- return false;
- }
- memcpy(key_, key.data(), key.size());
- return true;
-}
-
-bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) {
- DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize);
- if (nonce_prefix.size() != kNoncePrefixSize) {
- return false;
- }
- COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length);
- memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
- return true;
-}
-
-bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce,
- StringPiece associated_data,
- StringPiece plaintext,
- unsigned char* output) {
- if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) {
- return false;
- }
-
- size_t ciphertext_size = GetCiphertextSize(plaintext.length());
-
- // Import key_ into NSS.
- SECItem key_item;
- key_item.type = siBuffer;
- key_item.data = key_;
- key_item.len = sizeof(key_);
- PK11SlotInfo* slot = PK11_GetInternalSlot();
- // The exact value of the |origin| argument doesn't matter to NSS as long as
- // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a
- // placeholder.
- crypto::ScopedPK11SymKey aes_key(PK11_ImportSymKey(
- slot, GcmSupportChecker::aes_key_mechanism(), PK11_OriginUnwrap,
- CKA_ENCRYPT, &key_item, NULL));
- PK11_FreeSlot(slot);
- slot = NULL;
- if (!aes_key) {
- DVLOG(1) << "PK11_ImportSymKey failed";
- return false;
- }
-
- CK_GCM_PARAMS gcm_params = {0};
- gcm_params.pIv =
+void Aes128Gcm12Encrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.gcm_params);
+ CK_GCM_PARAMS* gcm_params = &aead_params->data.gcm_params;
+ gcm_params->pIv =
reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
- gcm_params.ulIvLen = nonce.size();
- gcm_params.pAAD =
+ gcm_params->ulIvLen = nonce.size();
+ gcm_params->pAAD =
reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
- gcm_params.ulAADLen = associated_data.size();
- gcm_params.ulTagBits = kAuthTagSize * 8;
-
- SECItem param;
- param.type = siBuffer;
- param.data = reinterpret_cast<unsigned char*>(&gcm_params);
- param.len = sizeof(gcm_params);
-
- unsigned int output_len;
- if (My_Encrypt(aes_key.get(), CKM_AES_GCM, &param,
- output, &output_len, ciphertext_size,
- reinterpret_cast<const unsigned char*>(plaintext.data()),
- plaintext.size()) != SECSuccess) {
- DVLOG(1) << "My_Encrypt failed";
- return false;
- }
-
- if (output_len != ciphertext_size) {
- DVLOG(1) << "Wrong output length";
- return false;
- }
-
- return true;
-}
-
-QuicData* Aes128Gcm12Encrypter::EncryptPacket(
- QuicPacketSequenceNumber sequence_number,
- StringPiece associated_data,
- StringPiece plaintext) {
- size_t ciphertext_size = GetCiphertextSize(plaintext.length());
- scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
-
- // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
- // same sequence number twice.
- uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
- COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
- memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
- memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number));
- if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)),
- associated_data, plaintext,
- reinterpret_cast<unsigned char*>(ciphertext.get()))) {
- return NULL;
- }
-
- return new QuicData(ciphertext.release(), ciphertext_size, true);
-}
-
-size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; }
-
-size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const {
- return kNoncePrefixSize;
-}
-
-size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
- return ciphertext_size - kAuthTagSize;
-}
-
-// An AEAD_AES_128_GCM_12 ciphertext is exactly 12 bytes longer than its
-// corresponding plaintext.
-size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const {
- return plaintext_size + kAuthTagSize;
-}
-
-StringPiece Aes128Gcm12Encrypter::GetKey() const {
- return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_));
-}
-
-StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const {
- return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
- kNoncePrefixSize);
+ gcm_params->ulAADLen = associated_data.size();
+ gcm_params->ulTagBits = auth_tag_size * 8;
}
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
index 394971a7382..6489528fd37 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
@@ -4,13 +4,7 @@
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
-#include <openssl/err.h>
#include <openssl/evp.h>
-#include <string.h>
-
-#include "base/memory/scoped_ptr.h"
-
-using base::StringPiece;
namespace net {
@@ -18,161 +12,17 @@ namespace {
const size_t kKeySize = 16;
const size_t kNoncePrefixSize = 4;
-const size_t kAESNonceSize = 12;
-
-void ClearOpenSslErrors() {
-#ifdef NDEBUG
- while (ERR_get_error()) {}
-#else
- while (long error = ERR_get_error()) {
- char buf[120];
- ERR_error_string_n(error, buf, arraysize(buf));
- DLOG(ERROR) << "OpenSSL error: " << buf;
- }
-#endif
-}
} // namespace
-Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() {}
-
-Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
-
-bool Aes128Gcm12Encrypter::SetKey(StringPiece key) {
- DCHECK_EQ(key.size(), sizeof(key_));
- if (key.size() != sizeof(key_)) {
- return false;
- }
- memcpy(key_, key.data(), key.size());
-
- // Set the cipher type and the key.
- if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_,
- NULL) == 0) {
- ClearOpenSslErrors();
- return false;
- }
-
- // Set the IV (nonce) length.
- if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize,
- NULL) == 0) {
- ClearOpenSslErrors();
- return false;
- }
-
- return true;
-}
-
-bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) {
- DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize);
- if (nonce_prefix.size() != kNoncePrefixSize) {
- return false;
- }
- COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length);
- memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
- return true;
+Aes128Gcm12Encrypter::Aes128Gcm12Encrypter()
+ : AeadBaseEncrypter(EVP_aead_aes_128_gcm(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
}
-bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce,
- StringPiece associated_data,
- StringPiece plaintext,
- unsigned char* output) {
- if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) {
- return false;
- }
-
- // Set the IV (nonce).
- if (EVP_EncryptInit_ex(
- ctx_.get(), NULL, NULL, NULL,
- reinterpret_cast<const unsigned char*>(nonce.data())) == 0) {
- ClearOpenSslErrors();
- return false;
- }
-
- // If we pass a NULL, zero-length associated data to OpenSSL then it breaks.
- // Thus we only set non-empty associated data.
- if (!associated_data.empty()) {
- // Set the associated data. The second argument (output buffer) must be
- // NULL.
- int unused_len;
- if (EVP_EncryptUpdate(
- ctx_.get(), NULL, &unused_len,
- reinterpret_cast<const unsigned char*>(associated_data.data()),
- associated_data.size()) == 0) {
- ClearOpenSslErrors();
- return false;
- }
- }
-
- int len;
- if (EVP_EncryptUpdate(
- ctx_.get(), output, &len,
- reinterpret_cast<const unsigned char*>(plaintext.data()),
- plaintext.size()) == 0) {
- ClearOpenSslErrors();
- return false;
- }
- output += len;
-
- if (EVP_EncryptFinal_ex(ctx_.get(), output, &len) == 0) {
- ClearOpenSslErrors();
- return false;
- }
- output += len;
-
- if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_GET_TAG, kAuthTagSize,
- output) == 0) {
- ClearOpenSslErrors();
- return false;
- }
-
- return true;
-}
-
-QuicData* Aes128Gcm12Encrypter::EncryptPacket(
- QuicPacketSequenceNumber sequence_number,
- StringPiece associated_data,
- StringPiece plaintext) {
- size_t ciphertext_size = GetCiphertextSize(plaintext.length());
- scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
-
- // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
- // same sequence number twice.
- uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
- COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
- memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
- memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number));
- if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)),
- associated_data, plaintext,
- reinterpret_cast<unsigned char*>(ciphertext.get()))) {
- return NULL;
- }
-
- return new QuicData(ciphertext.release(), ciphertext_size, true);
-}
-
-size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; }
-
-size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const {
- return kNoncePrefixSize;
-}
-
-size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
- return ciphertext_size - kAuthTagSize;
-}
-
-// An AEAD_AES_128_GCM_12 ciphertext is exactly 12 bytes longer than its
-// corresponding plaintext.
-size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const {
- return plaintext_size + kAuthTagSize;
-}
-
-StringPiece Aes128Gcm12Encrypter::GetKey() const {
- return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_));
-}
-
-StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const {
- return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
- kNoncePrefixSize);
-}
+Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
} // namespace net
diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc
index bfbf93cd716..afe431002e8 100644
--- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc
+++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc
@@ -224,57 +224,55 @@ QuicData* EncryptWithNonce(Aes128Gcm12Encrypter* encrypter,
}
TEST(Aes128Gcm12EncrypterTest, Encrypt) {
- string key;
- string iv;
- string pt;
- string aad;
- string ct;
- string tag;
-
for (size_t i = 0; i < arraysize(test_group_array); i++) {
SCOPED_TRACE(i);
- const TestVector* test_vector = test_group_array[i];
+ const TestVector* test_vectors = test_group_array[i];
const TestGroupInfo& test_info = test_group_info[i];
- for (size_t j = 0; test_vector[j].key != NULL; j++) {
+ for (size_t j = 0; test_vectors[j].key != NULL; j++) {
// Decode the test vector.
- ASSERT_TRUE(DecodeHexString(test_vector[j].key, &key));
- ASSERT_TRUE(DecodeHexString(test_vector[j].iv, &iv));
- ASSERT_TRUE(DecodeHexString(test_vector[j].pt, &pt));
- ASSERT_TRUE(DecodeHexString(test_vector[j].aad, &aad));
- ASSERT_TRUE(DecodeHexString(test_vector[j].ct, &ct));
- ASSERT_TRUE(DecodeHexString(test_vector[j].tag, &tag));
+ string key;
+ string iv;
+ string pt;
+ string aad;
+ string ct;
+ string tag;
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].pt, &pt));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].ct, &ct));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].tag, &tag));
// The test vector's lengths should look sane. Note that the lengths
// in |test_info| are in bits.
- EXPECT_EQ(test_info.key_len, key.size() * 8);
- EXPECT_EQ(test_info.iv_len, iv.size() * 8);
- EXPECT_EQ(test_info.pt_len, pt.size() * 8);
- EXPECT_EQ(test_info.aad_len, aad.size() * 8);
- EXPECT_EQ(test_info.pt_len, ct.size() * 8);
- EXPECT_EQ(test_info.tag_len, tag.size() * 8);
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
Aes128Gcm12Encrypter encrypter;
ASSERT_TRUE(encrypter.SetKey(key));
scoped_ptr<QuicData> encrypted(EncryptWithNonce(
&encrypter, iv,
- // OpenSSL fails if NULL is set as the AAD, as opposed to a
- // zero-length, non-NULL pointer. This deliberately tests that we
- // handle this case.
- aad.size() ? aad : StringPiece(), pt));
+ // This deliberately tests that the encrypter can handle an AAD that
+ // is set to NULL, as opposed to a zero-length, non-NULL pointer.
+ aad.length() ? aad : StringPiece(), pt));
ASSERT_TRUE(encrypted.get());
// The test vectors have 16 byte authenticators but this code only uses
// the first 12.
ASSERT_LE(static_cast<size_t>(Aes128Gcm12Encrypter::kAuthTagSize),
- tag.size());
- size_t tag_len = Aes128Gcm12Encrypter::kAuthTagSize;
+ tag.length());
+ tag.resize(Aes128Gcm12Encrypter::kAuthTagSize);
- ASSERT_EQ(ct.size() + tag_len, encrypted->length());
+ ASSERT_EQ(ct.length() + tag.length(), encrypted->length());
test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
- ct.size(), ct.data(), ct.size());
+ ct.length(), ct.data(), ct.length());
test::CompareCharArraysWithHexError(
- "authentication tag", encrypted->data() + ct.size(), tag_len,
- tag.data(), tag_len);
+ "authentication tag", encrypted->data() + ct.length(), tag.length(),
+ tag.data(), tag.length());
}
}
}
diff --git a/chromium/net/quic/crypto/cert_compressor.h b/chromium/net/quic/crypto/cert_compressor.h
index 7b7e2f0eb05..d95c5bce540 100644
--- a/chromium/net/quic/crypto/cert_compressor.h
+++ b/chromium/net/quic/crypto/cert_compressor.h
@@ -48,6 +48,9 @@ class NET_EXPORT_PRIVATE CertCompressor {
const std::vector<std::string>& cached_certs,
const CommonCertSets* common_sets,
std::vector<std::string>* out_certs);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CertCompressor);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter.h b/chromium/net/quic/crypto/chacha20_poly1305_decrypter.h
new file mode 100644
index 00000000000..9d24ba2d13e
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter.h
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
+#define NET_QUIC_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
+
+#include "net/quic/crypto/aead_base_decrypter.h"
+
+namespace net {
+
+// A ChaCha20Poly1305Decrypter is a QuicDecrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in
+// draft-agl-tls-chacha20poly1305-04, except that it truncates the Poly1305
+// authenticator to 12 bytes. Create an instance by calling
+// QuicDecrypter::Create(kCC12).
+//
+// It uses an authentication tag of 16 bytes (128 bits). There is no
+// fixed nonce prefix.
+class NET_EXPORT_PRIVATE ChaCha20Poly1305Decrypter : public AeadBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 12,
+ };
+
+ ChaCha20Poly1305Decrypter();
+ virtual ~ChaCha20Poly1305Decrypter();
+
+ // Returns true if the underlying crypto library supports ChaCha20+Poly1305.
+ static bool IsSupported();
+
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseDecrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChaCha20Poly1305Decrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc
new file mode 100644
index 00000000000..e8100af49f6
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_decrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/logging.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+#if defined(USE_NSS)
+
+// System NSS doesn't support ChaCha20+Poly1305 yet.
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : AeadBaseDecrypter(CKM_INVALID_MECHANISM, NULL, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ NOTIMPLEMENTED();
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+// static
+bool ChaCha20Poly1305Decrypter::IsSupported() {
+ return false;
+}
+
+void ChaCha20Poly1305Decrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ NOTIMPLEMENTED();
+}
+
+#else // defined(USE_NSS)
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : AeadBaseDecrypter(CKM_NSS_CHACHA20_POLY1305, PK11_Decrypt, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+// static
+bool ChaCha20Poly1305Decrypter::IsSupported() {
+ return true;
+}
+
+void ChaCha20Poly1305Decrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.nss_aead_params);
+ CK_NSS_AEAD_PARAMS* nss_aead_params = &aead_params->data.nss_aead_params;
+ nss_aead_params->pIv =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
+ nss_aead_params->ulIvLen = nonce.size();
+ nss_aead_params->pAAD =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
+ nss_aead_params->ulAADLen = associated_data.size();
+ nss_aead_params->ulTagLen = auth_tag_size;
+}
+
+#endif // defined(USE_NSS)
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc
new file mode 100644
index 00000000000..7f0e24da704
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_decrypter.h"
+
+#include <openssl/evp.h>
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : AeadBaseDecrypter(EVP_aead_chacha20_poly1305(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+// static
+bool ChaCha20Poly1305Decrypter::IsSupported() { return true; }
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc
new file mode 100644
index 00000000000..1825187bb33
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc
@@ -0,0 +1,132 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_decrypter.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+
+namespace {
+
+// The test vectors come from draft-agl-tls-chacha20poly1305-04 Section 7.
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a NULL |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* aad;
+ const char* ct;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. NULL means decryption
+ // failed.
+};
+
+const TestVector test_vectors[] = {
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated.
+ "86d09974840bded2a5ca"
+ },
+ // Modify the ciphertext (ChaCha20 encryption output).
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "f3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated.
+ NULL // FAIL
+ },
+ // Modify the ciphertext (Poly1305 authenticator).
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28476", // "3896e1d6" truncated.
+ NULL // FAIL
+ },
+ // Modify the associated data.
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "dd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated.
+ NULL // FAIL
+ },
+ { NULL }
+};
+
+} // namespace
+
+namespace net {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(ChaCha20Poly1305Decrypter* decrypter,
+ StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ size_t plaintext_size = ciphertext.length();
+ scoped_ptr<char[]> plaintext(new char[plaintext_size]);
+
+ if (!decrypter->Decrypt(nonce, associated_data, ciphertext,
+ reinterpret_cast<unsigned char*>(plaintext.get()),
+ &plaintext_size)) {
+ return NULL;
+ }
+ return new QuicData(plaintext.release(), plaintext_size, true);
+}
+
+TEST(ChaCha20Poly1305DecrypterTest, Decrypt) {
+ if (!ChaCha20Poly1305Decrypter::IsSupported()) {
+ LOG(INFO) << "ChaCha20+Poly1305 not supported. Test skipped.";
+ return;
+ }
+
+ for (size_t i = 0; test_vectors[i].key != NULL; i++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[i].pt;
+
+ // Decode the test vector.
+ string key;
+ string iv;
+ string aad;
+ string ct;
+ string pt;
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].ct, &ct));
+ if (has_pt) {
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].pt, &pt));
+ }
+
+ ChaCha20Poly1305Decrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+ scoped_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, iv,
+ // This deliberately tests that the decrypter can handle an AAD that
+ // is set to NULL, as opposed to a zero-length, non-NULL pointer.
+ StringPiece(aad.length() ? aad.data() : NULL, aad.length()), ct));
+ if (!decrypted.get()) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ ASSERT_EQ(pt.length(), decrypted->length());
+ test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
+ pt.length(), pt.data(), pt.length());
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter.h b/chromium/net/quic/crypto/chacha20_poly1305_encrypter.h
new file mode 100644
index 00000000000..4a68caa531b
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter.h
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
+#define NET_QUIC_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
+
+#include "net/quic/crypto/aead_base_encrypter.h"
+
+namespace net {
+
+// A ChaCha20Poly1305Encrypter is a QuicEncrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in
+// draft-agl-tls-chacha20poly1305-04, except that it truncates the Poly1305
+// authenticator to 12 bytes. Create an instance by calling
+// QuicEncrypter::Create(kCC12).
+//
+// It uses an authentication tag of 16 bytes (128 bits). There is no
+// fixed nonce prefix.
+class NET_EXPORT_PRIVATE ChaCha20Poly1305Encrypter : public AeadBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 12,
+ };
+
+ ChaCha20Poly1305Encrypter();
+ virtual ~ChaCha20Poly1305Encrypter();
+
+ // Returns true if the underlying crypto library supports ChaCha20+Poly1305.
+ static bool IsSupported();
+
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseEncrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChaCha20Poly1305Encrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc
new file mode 100644
index 00000000000..fd6f76ca52f
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/logging.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+#if defined(USE_NSS)
+
+// System NSS doesn't support ChaCha20+Poly1305 yet.
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : AeadBaseEncrypter(CKM_INVALID_MECHANISM, NULL, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ NOTIMPLEMENTED();
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+// static
+bool ChaCha20Poly1305Encrypter::IsSupported() {
+ return false;
+}
+
+void ChaCha20Poly1305Encrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ NOTIMPLEMENTED();
+}
+
+#else // defined(USE_NSS)
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : AeadBaseEncrypter(CKM_NSS_CHACHA20_POLY1305, PK11_Encrypt, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+// static
+bool ChaCha20Poly1305Encrypter::IsSupported() {
+ return true;
+}
+
+void ChaCha20Poly1305Encrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.nss_aead_params);
+ CK_NSS_AEAD_PARAMS* nss_aead_params = &aead_params->data.nss_aead_params;
+ nss_aead_params->pIv =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
+ nss_aead_params->ulIvLen = nonce.size();
+ nss_aead_params->pAAD =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
+ nss_aead_params->ulAADLen = associated_data.size();
+ nss_aead_params->ulTagLen = auth_tag_size;
+}
+
+#endif // defined(USE_NSS)
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc
new file mode 100644
index 00000000000..e256c2a5807
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+
+#include <openssl/evp.h>
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : AeadBaseEncrypter(EVP_aead_chacha20_poly1305(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+// static
+bool ChaCha20Poly1305Encrypter::IsSupported() { return true; }
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc
new file mode 100644
index 00000000000..0273b27933c
--- /dev/null
+++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+
+namespace {
+
+// The test vectors come from draft-agl-tls-chacha20poly1305-04 Section 7.
+
+// Each test vector consists of five strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a NULL |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* pt;
+ const char* iv;
+ const char* aad;
+ const char* ct;
+};
+
+const TestVector test_vectors[] = {
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "86d09974840bded2a5ca",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475" // "3896e1d6" truncated.
+ },
+ { NULL }
+};
+
+} // namespace
+
+namespace net {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(ChaCha20Poly1305Encrypter* encrypter,
+ StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return NULL;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+TEST(ChaCha20Poly1305EncrypterTest, Encrypt) {
+ if (!ChaCha20Poly1305Encrypter::IsSupported()) {
+ LOG(INFO) << "ChaCha20+Poly1305 not supported. Test skipped.";
+ return;
+ }
+
+ for (size_t i = 0; test_vectors[i].key != NULL; i++) {
+ // Decode the test vector.
+ string key;
+ string pt;
+ string iv;
+ string aad;
+ string ct;
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].pt, &pt));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].ct, &ct));
+
+ ChaCha20Poly1305Encrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ scoped_ptr<QuicData> encrypted(EncryptWithNonce(
+ &encrypter, iv,
+ // This deliberately tests that the encrypter can handle an AAD that
+ // is set to NULL, as opposed to a zero-length, non-NULL pointer.
+ StringPiece(aad.length() ? aad.data() : NULL, aad.length()), pt));
+ ASSERT_TRUE(encrypted.get());
+
+ test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ encrypted->length(), ct.data(),
+ ct.length());
+ }
+}
+
+TEST(ChaCha20Poly1305EncrypterTest, GetMaxPlaintextSize) {
+ ChaCha20Poly1305Encrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+}
+
+TEST(ChaCha20Poly1305EncrypterTest, GetCiphertextSize) {
+ ChaCha20Poly1305Encrypter encrypter;
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/crypto/channel_id.h b/chromium/net/quic/crypto/channel_id.h
index 2d0c29de25a..7d29932406b 100644
--- a/chromium/net/quic/crypto/channel_id.h
+++ b/chromium/net/quic/crypto/channel_id.h
@@ -7,28 +7,59 @@
#include <string>
+#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
+#include "net/quic/quic_types.h"
namespace net {
-// ChannelIDSigner is an abstract interface that implements signing by
-// ChannelID keys.
-class NET_EXPORT_PRIVATE ChannelIDSigner {
+// ChannelIDKey is an interface that supports signing with and serializing a
+// ChannelID key.
+class NET_EXPORT_PRIVATE ChannelIDKey {
public:
- virtual ~ChannelIDSigner() { }
-
- // Sign signs |signed_data| using the ChannelID key for |hostname| and puts
- // the serialized public key into |out_key| and the signature into
- // |out_signature|. It returns true on success.
- virtual bool Sign(const std::string& hostname,
- base::StringPiece signed_data,
- std::string* out_key,
- std::string* out_signature) = 0;
-
- // GetKeyForHostname returns the ChannelID key that |ChannelIDSigner| will use
- // for the given hostname.
- virtual std::string GetKeyForHostname(const std::string& hostname) = 0;
+ virtual ~ChannelIDKey() {}
+
+ // Sign signs |signed_data| using the ChannelID private key and puts the
+ // signature into |out_signature|. It returns true on success.
+ virtual bool Sign(base::StringPiece signed_data,
+ std::string* out_signature) const = 0;
+
+ // SerializeKey returns the serialized ChannelID public key.
+ virtual std::string SerializeKey() const = 0;
+};
+
+// ChannelIDSourceCallback provides a generic mechanism for a ChannelIDSource
+// to call back after an asynchronous GetChannelIDKey operation.
+class ChannelIDSourceCallback {
+ public:
+ virtual ~ChannelIDSourceCallback() {}
+
+ // Run is called on the original thread to mark the completion of an
+ // asynchonous GetChannelIDKey operation. If |*channel_id_key| is not NULL
+ // then the channel ID lookup is successful. |Run| may take ownership of
+ // |*channel_id_key| by calling |release| on it.
+ virtual void Run(scoped_ptr<ChannelIDKey>* channel_id_key) = 0;
+};
+
+// ChannelIDSource is an abstract interface by which a QUIC client can obtain
+// a ChannelIDKey for a given hostname.
+class NET_EXPORT_PRIVATE ChannelIDSource {
+ public:
+ virtual ~ChannelIDSource() {}
+
+ // GetChannelIDKey looks up the ChannelIDKey for |hostname|. On success it
+ // returns QUIC_SUCCESS and stores the ChannelIDKey in |*channel_id_key|,
+ // which the caller takes ownership of. On failure, it returns QUIC_FAILURE.
+ //
+ // This function may also return QUIC_PENDING, in which case the
+ // ChannelIDSource will call back, on the original thread, via |callback|
+ // when complete. In this case, the ChannelIDSource will take ownership of
+ // |callback|.
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const std::string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) = 0;
};
// ChannelIDVerifier verifies ChannelID signatures.
@@ -57,6 +88,9 @@ class NET_EXPORT_PRIVATE ChannelIDVerifier {
base::StringPiece signed_data,
base::StringPiece signature,
bool is_channel_id_signature);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChannelIDVerifier);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/channel_id_test.cc b/chromium/net/quic/crypto/channel_id_test.cc
index 48319ec334b..711060c9d7f 100644
--- a/chromium/net/quic/crypto/channel_id_test.cc
+++ b/chromium/net/quic/crypto/channel_id_test.cc
@@ -220,16 +220,20 @@ TEST(ChannelIDTest, VerifyKnownAnswerTest) {
}
TEST(ChannelIDTest, SignAndVerify) {
- scoped_ptr<ChannelIDSigner> signer(
- CryptoTestUtils::ChannelIDSignerForTesting());
+ scoped_ptr<ChannelIDSource> source(
+ CryptoTestUtils::ChannelIDSourceForTesting());
const string signed_data = "signed data";
const string hostname = "foo.example.com";
- string key, signature;
- ASSERT_TRUE(signer->Sign(hostname, signed_data, &key, &signature));
+ scoped_ptr<ChannelIDKey> channel_id_key;
+ QuicAsyncStatus status =
+ source->GetChannelIDKey(hostname, &channel_id_key, NULL);
+ ASSERT_EQ(QUIC_SUCCESS, status);
- EXPECT_EQ(key, signer->GetKeyForHostname(hostname));
+ string signature;
+ ASSERT_TRUE(channel_id_key->Sign(signed_data, &signature));
+ string key = channel_id_key->SerializeKey();
EXPECT_TRUE(ChannelIDVerifier::Verify(key, signed_data, signature));
EXPECT_FALSE(ChannelIDVerifier::Verify("a" + key, signed_data, signature));
diff --git a/chromium/net/quic/crypto/crypto_framer.cc b/chromium/net/quic/crypto/crypto_framer.cc
index dd5d24f9ae2..5cb16743e69 100644
--- a/chromium/net/quic/crypto/crypto_framer.cc
+++ b/chromium/net/quic/crypto/crypto_framer.cc
@@ -4,7 +4,7 @@
#include "net/quic/crypto/crypto_framer.h"
-#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/quic_data_reader.h"
#include "net/quic/quic_data_writer.h"
@@ -24,22 +24,21 @@ const size_t kNumEntriesSize = sizeof(uint16);
// OneShotVisitor is a framer visitor that records a single handshake message.
class OneShotVisitor : public CryptoFramerVisitorInterface {
public:
- explicit OneShotVisitor(CryptoHandshakeMessage* out)
- : out_(out),
- error_(false) {
- }
+ OneShotVisitor() : error_(false) {}
virtual void OnError(CryptoFramer* framer) OVERRIDE { error_ = true; }
virtual void OnHandshakeMessage(
const CryptoHandshakeMessage& message) OVERRIDE {
- *out_ = message;
+ out_.reset(new CryptoHandshakeMessage(message));
}
bool error() const { return error_; }
+ CryptoHandshakeMessage* release() { return out_.release(); }
+
private:
- CryptoHandshakeMessage* const out_;
+ scoped_ptr<CryptoHandshakeMessage> out_;
bool error_;
};
@@ -56,8 +55,7 @@ CryptoFramer::~CryptoFramer() {}
// static
CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) {
- scoped_ptr<CryptoHandshakeMessage> msg(new CryptoHandshakeMessage);
- OneShotVisitor visitor(msg.get());
+ OneShotVisitor visitor;
CryptoFramer framer;
framer.set_visitor(&visitor);
@@ -66,7 +64,7 @@ CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) {
return NULL;
}
- return msg.release();
+ return visitor.release();
}
bool CryptoFramer::ProcessInput(StringPiece input) {
diff --git a/chromium/net/quic/crypto/crypto_framer.h b/chromium/net/quic/crypto/crypto_framer.h
index ea69f3a5724..7ce714a666b 100644
--- a/chromium/net/quic/crypto/crypto_framer.h
+++ b/chromium/net/quic/crypto/crypto_framer.h
@@ -10,11 +10,9 @@
#include "base/basictypes.h"
#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
-#include "net/quic/crypto/crypto_handshake.h"
-#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
#include "net/quic/quic_protocol.h"
namespace net {
diff --git a/chromium/net/quic/crypto/crypto_handshake.cc b/chromium/net/quic/crypto/crypto_handshake.cc
index 056272e9c94..408b76bdecf 100644
--- a/chromium/net/quic/crypto/crypto_handshake.cc
+++ b/chromium/net/quic/crypto/crypto_handshake.cc
@@ -4,323 +4,13 @@
#include "net/quic/crypto/crypto_handshake.h"
-#include <ctype.h>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "crypto/secure_hash.h"
-#include "net/base/net_util.h"
#include "net/quic/crypto/common_cert_set.h"
-#include "net/quic/crypto/crypto_framer.h"
#include "net/quic/crypto/key_exchange.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
-#include "net/quic/crypto/quic_random.h"
-#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_utils.h"
-
-using base::StringPiece;
-using base::StringPrintf;
-using std::string;
-using std::vector;
namespace net {
-CryptoHandshakeMessage::CryptoHandshakeMessage()
- : tag_(0),
- minimum_size_(0) {}
-
-CryptoHandshakeMessage::CryptoHandshakeMessage(
- const CryptoHandshakeMessage& other)
- : tag_(other.tag_),
- tag_value_map_(other.tag_value_map_),
- minimum_size_(other.minimum_size_) {
- // Don't copy serialized_. scoped_ptr doesn't have a copy constructor.
- // The new object can lazily reconstruct serialized_.
-}
-
-CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
-
-CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
- const CryptoHandshakeMessage& other) {
- tag_ = other.tag_;
- tag_value_map_ = other.tag_value_map_;
- // Don't copy serialized_. scoped_ptr doesn't have an assignment operator.
- // However, invalidate serialized_.
- serialized_.reset();
- minimum_size_ = other.minimum_size_;
- return *this;
-}
-
-void CryptoHandshakeMessage::Clear() {
- tag_ = 0;
- tag_value_map_.clear();
- minimum_size_ = 0;
- serialized_.reset();
-}
-
-const QuicData& CryptoHandshakeMessage::GetSerialized() const {
- if (!serialized_.get()) {
- serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this));
- }
- return *serialized_.get();
-}
-
-void CryptoHandshakeMessage::MarkDirty() {
- serialized_.reset();
-}
-
-void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) {
- // Warning, if sizeof(QuicTag) > sizeof(int) then this function will break
- // because the terminating 0 will only be promoted to int.
- COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int),
- crypto_tag_may_not_be_larger_than_int_or_varargs_will_break);
-
- vector<QuicTag> tags;
- va_list ap;
-
- va_start(ap, tag);
- for (;;) {
- QuicTag list_item = va_arg(ap, QuicTag);
- if (list_item == 0) {
- break;
- }
- tags.push_back(list_item);
- }
-
- // Because of the way that we keep tags in memory, we can copy the contents
- // of the vector and get the correct bytes in wire format. See
- // crypto_protocol.h. This assumes that the system is little-endian.
- SetVector(tag, tags);
-
- va_end(ap);
-}
-
-void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) {
- tag_value_map_[tag] = value.as_string();
-}
-
-void CryptoHandshakeMessage::Erase(QuicTag tag) {
- tag_value_map_.erase(tag);
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag,
- const QuicTag** out_tags,
- size_t* out_len) const {
- QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
- QuicErrorCode ret = QUIC_NO_ERROR;
-
- if (it == tag_value_map_.end()) {
- ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
- } else if (it->second.size() % sizeof(QuicTag) != 0) {
- ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- if (ret != QUIC_NO_ERROR) {
- *out_tags = NULL;
- *out_len = 0;
- return ret;
- }
-
- *out_tags = reinterpret_cast<const QuicTag*>(it->second.data());
- *out_len = it->second.size() / sizeof(QuicTag);
- return ret;
-}
-
-bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag,
- StringPiece* out) const {
- QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
- if (it == tag_value_map_.end()) {
- return false;
- }
- *out = it->second;
- return true;
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag tag,
- unsigned index,
- StringPiece* out) const {
- StringPiece value;
- if (!GetStringPiece(tag, &value)) {
- return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
- }
-
- for (unsigned i = 0;; i++) {
- if (value.empty()) {
- return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND;
- }
- if (value.size() < 3) {
- return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- const unsigned char* data =
- reinterpret_cast<const unsigned char*>(value.data());
- size_t size = static_cast<size_t>(data[0]) |
- (static_cast<size_t>(data[1]) << 8) |
- (static_cast<size_t>(data[2]) << 16);
- value.remove_prefix(3);
-
- if (value.size() < size) {
- return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- if (i == index) {
- *out = StringPiece(value.data(), size);
- return QUIC_NO_ERROR;
- }
-
- value.remove_prefix(size);
- }
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag,
- uint16* out) const {
- return GetPOD(tag, out, sizeof(uint16));
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag,
- uint32* out) const {
- return GetPOD(tag, out, sizeof(uint32));
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag,
- uint64* out) const {
- return GetPOD(tag, out, sizeof(uint64));
-}
-
-size_t CryptoHandshakeMessage::size() const {
- size_t ret = sizeof(QuicTag) +
- sizeof(uint16) /* number of entries */ +
- sizeof(uint16) /* padding */;
- ret += (sizeof(QuicTag) + sizeof(uint32) /* end offset */) *
- tag_value_map_.size();
- for (QuicTagValueMap::const_iterator i = tag_value_map_.begin();
- i != tag_value_map_.end(); ++i) {
- ret += i->second.size();
- }
-
- return ret;
-}
-
-void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) {
- if (min_bytes == minimum_size_) {
- return;
- }
- serialized_.reset();
- minimum_size_ = min_bytes;
-}
-
-size_t CryptoHandshakeMessage::minimum_size() const {
- return minimum_size_;
-}
-
-string CryptoHandshakeMessage::DebugString() const {
- return DebugStringInternal(0);
-}
-
-QuicErrorCode CryptoHandshakeMessage::GetPOD(
- QuicTag tag, void* out, size_t len) const {
- QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
- QuicErrorCode ret = QUIC_NO_ERROR;
-
- if (it == tag_value_map_.end()) {
- ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
- } else if (it->second.size() != len) {
- ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
- }
-
- if (ret != QUIC_NO_ERROR) {
- memset(out, 0, len);
- return ret;
- }
-
- memcpy(out, it->second.data(), len);
- return ret;
-}
-
-string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
- string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n";
- ++indent;
- for (QuicTagValueMap::const_iterator it = tag_value_map_.begin();
- it != tag_value_map_.end(); ++it) {
- ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": ";
-
- bool done = false;
- switch (it->first) {
- case kICSL:
- case kIRTT:
- case kKATO:
- case kMSPC:
- case kSWND:
- // uint32 value
- if (it->second.size() == 4) {
- uint32 value;
- memcpy(&value, it->second.data(), sizeof(value));
- ret += base::UintToString(value);
- done = true;
- }
- break;
- case kVERS:
- // uint16 value
- if (it->second.size() == 2) {
- uint16 value;
- memcpy(&value, it->second.data(), sizeof(value));
- ret += base::UintToString(value);
- done = true;
- }
- break;
- case kKEXS:
- case kAEAD:
- case kCGST:
- case kPDMD:
- case kVER:
- // tag lists
- if (it->second.size() % sizeof(QuicTag) == 0) {
- for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) {
- QuicTag tag;
- memcpy(&tag, it->second.data() + j, sizeof(tag));
- if (j > 0) {
- ret += ",";
- }
- ret += "'" + QuicUtils::TagToString(tag) + "'";
- }
- done = true;
- }
- break;
- case kSCFG:
- // nested messages.
- if (!it->second.empty()) {
- scoped_ptr<CryptoHandshakeMessage> msg(
- CryptoFramer::ParseMessage(it->second));
- if (msg.get()) {
- ret += "\n";
- ret += msg->DebugStringInternal(indent + 1);
-
- done = true;
- }
- }
- break;
- case kPAD:
- ret += StringPrintf("(%d bytes of padding)",
- static_cast<int>(it->second.size()));
- done = true;
- break;
- }
-
- if (!done) {
- // If there's no specific format for this tag, or the value is invalid,
- // then just use hex.
- ret += "0x" + base::HexEncode(it->second.data(), it->second.size());
- }
- ret += "\n";
- }
- --indent;
- ret += string(2 * indent, ' ') + ">";
- return ret;
-}
-
QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters()
: key_exchange(0),
aead(0) {
diff --git a/chromium/net/quic/crypto/crypto_handshake.h b/chromium/net/quic/crypto/crypto_handshake.h
index 962e13336ef..fec53935326 100644
--- a/chromium/net/quic/crypto/crypto_handshake.h
+++ b/chromium/net/quic/crypto/crypto_handshake.h
@@ -5,143 +5,19 @@
#ifndef NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_
#define NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_
-#include <map>
#include <string>
#include <vector>
#include "base/memory/scoped_ptr.h"
-#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
-#include "net/cert/cert_verify_result.h"
-#include "net/cert/x509_certificate.h"
-#include "net/quic/crypto/crypto_protocol.h"
-#include "net/quic/crypto/proof_verifier.h"
#include "net/quic/quic_protocol.h"
namespace net {
-class ChannelIDSigner;
class CommonCertSets;
class KeyExchange;
-class ProofVerifier;
class QuicDecrypter;
class QuicEncrypter;
-class QuicRandom;
-
-// An intermediate format of a handshake message that's convenient for a
-// CryptoFramer to serialize from or parse into.
-class NET_EXPORT_PRIVATE CryptoHandshakeMessage {
- public:
- CryptoHandshakeMessage();
- CryptoHandshakeMessage(const CryptoHandshakeMessage& other);
- ~CryptoHandshakeMessage();
-
- CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other);
-
- // Clears state.
- void Clear();
-
- // GetSerialized returns the serialized form of this message and caches the
- // result. Subsequently altering the message does not invalidate the cache.
- const QuicData& GetSerialized() const;
-
- // MarkDirty invalidates the cache created by |GetSerialized|.
- void MarkDirty();
-
- // SetValue sets an element with the given tag to the raw, memory contents of
- // |v|.
- template<class T> void SetValue(QuicTag tag, const T& v) {
- tag_value_map_[tag] =
- std::string(reinterpret_cast<const char*>(&v), sizeof(v));
- }
-
- // SetVector sets an element with the given tag to the raw contents of an
- // array of elements in |v|.
- template<class T> void SetVector(QuicTag tag, const std::vector<T>& v) {
- if (v.empty()) {
- tag_value_map_[tag] = std::string();
- } else {
- tag_value_map_[tag] = std::string(reinterpret_cast<const char*>(&v[0]),
- v.size() * sizeof(T));
- }
- }
-
- // Returns the message tag.
- QuicTag tag() const { return tag_; }
- // Sets the message tag.
- void set_tag(QuicTag tag) { tag_ = tag; }
-
- const QuicTagValueMap& tag_value_map() const { return tag_value_map_; }
-
- // SetTaglist sets an element with the given tag to contain a list of tags,
- // passed as varargs. The argument list must be terminated with a 0 element.
- void SetTaglist(QuicTag tag, ...);
-
- void SetStringPiece(QuicTag tag, base::StringPiece value);
-
- // Erase removes a tag/value, if present, from the message.
- void Erase(QuicTag tag);
-
- // GetTaglist finds an element with the given tag containing zero or more
- // tags. If such a tag doesn't exist, it returns false. Otherwise it sets
- // |out_tags| and |out_len| to point to the array of tags and returns true.
- // The array points into the CryptoHandshakeMessage and is valid only for as
- // long as the CryptoHandshakeMessage exists and is not modified.
- QuicErrorCode GetTaglist(QuicTag tag, const QuicTag** out_tags,
- size_t* out_len) const;
-
- bool GetStringPiece(QuicTag tag, base::StringPiece* out) const;
-
- // GetNthValue24 interprets the value with the given tag to be a series of
- // 24-bit, length prefixed values and it returns the subvalue with the given
- // index.
- QuicErrorCode GetNthValue24(QuicTag tag,
- unsigned index,
- base::StringPiece* out) const;
- QuicErrorCode GetUint16(QuicTag tag, uint16* out) const;
- QuicErrorCode GetUint32(QuicTag tag, uint32* out) const;
- QuicErrorCode GetUint64(QuicTag tag, uint64* out) const;
-
- // size returns 4 (message tag) + 2 (uint16, number of entries) +
- // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes.
- size_t size() const;
-
- // set_minimum_size sets the minimum number of bytes that the message should
- // consume. The CryptoFramer will add a PAD tag as needed when serializing in
- // order to ensure this. Setting a value of 0 disables padding.
- //
- // Padding is useful in order to ensure that messages are a minimum size. A
- // QUIC server can require a minimum size in order to reduce the
- // amplification factor of any mirror DoS attack.
- void set_minimum_size(size_t min_bytes);
-
- size_t minimum_size() const;
-
- // DebugString returns a multi-line, string representation of the message
- // suitable for including in debug output.
- std::string DebugString() const;
-
- private:
- // GetPOD is a utility function for extracting a plain-old-data value. If
- // |tag| exists in the message, and has a value of exactly |len| bytes then
- // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out|
- // are zeroed out.
- //
- // If used to copy integers then this assumes that the machine is
- // little-endian.
- QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const;
-
- std::string DebugStringInternal(size_t indent) const;
-
- QuicTag tag_;
- QuicTagValueMap tag_value_map_;
-
- size_t minimum_size_;
-
- // The serialized form of the handshake message. This member is constructed
- // lasily.
- mutable scoped_ptr<QuicData> serialized_;
-};
// A CrypterPair contains the encrypter and decrypter for an encryption level.
struct NET_EXPORT_PRIVATE CrypterPair {
@@ -167,10 +43,10 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters {
std::string sni;
std::string client_nonce;
std::string server_nonce;
- // hkdf_input_suffix contains the HKDF input following the label: the GUID,
- // client hello and server config. This is only populated in the client
- // because only the client needs to derive the forward secure keys at a later
- // time from the initial keys.
+ // hkdf_input_suffix contains the HKDF input following the label: the
+ // ConnectionId, client hello and server config. This is only populated in the
+ // client because only the client needs to derive the forward secure keys at a
+ // later time from the initial keys.
std::string hkdf_input_suffix;
// cached_certs contains the cached certificates that a client used when
// sending a client hello.
diff --git a/chromium/net/quic/crypto/crypto_handshake_message.cc b/chromium/net/quic/crypto/crypto_handshake_message.cc
new file mode 100644
index 00000000000..77f7c525cf3
--- /dev/null
+++ b/chromium/net/quic/crypto/crypto_handshake_message.cc
@@ -0,0 +1,324 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_handshake_message.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_socket_address_coder.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using base::StringPrintf;
+using std::string;
+using std::vector;
+
+namespace net {
+
+CryptoHandshakeMessage::CryptoHandshakeMessage()
+ : tag_(0),
+ minimum_size_(0) {}
+
+CryptoHandshakeMessage::CryptoHandshakeMessage(
+ const CryptoHandshakeMessage& other)
+ : tag_(other.tag_),
+ tag_value_map_(other.tag_value_map_),
+ minimum_size_(other.minimum_size_) {
+ // Don't copy serialized_. scoped_ptr doesn't have a copy constructor.
+ // The new object can lazily reconstruct serialized_.
+}
+
+CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
+
+CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
+ const CryptoHandshakeMessage& other) {
+ tag_ = other.tag_;
+ tag_value_map_ = other.tag_value_map_;
+ // Don't copy serialized_. scoped_ptr doesn't have an assignment operator.
+ // However, invalidate serialized_.
+ serialized_.reset();
+ minimum_size_ = other.minimum_size_;
+ return *this;
+}
+
+void CryptoHandshakeMessage::Clear() {
+ tag_ = 0;
+ tag_value_map_.clear();
+ minimum_size_ = 0;
+ serialized_.reset();
+}
+
+const QuicData& CryptoHandshakeMessage::GetSerialized() const {
+ if (!serialized_.get()) {
+ serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this));
+ }
+ return *serialized_.get();
+}
+
+void CryptoHandshakeMessage::MarkDirty() {
+ serialized_.reset();
+}
+
+void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) {
+ // Warning, if sizeof(QuicTag) > sizeof(int) then this function will break
+ // because the terminating 0 will only be promoted to int.
+ COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int),
+ crypto_tag_may_not_be_larger_than_int_or_varargs_will_break);
+
+ vector<QuicTag> tags;
+ va_list ap;
+
+ va_start(ap, tag);
+ for (;;) {
+ QuicTag list_item = va_arg(ap, QuicTag);
+ if (list_item == 0) {
+ break;
+ }
+ tags.push_back(list_item);
+ }
+
+ // Because of the way that we keep tags in memory, we can copy the contents
+ // of the vector and get the correct bytes in wire format. See
+ // crypto_protocol.h. This assumes that the system is little-endian.
+ SetVector(tag, tags);
+
+ va_end(ap);
+}
+
+void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) {
+ tag_value_map_[tag] = value.as_string();
+}
+
+void CryptoHandshakeMessage::Erase(QuicTag tag) {
+ tag_value_map_.erase(tag);
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag,
+ const QuicTag** out_tags,
+ size_t* out_len) const {
+ QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
+ QuicErrorCode ret = QUIC_NO_ERROR;
+
+ if (it == tag_value_map_.end()) {
+ ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ } else if (it->second.size() % sizeof(QuicTag) != 0) {
+ ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (ret != QUIC_NO_ERROR) {
+ *out_tags = NULL;
+ *out_len = 0;
+ return ret;
+ }
+
+ *out_tags = reinterpret_cast<const QuicTag*>(it->second.data());
+ *out_len = it->second.size() / sizeof(QuicTag);
+ return ret;
+}
+
+bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag,
+ StringPiece* out) const {
+ QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
+ if (it == tag_value_map_.end()) {
+ return false;
+ }
+ *out = it->second;
+ return true;
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag tag,
+ unsigned index,
+ StringPiece* out) const {
+ StringPiece value;
+ if (!GetStringPiece(tag, &value)) {
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ for (unsigned i = 0;; i++) {
+ if (value.empty()) {
+ return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND;
+ }
+ if (value.size() < 3) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ const unsigned char* data =
+ reinterpret_cast<const unsigned char*>(value.data());
+ size_t size = static_cast<size_t>(data[0]) |
+ (static_cast<size_t>(data[1]) << 8) |
+ (static_cast<size_t>(data[2]) << 16);
+ value.remove_prefix(3);
+
+ if (value.size() < size) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (i == index) {
+ *out = StringPiece(value.data(), size);
+ return QUIC_NO_ERROR;
+ }
+
+ value.remove_prefix(size);
+ }
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag,
+ uint16* out) const {
+ return GetPOD(tag, out, sizeof(uint16));
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag,
+ uint32* out) const {
+ return GetPOD(tag, out, sizeof(uint32));
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag,
+ uint64* out) const {
+ return GetPOD(tag, out, sizeof(uint64));
+}
+
+size_t CryptoHandshakeMessage::size() const {
+ size_t ret = sizeof(QuicTag) +
+ sizeof(uint16) /* number of entries */ +
+ sizeof(uint16) /* padding */;
+ ret += (sizeof(QuicTag) + sizeof(uint32) /* end offset */) *
+ tag_value_map_.size();
+ for (QuicTagValueMap::const_iterator i = tag_value_map_.begin();
+ i != tag_value_map_.end(); ++i) {
+ ret += i->second.size();
+ }
+
+ return ret;
+}
+
+void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) {
+ if (min_bytes == minimum_size_) {
+ return;
+ }
+ serialized_.reset();
+ minimum_size_ = min_bytes;
+}
+
+size_t CryptoHandshakeMessage::minimum_size() const {
+ return minimum_size_;
+}
+
+string CryptoHandshakeMessage::DebugString() const {
+ return DebugStringInternal(0);
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetPOD(
+ QuicTag tag, void* out, size_t len) const {
+ QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
+ QuicErrorCode ret = QUIC_NO_ERROR;
+
+ if (it == tag_value_map_.end()) {
+ ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ } else if (it->second.size() != len) {
+ ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (ret != QUIC_NO_ERROR) {
+ memset(out, 0, len);
+ return ret;
+ }
+
+ memcpy(out, it->second.data(), len);
+ return ret;
+}
+
+string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
+ string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n";
+ ++indent;
+ for (QuicTagValueMap::const_iterator it = tag_value_map_.begin();
+ it != tag_value_map_.end(); ++it) {
+ ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": ";
+
+ bool done = false;
+ switch (it->first) {
+ case kICSL:
+ case kIFCW:
+ case kCFCW:
+ case kSFCW:
+ case kIRTT:
+ case kKATO:
+ case kMSPC:
+ case kSWND:
+ // uint32 value
+ if (it->second.size() == 4) {
+ uint32 value;
+ memcpy(&value, it->second.data(), sizeof(value));
+ ret += base::UintToString(value);
+ done = true;
+ }
+ break;
+ case kKEXS:
+ case kAEAD:
+ case kCGST:
+ case kCOPT:
+ case kLOSS:
+ case kPDMD:
+ case kVER:
+ // tag lists
+ if (it->second.size() % sizeof(QuicTag) == 0) {
+ for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) {
+ QuicTag tag;
+ memcpy(&tag, it->second.data() + j, sizeof(tag));
+ if (j > 0) {
+ ret += ",";
+ }
+ ret += "'" + QuicUtils::TagToString(tag) + "'";
+ }
+ done = true;
+ }
+ break;
+ case kCADR:
+ // IP address and port
+ if (!it->second.empty()) {
+ QuicSocketAddressCoder decoder;
+ if (decoder.Decode(it->second.data(), it->second.size())) {
+ ret += IPAddressToStringWithPort(decoder.ip(), decoder.port());
+ done = true;
+ }
+ }
+ break;
+ case kSCFG:
+ // nested messages.
+ if (!it->second.empty()) {
+ scoped_ptr<CryptoHandshakeMessage> msg(
+ CryptoFramer::ParseMessage(it->second));
+ if (msg.get()) {
+ ret += "\n";
+ ret += msg->DebugStringInternal(indent + 1);
+
+ done = true;
+ }
+ }
+ break;
+ case kPAD:
+ ret += StringPrintf("(%d bytes of padding)",
+ static_cast<int>(it->second.size()));
+ done = true;
+ break;
+ case kUAID:
+ ret += it->second;
+ done = true;
+ break;
+ }
+
+ if (!done) {
+ // If there's no specific format for this tag, or the value is invalid,
+ // then just use hex.
+ ret += "0x" + base::HexEncode(it->second.data(), it->second.size());
+ }
+ ret += "\n";
+ }
+ --indent;
+ ret += string(2 * indent, ' ') + ">";
+ return ret;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/crypto_handshake_message.h b/chromium/net/quic/crypto/crypto_handshake_message.h
new file mode 100644
index 00000000000..fcb39307b1c
--- /dev/null
+++ b/chromium/net/quic/crypto/crypto_handshake_message.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
+#define NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+// An intermediate format of a handshake message that's convenient for a
+// CryptoFramer to serialize from or parse into.
+class NET_EXPORT_PRIVATE CryptoHandshakeMessage {
+ public:
+ CryptoHandshakeMessage();
+ CryptoHandshakeMessage(const CryptoHandshakeMessage& other);
+ ~CryptoHandshakeMessage();
+
+ CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other);
+
+ // Clears state.
+ void Clear();
+
+ // GetSerialized returns the serialized form of this message and caches the
+ // result. Subsequently altering the message does not invalidate the cache.
+ const QuicData& GetSerialized() const;
+
+ // MarkDirty invalidates the cache created by |GetSerialized|.
+ void MarkDirty();
+
+ // SetValue sets an element with the given tag to the raw, memory contents of
+ // |v|.
+ template<class T> void SetValue(QuicTag tag, const T& v) {
+ tag_value_map_[tag] =
+ std::string(reinterpret_cast<const char*>(&v), sizeof(v));
+ }
+
+ // SetVector sets an element with the given tag to the raw contents of an
+ // array of elements in |v|.
+ template<class T> void SetVector(QuicTag tag, const std::vector<T>& v) {
+ if (v.empty()) {
+ tag_value_map_[tag] = std::string();
+ } else {
+ tag_value_map_[tag] = std::string(reinterpret_cast<const char*>(&v[0]),
+ v.size() * sizeof(T));
+ }
+ }
+
+ // Returns the message tag.
+ QuicTag tag() const { return tag_; }
+ // Sets the message tag.
+ void set_tag(QuicTag tag) { tag_ = tag; }
+
+ const QuicTagValueMap& tag_value_map() const { return tag_value_map_; }
+
+ // SetTaglist sets an element with the given tag to contain a list of tags,
+ // passed as varargs. The argument list must be terminated with a 0 element.
+ void SetTaglist(QuicTag tag, ...);
+
+ void SetStringPiece(QuicTag tag, base::StringPiece value);
+
+ // Erase removes a tag/value, if present, from the message.
+ void Erase(QuicTag tag);
+
+ // GetTaglist finds an element with the given tag containing zero or more
+ // tags. If such a tag doesn't exist, it returns false. Otherwise it sets
+ // |out_tags| and |out_len| to point to the array of tags and returns true.
+ // The array points into the CryptoHandshakeMessage and is valid only for as
+ // long as the CryptoHandshakeMessage exists and is not modified.
+ QuicErrorCode GetTaglist(QuicTag tag, const QuicTag** out_tags,
+ size_t* out_len) const;
+
+ bool GetStringPiece(QuicTag tag, base::StringPiece* out) const;
+
+ // GetNthValue24 interprets the value with the given tag to be a series of
+ // 24-bit, length prefixed values and it returns the subvalue with the given
+ // index.
+ QuicErrorCode GetNthValue24(QuicTag tag,
+ unsigned index,
+ base::StringPiece* out) const;
+ QuicErrorCode GetUint16(QuicTag tag, uint16* out) const;
+ QuicErrorCode GetUint32(QuicTag tag, uint32* out) const;
+ QuicErrorCode GetUint64(QuicTag tag, uint64* out) const;
+
+ // size returns 4 (message tag) + 2 (uint16, number of entries) +
+ // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes.
+ size_t size() const;
+
+ // set_minimum_size sets the minimum number of bytes that the message should
+ // consume. The CryptoFramer will add a PAD tag as needed when serializing in
+ // order to ensure this. Setting a value of 0 disables padding.
+ //
+ // Padding is useful in order to ensure that messages are a minimum size. A
+ // QUIC server can require a minimum size in order to reduce the
+ // amplification factor of any mirror DoS attack.
+ void set_minimum_size(size_t min_bytes);
+
+ size_t minimum_size() const;
+
+ // DebugString returns a multi-line, string representation of the message
+ // suitable for including in debug output.
+ std::string DebugString() const;
+
+ private:
+ // GetPOD is a utility function for extracting a plain-old-data value. If
+ // |tag| exists in the message, and has a value of exactly |len| bytes then
+ // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out|
+ // are zeroed out.
+ //
+ // If used to copy integers then this assumes that the machine is
+ // little-endian.
+ QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const;
+
+ std::string DebugStringInternal(size_t indent) const;
+
+ QuicTag tag_;
+ QuicTagValueMap tag_value_map_;
+
+ size_t minimum_size_;
+
+ // The serialized form of the handshake message. This member is constructed
+ // lasily.
+ mutable scoped_ptr<QuicData> serialized_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
diff --git a/chromium/net/quic/crypto/crypto_protocol.h b/chromium/net/quic/crypto/crypto_protocol.h
index 1971b58c543..f2e7a16399f 100644
--- a/chromium/net/quic/crypto/crypto_protocol.h
+++ b/chromium/net/quic/crypto/crypto_protocol.h
@@ -5,9 +5,7 @@
#ifndef NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_
#define NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_
-#include <map>
#include <string>
-#include <vector>
#include "net/base/net_export.h"
#include "net/quic/quic_protocol.h"
@@ -26,71 +24,98 @@
namespace net {
typedef std::string ServerConfigID;
-typedef std::map<QuicTag, std::string> QuicTagValueMap;
-const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello
-const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello
-const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config
-const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject
-const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value
- // pairs
+const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello
+const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello
+const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config
+const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject
+const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value
+ // pairs
+const QuicTag kPRST = TAG('P', 'R', 'S', 'T'); // Public reset
// Key exchange methods
-const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256
-const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519
+const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256
+const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519
// AEAD algorithms
-const QuicTag kNULL = TAG('N', 'U', 'L', 'N'); // null algorithm
-const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12
+const QuicTag kNULL = TAG('N', 'U', 'L', 'N'); // null algorithm
+const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12
+const QuicTag kCC12 = TAG('C', 'C', '1', '2'); // ChaCha20 + Poly1305
// Congestion control feedback types
-const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic
-const QuicTag kPACE = TAG('P', 'A', 'C', 'E'); // Paced TCP cubic
-const QuicTag kINAR = TAG('I', 'N', 'A', 'R'); // Inter arrival
+const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic
+const QuicTag kPACE = TAG('P', 'A', 'C', 'E'); // Paced TCP cubic
+const QuicTag kINAR = TAG('I', 'N', 'A', 'R'); // Inter arrival
+
+// Congestion control options
+const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP
+
+// Loss detection algorithm types
+const QuicTag kNACK = TAG('N', 'A', 'C', 'K'); // TCP style nack counting
+const QuicTag kTIME = TAG('T', 'I', 'M', 'E'); // Time based
// Proof types (i.e. certificate types)
// NOTE: although it would be silly to do so, specifying both kX509 and kX59R
// is allowed and is equivalent to specifying only kX509.
-const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate, all key
- // types
-const QuicTag kX59R = TAG('X', '5', '9', 'R'); // X.509 certificate, RSA keys
- // only
-const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID.
+const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate, all key
+ // types
+const QuicTag kX59R = TAG('X', '5', '9', 'R'); // X.509 certificate, RSA keys
+ // only
+const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID.
// Client hello tags
-// TODO(rch): Remove once we remove QUIC_VERSION_12.
-const QuicTag kVERS = TAG('V', 'E', 'R', 'S'); // Version (obsolete)
-const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version (new)
-const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce
-const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods
-const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated
- // encryption algorithms
-const QuicTag kCGST = TAG('C', 'G', 'S', 'T'); // Congestion control
- // feedback types
-const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state
- // lifetime
-const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout
-const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection.
-const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us.
-const QuicTag kSWND = TAG('S', 'W', 'N', 'D'); // Server's Initial congestion
- // window.
-const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name
- // indication
-const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values
-const QuicTag kSCID = TAG('S', 'C', 'I', 'D'); // Server config id
-const QuicTag kORBT = TAG('O', 'B', 'I', 'T'); // Server orbit.
-const QuicTag kPDMD = TAG('P', 'D', 'M', 'D'); // Proof demand.
-const QuicTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature).
-const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set
-const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate
-const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry
+const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version (new)
+const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce
+const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods
+const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated
+ // encryption algorithms
+const QuicTag kCGST = TAG('C', 'G', 'S', 'T'); // Congestion control
+ // feedback types
+const QuicTag kCOPT = TAG('C', 'O', 'P', 'T'); // Congestion control options
+// kLOSS was 'L', 'O', 'S', 'S', but was changed from a tag vector to a tag.
+const QuicTag kLOSS = TAG('L', 'O', 'S', 'A'); // Loss detection algorithms
+const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state
+ // lifetime
+const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout
+const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection.
+const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us.
+const QuicTag kSWND = TAG('S', 'W', 'N', 'D'); // Server's Initial congestion
+ // window.
+const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name
+ // indication
+const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values
+const QuicTag kSCID = TAG('S', 'C', 'I', 'D'); // Server config id
+const QuicTag kORBT = TAG('O', 'B', 'I', 'T'); // Server orbit.
+const QuicTag kPDMD = TAG('P', 'D', 'M', 'D'); // Proof demand.
+const QuicTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature).
+const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set
+const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate
+const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry
+// TODO(rjshade): Remove kIFCW when removing QUIC_VERSION_19.
+const QuicTag kIFCW = TAG('I', 'F', 'C', 'W'); // Initial flow control receive
+ // window.
+const QuicTag kSFCW = TAG('S', 'F', 'C', 'W'); // Initial stream flow control
+ // receive window.
+const QuicTag kCFCW = TAG('C', 'F', 'C', 'W'); // Initial session/connection
+ // flow control receive window.
+const QuicTag kUAID = TAG('U', 'A', 'I', 'D'); // Client's User Agent ID.
+
+// Server hello tags
+const QuicTag kCADR = TAG('C', 'A', 'D', 'R'); // Client IP address and port
// CETV tags
-const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key
-const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature
+const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key
+const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature
+
+// Public reset tags
+const QuicTag kRNON = TAG('R', 'N', 'O', 'N'); // Public reset nonce proof
+const QuicTag kRSEQ = TAG('R', 'S', 'E', 'Q'); // Rejected sequence number
// Universal tags
-const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding
+const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding
+
+// Reasons for server sending rejection message tag.
+const QuicTag kRREJ = TAG('R', 'R', 'E', 'J');
// These tags have a special form so that they appear either at the beginning
// or the end of a handshake message. Since handshake messages are sorted by
@@ -135,12 +160,6 @@ const char kProofSignatureLabel[] = "QUIC server config signature";
// rejection message.
const size_t kClientHelloMinimumSize = 1024;
-// kClientHelloMinimumSizeOld is the previous value of kClientHelloMinimumSize.
-// To support old clients, the server only enforces this size.
-// TODO(wtc): Replace it with kClientHelloMinimumSize when we drop support for
-// QUIC_VERSION_12 clients.
-const size_t kClientHelloMinimumSizeOld = 512;
-
} // namespace net
#endif // NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_
diff --git a/chromium/net/quic/crypto/crypto_secret_boxer.cc b/chromium/net/quic/crypto/crypto_secret_boxer.cc
index 73562c638bc..5d8a11b27a3 100644
--- a/chromium/net/quic/crypto/crypto_secret_boxer.cc
+++ b/chromium/net/quic/crypto/crypto_secret_boxer.cc
@@ -6,8 +6,9 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "crypto/secure_hash.h"
-#include "crypto/sha2.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/crypto/quic_random.h"
using base::StringPiece;
@@ -19,21 +20,36 @@ namespace net {
static const size_t kKeySize = 16;
// kBoxNonceSize contains the number of bytes of nonce that we use in each box.
-static const size_t kBoxNonceSize = 16;
+// TODO(rtenneti): Add support for kBoxNonceSize to be 16 bytes.
+//
+// From agl@:
+// 96-bit nonces are on the edge. An attacker who can collect 2^41
+// source-address tokens has a 1% chance of finding a duplicate.
+//
+// The "average" DDoS is now 32.4M PPS. That's 2^25 source-address tokens
+// per second. So one day of that DDoS botnot would reach the 1% mark.
+//
+// It's not terrible, but it's not a "forget about it" margin.
+static const size_t kBoxNonceSize = 12;
// static
size_t CryptoSecretBoxer::GetKeySize() { return kKeySize; }
void CryptoSecretBoxer::SetKey(StringPiece key) {
- DCHECK_EQ(static_cast<size_t>(kKeySize), key.size());
+ DCHECK_EQ(kKeySize, key.size());
key_ = key.as_string();
}
-// TODO(rtenneti): Delete sha256 based code. Use Aes128Gcm12Encrypter to Box the
-// plaintext. This is temporary solution for tests to pass.
string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const {
+ scoped_ptr<QuicEncrypter> encrypter(QuicEncrypter::Create(kAESG));
+ if (!encrypter->SetKey(key_)) {
+ DLOG(DFATAL) << "CryptoSecretBoxer's encrypter->SetKey failed.";
+ return string();
+ }
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+
string ret;
- const size_t len = kBoxNonceSize + plaintext.size() + crypto::kSHA256Length;
+ const size_t len = kBoxNonceSize + ciphertext_size;
ret.resize(len);
char* data = &ret[0];
@@ -41,49 +57,43 @@ string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const {
rand->RandBytes(data, kBoxNonceSize);
memcpy(data + kBoxNonceSize, plaintext.data(), plaintext.size());
- // Compute sha256 for nonce + plaintext.
- scoped_ptr<crypto::SecureHash> sha256(crypto::SecureHash::Create(
- crypto::SecureHash::SHA256));
- sha256->Update(data, kBoxNonceSize + plaintext.size());
- sha256->Finish(data + kBoxNonceSize + plaintext.size(),
- crypto::kSHA256Length);
+ if (!encrypter->Encrypt(StringPiece(data, kBoxNonceSize), StringPiece(),
+ plaintext, reinterpret_cast<unsigned char*>(
+ data + kBoxNonceSize))) {
+ DLOG(DFATAL) << "CryptoSecretBoxer's Encrypt failed.";
+ return string();
+ }
return ret;
}
-// TODO(rtenneti): Delete sha256 based code. Use Aes128Gcm12Decrypter to Unbox
-// the plaintext. This is temporary solution for tests to pass.
bool CryptoSecretBoxer::Unbox(StringPiece ciphertext,
string* out_storage,
StringPiece* out) const {
- if (ciphertext.size() < kBoxNonceSize + crypto::kSHA256Length) {
+ if (ciphertext.size() < kBoxNonceSize) {
return false;
}
- const size_t plaintext_len =
- ciphertext.size() - kBoxNonceSize - crypto::kSHA256Length;
- out_storage->resize(plaintext_len);
+ char nonce[kBoxNonceSize];
+ memcpy(nonce, ciphertext.data(), kBoxNonceSize);
+ ciphertext.remove_prefix(kBoxNonceSize);
+
+ size_t len = ciphertext.size();
+ out_storage->resize(len);
char* data = const_cast<char*>(out_storage->data());
- // Copy plaintext from ciphertext.
- if (plaintext_len != 0) {
- memcpy(data, ciphertext.data() + kBoxNonceSize, plaintext_len);
+ scoped_ptr<QuicDecrypter> decrypter(QuicDecrypter::Create(kAESG));
+ if (!decrypter->SetKey(key_)) {
+ DLOG(DFATAL) << "CryptoSecretBoxer's decrypter->SetKey failed.";
+ return false;
}
-
- // Compute sha256 for nonce + plaintext.
- scoped_ptr<crypto::SecureHash> sha256(crypto::SecureHash::Create(
- crypto::SecureHash::SHA256));
- sha256->Update(ciphertext.data(), ciphertext.size() - crypto::kSHA256Length);
- char sha256_bytes[crypto::kSHA256Length];
- sha256->Finish(sha256_bytes, sizeof(sha256_bytes));
-
- // Verify sha256.
- if (0 != memcmp(ciphertext.data() + ciphertext.size() - crypto::kSHA256Length,
- sha256_bytes, crypto::kSHA256Length)) {
+ if (!decrypter->Decrypt(StringPiece(nonce, kBoxNonceSize), StringPiece(),
+ ciphertext, reinterpret_cast<unsigned char*>(data),
+ &len)) {
return false;
}
- out->set(data, plaintext_len);
+ out->set(data, len);
return true;
}
diff --git a/chromium/net/quic/crypto/crypto_secret_boxer.h b/chromium/net/quic/crypto/crypto_secret_boxer.h
index ba9baf2bb2a..38b8fb339ec 100644
--- a/chromium/net/quic/crypto/crypto_secret_boxer.h
+++ b/chromium/net/quic/crypto/crypto_secret_boxer.h
@@ -19,6 +19,8 @@ class QuicRandom;
// thread-safe.
class NET_EXPORT_PRIVATE CryptoSecretBoxer {
public:
+ CryptoSecretBoxer() {}
+
// GetKeySize returns the number of bytes in a key.
static size_t GetKeySize();
@@ -42,6 +44,8 @@ class NET_EXPORT_PRIVATE CryptoSecretBoxer {
private:
std::string key_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptoSecretBoxer);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/crypto_server_config_protobuf.cc b/chromium/net/quic/crypto/crypto_server_config_protobuf.cc
index a3418e4228a..d292f9e9db9 100644
--- a/chromium/net/quic/crypto/crypto_server_config_protobuf.cc
+++ b/chromium/net/quic/crypto/crypto_server_config_protobuf.cc
@@ -10,7 +10,8 @@
namespace net {
QuicServerConfigProtobuf::QuicServerConfigProtobuf()
- : primary_time_(QuicWallTime::Zero().ToUNIXSeconds()) {
+ : primary_time_(QuicWallTime::Zero().ToUNIXSeconds()),
+ priority_(0) {
}
QuicServerConfigProtobuf::~QuicServerConfigProtobuf() {
diff --git a/chromium/net/quic/crypto/crypto_server_config_protobuf.h b/chromium/net/quic/crypto/crypto_server_config_protobuf.h
index 6340ae023c4..57ebfb04847 100644
--- a/chromium/net/quic/crypto/crypto_server_config_protobuf.h
+++ b/chromium/net/quic/crypto/crypto_server_config_protobuf.h
@@ -61,7 +61,7 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf {
}
void set_config(base::StringPiece config) {
- config_ = config.as_string();
+ config.CopyToString(&config_);
}
QuicServerConfigProtobuf::PrivateKey* add_key() {
@@ -85,6 +85,32 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf {
primary_time_ = primary_time;
}
+ bool has_priority() const {
+ return priority_ > 0;
+ }
+
+ uint64 priority() const {
+ return priority_;
+ }
+
+ void set_priority(int64 priority) {
+ priority_ = priority;
+ }
+
+ bool has_source_address_token_secret_override() const {
+ return !source_address_token_secret_override_.empty();
+ }
+
+ std::string source_address_token_secret_override() const {
+ return source_address_token_secret_override_;
+ }
+
+ void set_source_address_token_secret_override(
+ base::StringPiece source_address_token_secret_override) {
+ source_address_token_secret_override.CopyToString(
+ &source_address_token_secret_override_);
+ }
+
private:
std::vector<PrivateKey*> keys_;
@@ -94,6 +120,18 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf {
// primary_time_ contains a UNIX epoch seconds value that indicates when this
// config should become primary.
int64 primary_time_;
+
+ // Relative priority of this config vs other configs with the same
+ // primary time. For use as a secondary sort key when selecting the
+ // primary config.
+ uint64 priority_;
+
+ // Optional override to the secret used to box/unbox source address
+ // tokens when talking to clients that select this server config.
+ // It can be of any length as it is fed into a KDF before use.
+ std::string source_address_token_secret_override_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerConfigProtobuf);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/crypto_server_test.cc b/chromium/net/quic/crypto/crypto_server_test.cc
index 4eec2a86a28..7cb7e990fa2 100644
--- a/chromium/net/quic/crypto/crypto_server_test.cc
+++ b/chromium/net/quic/crypto/crypto_server_test.cc
@@ -2,33 +2,94 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <ostream>
+#include <vector>
+
+#include "base/basictypes.h"
#include "base/strings/string_number_conversions.h"
#include "crypto/secure_hash.h"
#include "net/quic/crypto/crypto_utils.h"
#include "net/quic/crypto/quic_crypto_server_config.h"
#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_socket_address_coder.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/delayed_verify_strike_register_client.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece;
+using std::ostream;
using std::string;
+using std::vector;
namespace net {
namespace test {
-class CryptoServerTest : public ::testing::Test {
+class QuicCryptoServerConfigPeer {
+ public:
+ explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config)
+ : server_config_(server_config) {}
+
+ base::Lock* GetStrikeRegisterClientLock() {
+ return &server_config_->strike_register_client_lock_;
+ }
+
+ private:
+ QuicCryptoServerConfig* server_config_;
+};
+
+// Run tests with combinations of
+// {FLAGS_use_early_return_when_verifying_chlo,
+// FLAGS_send_quic_crypto_reject_reason}.
+struct TestParams {
+ TestParams(bool use_early_return_when_verifying_chlo,
+ bool send_quic_crypto_reject_reason)
+ : use_early_return_when_verifying_chlo(
+ use_early_return_when_verifying_chlo),
+ send_quic_crypto_reject_reason(send_quic_crypto_reject_reason) {
+ }
+
+ friend ostream& operator<<(ostream& os, const TestParams& p) {
+ os << "{ use_early_return_when_verifying_chlo: "
+ << p.use_early_return_when_verifying_chlo
+ << " send_quic_crypto_reject_reason: "
+ << p.send_quic_crypto_reject_reason << " }";
+ return os;
+ }
+
+ bool use_early_return_when_verifying_chlo;
+ bool send_quic_crypto_reject_reason;
+};
+
+// Constructs various test permutations.
+vector<TestParams> GetTestParams() {
+ vector<TestParams> params;
+ params.push_back(TestParams(false, false));
+ params.push_back(TestParams(false, true));
+ params.push_back(TestParams(true, false));
+ params.push_back(TestParams(true, true));
+ return params;
+}
+
+class CryptoServerTest : public ::testing::TestWithParam<TestParams> {
public:
CryptoServerTest()
: rand_(QuicRandom::GetInstance()),
- config_(QuicCryptoServerConfig::TESTING, rand_),
- addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ?
- ip_ : IPAddressNumber(), 1) {
+ client_address_(Loopback4(), 1234),
+ config_(QuicCryptoServerConfig::TESTING, rand_) {
config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting());
supported_versions_ = QuicSupportedVersions();
+ client_version_ = QuicUtils::TagToString(
+ QuicVersionToQuicTag(supported_versions_.front()));
+
+ FLAGS_use_early_return_when_verifying_chlo =
+ GetParam().use_early_return_when_verifying_chlo;
+ FLAGS_send_quic_crypto_reject_reason =
+ GetParam().send_quic_crypto_reject_reason;
}
virtual void SetUp() {
@@ -54,12 +115,17 @@ class CryptoServerTest : public ::testing::Test {
"KEXS", "C255",
"PUBS", pub_hex_.c_str(),
"NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
NULL);
ShouldSucceed(client_hello);
// The message should be rejected because the source-address token is
// missing.
ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
StringPiece srct;
ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct));
@@ -91,6 +157,15 @@ class CryptoServerTest : public ::testing::Test {
virtual void RunImpl(const CryptoHandshakeMessage& client_hello,
const Result& result) OVERRIDE {
+ {
+ // Ensure that the strike register client lock is not held.
+ QuicCryptoServerConfigPeer peer(&test_->config_);
+ base::Lock* m = peer.GetStrikeRegisterClientLock();
+ // In Chromium, we will dead lock if the lock is held by the current
+ // thread. Chromium doesn't have AssertNotHeld API call.
+ // m->AssertNotHeld();
+ base::AutoLock lock(*m);
+ }
ASSERT_FALSE(*called_);
test_->ProcessValidationResult(
client_hello, result, should_succeed_, error_substr_);
@@ -104,17 +179,33 @@ class CryptoServerTest : public ::testing::Test {
bool* called_;
};
+ void CheckServerHello(const CryptoHandshakeMessage& server_hello) {
+ const QuicTag* versions;
+ size_t num_versions;
+ server_hello.GetTaglist(kVER, &versions, &num_versions);
+ ASSERT_EQ(QuicSupportedVersions().size(), num_versions);
+ for (size_t i = 0; i < num_versions; ++i) {
+ EXPECT_EQ(QuicVersionToQuicTag(QuicSupportedVersions()[i]), versions[i]);
+ }
+
+ StringPiece address;
+ ASSERT_TRUE(server_hello.GetStringPiece(kCADR, &address));
+ QuicSocketAddressCoder decoder;
+ ASSERT_TRUE(decoder.Decode(address.data(), address.size()));
+ EXPECT_EQ(client_address_.address(), decoder.ip());
+ EXPECT_EQ(client_address_.port(), decoder.port());
+ }
+
void ShouldSucceed(const CryptoHandshakeMessage& message) {
bool called = false;
- ShouldSucceed(message, &called);
+ RunValidate(message, new ValidateCallback(this, true, "", &called));
EXPECT_TRUE(called);
}
- void ShouldSucceed(const CryptoHandshakeMessage& message,
- bool* called) {
- config_.ValidateClientHello(
- message, addr_, &clock_,
- new ValidateCallback(this, true, "", called));
+ void RunValidate(
+ const CryptoHandshakeMessage& message,
+ ValidateClientHelloResultCallback* cb) {
+ config_.ValidateClientHello(message, client_address_, &clock_, cb);
}
void ShouldFailMentioning(const char* error_substr,
@@ -128,7 +219,7 @@ class CryptoServerTest : public ::testing::Test {
const CryptoHandshakeMessage& message,
bool* called) {
config_.ValidateClientHello(
- message, addr_, &clock_,
+ message, client_address_, &clock_,
new ValidateCallback(this, false, error_substr, called));
}
@@ -138,7 +229,7 @@ class CryptoServerTest : public ::testing::Test {
const char* error_substr) {
string error_details;
QuicErrorCode error = config_.ProcessClientHello(
- result, 1 /* GUID */, addr_,
+ result, 1 /* ConnectionId */, client_address_,
supported_versions_.front(), supported_versions_, &clock_, rand_,
&params_, &out_, &error_details);
@@ -176,16 +267,39 @@ class CryptoServerTest : public ::testing::Test {
return nonce;
}
+ void CheckRejectReasons(
+ const HandshakeFailureReason* expected_handshake_failures,
+ size_t expected_count) {
+ const QuicTag* reject_reason_tags;
+ size_t num_reject_reasons;
+ QuicErrorCode error_code = out_.GetTaglist(kRREJ, &reject_reason_tags,
+ &num_reject_reasons);
+ if (!FLAGS_send_quic_crypto_reject_reason) {
+ ASSERT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error_code);
+ return;
+ }
+ ASSERT_EQ(QUIC_NO_ERROR, error_code);
+
+ if (FLAGS_use_early_return_when_verifying_chlo) {
+ EXPECT_EQ(1u, num_reject_reasons);
+ } else {
+ EXPECT_EQ(expected_count, num_reject_reasons);
+ }
+ for (size_t i = 0; i < num_reject_reasons; ++i) {
+ EXPECT_EQ(expected_handshake_failures[i], reject_reason_tags[i]);
+ }
+ }
+
protected:
QuicRandom* const rand_;
MockClock clock_;
+ const IPEndPoint client_address_;
QuicVersionVector supported_versions_;
+ string client_version_;
QuicCryptoServerConfig config_;
QuicCryptoServerConfig::ConfigOptions config_options_;
QuicCryptoNegotiatedParameters params_;
CryptoHandshakeMessage out_;
- IPAddressNumber ip_;
- IPEndPoint addr_;
uint8 orbit_[kOrbitSize];
// These strings contain hex escaped values from the server suitable for
@@ -194,7 +308,14 @@ class CryptoServerTest : public ::testing::Test {
scoped_ptr<CryptoHandshakeMessage> server_config_;
};
-TEST_F(CryptoServerTest, BadSNI) {
+// Run all CryptoServerTest with all combinations of
+// FLAGS_use_early_return_when_verifying_chlo and
+// FLAGS_send_quic_crypto_reject_reason.
+INSTANTIATE_TEST_CASE_P(CryptoServerTests,
+ CryptoServerTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(CryptoServerTest, BadSNI) {
static const char* kBadSNIs[] = {
"",
"foo",
@@ -204,11 +325,19 @@ TEST_F(CryptoServerTest, BadSNI) {
"ffee::1",
};
+ string client_version = QuicUtils::TagToString(
+ QuicVersionToQuicTag(supported_versions_.front()));
+
for (size_t i = 0; i < arraysize(kBadSNIs); i++) {
ShouldFailMentioning("SNI", InchoateClientHello(
"CHLO",
"SNI", kBadSNIs[i],
+ "VER\0", client_version.data(),
NULL));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
}
@@ -226,6 +355,7 @@ TEST_F(CryptoServerTest, DISABLED_DefaultCert) {
"NONC", nonce_hex_.c_str(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
"PDMD", "X509",
+ "VER\0", client_version_.data(),
NULL));
StringPiece cert, proof;
@@ -233,15 +363,24 @@ TEST_F(CryptoServerTest, DISABLED_DefaultCert) {
EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
EXPECT_NE(0u, cert.size());
EXPECT_NE(0u, proof.size());
+ const HandshakeFailureReason kRejectReasons[] = {
+ CLIENT_NONCE_UNKNOWN_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
-TEST_F(CryptoServerTest, TooSmall) {
+TEST_P(CryptoServerTest, TooSmall) {
ShouldFailMentioning("too small", CryptoTestUtils::Message(
"CHLO",
+ "VER\0", client_version_.data(),
NULL));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
-TEST_F(CryptoServerTest, BadSourceAddressToken) {
+TEST_P(CryptoServerTest, BadSourceAddressToken) {
// Invalid source-address tokens should be ignored.
static const char* kBadSourceAddressTokens[] = {
"",
@@ -254,11 +393,16 @@ TEST_F(CryptoServerTest, BadSourceAddressToken) {
ShouldSucceed(InchoateClientHello(
"CHLO",
"STK", kBadSourceAddressTokens[i],
+ "VER\0", client_version_.data(),
NULL));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
}
-TEST_F(CryptoServerTest, BadClientNonce) {
+TEST_P(CryptoServerTest, BadClientNonce) {
// Invalid nonces should be ignored.
static const char* kBadNonces[] = {
"",
@@ -270,27 +414,124 @@ TEST_F(CryptoServerTest, BadClientNonce) {
ShouldSucceed(InchoateClientHello(
"CHLO",
"NONC", kBadNonces[i],
+ "VER\0", client_version_.data(),
NULL));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
}
-TEST_F(CryptoServerTest, DowngradeAttack) {
+TEST_P(CryptoServerTest, DowngradeAttack) {
if (supported_versions_.size() == 1) {
// No downgrade attack is possible if the server only supports one version.
return;
}
// Set the client's preferred version to a supported version that
// is not the "current" version (supported_versions_.front()).
- string client_version = QuicUtils::TagToString(
+ string bad_version = QuicUtils::TagToString(
QuicVersionToQuicTag(supported_versions_.back()));
ShouldFailMentioning("Downgrade", InchoateClientHello(
"CHLO",
- "VER\0", client_version.data(),
+ "VER\0", bad_version.data(),
NULL));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptServerConfig) {
+ // This tests corrupted server config.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", (string(1, 'X') + scid_hex_).c_str(),
+ "#004b5453", srct_hex_.c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ NULL);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
-TEST_F(CryptoServerTest, ReplayProtection) {
+TEST_P(CryptoServerTest, CorruptSourceAddressToken) {
+ // This tests corrupted source address token.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", (string(1, 'X') + srct_hex_).c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ NULL);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptClientNonceAndSourceAddressToken) {
+ // This test corrupts client nonce and source address token.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", (string(1, 'X') + srct_hex_).c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", (string(1, 'X') + nonce_hex_).c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ NULL);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ CLIENT_NONCE_INVALID_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptMultipleTags) {
+ // This test corrupts client nonce, server nonce and source address token.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", (string(1, 'X') + srct_hex_).c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", (string(1, 'X') + nonce_hex_).c_str(),
+ "SNO\0", (string(1, 'X') + nonce_hex_).c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ NULL);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ CLIENT_NONCE_INVALID_FAILURE,
+ SERVER_NONCE_DECRYPTION_FAILURE,
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, ReplayProtection) {
// This tests that disabling replay protection works.
CryptoHandshakeMessage msg = CryptoTestUtils::Message(
"CHLO",
@@ -300,6 +541,7 @@ TEST_F(CryptoServerTest, ReplayProtection) {
"#004b5453", srct_hex_.c_str(),
"PUBS", pub_hex_.c_str(),
"NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
NULL);
ShouldSucceed(msg);
@@ -307,22 +549,22 @@ TEST_F(CryptoServerTest, ReplayProtection) {
// quiescent.
ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ CLIENT_NONCE_UNKNOWN_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+
config_.set_replay_protection(false);
ShouldSucceed(msg);
// The message should be accepted now.
ASSERT_EQ(kSHLO, out_.tag());
+ CheckServerHello(out_);
ShouldSucceed(msg);
// The message should accepted twice when replay protection is off.
ASSERT_EQ(kSHLO, out_.tag());
- const QuicTag* versions;
- size_t num_versions;
- out_.GetTaglist(kVER, &versions, &num_versions);
- ASSERT_EQ(QuicSupportedVersions().size(), num_versions);
- for (size_t i = 0; i < num_versions; ++i) {
- EXPECT_EQ(QuicVersionToQuicTag(QuicSupportedVersions()[i]), versions[i]);
- }
+ CheckServerHello(out_);
}
TEST(CryptoServerConfigGenerationTest, Determinism) {
@@ -392,7 +634,7 @@ TEST(CryptoServerConfigGenerationTest, SCIDIsHashOfServerConfig) {
hash->Finish(digest, sizeof(digest));
ASSERT_EQ(scid.size(), sizeof(digest));
- EXPECT_TRUE(0 == memcmp(digest, scid_str.data(), sizeof(digest)));
+ EXPECT_EQ(0, memcmp(digest, scid_str.data(), sizeof(digest)));
}
class CryptoServerTestNoConfig : public CryptoServerTest {
@@ -402,10 +644,16 @@ class CryptoServerTestNoConfig : public CryptoServerTest {
}
};
-TEST_F(CryptoServerTestNoConfig, DontCrash) {
- ShouldFailMentioning("No config", InchoateClientHello(
- "CHLO",
- NULL));
+TEST_P(CryptoServerTestNoConfig, DontCrash) {
+ ShouldFailMentioning("No config", InchoateClientHello(
+ "CHLO",
+ "VER\0", client_version_.data(),
+ NULL));
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ CLIENT_NONCE_UNKNOWN_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
}
class AsyncStrikeServerVerificationTest : public CryptoServerTest {
@@ -430,7 +678,7 @@ class AsyncStrikeServerVerificationTest : public CryptoServerTest {
DelayedVerifyStrikeRegisterClient* strike_register_client_;
};
-TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
+TEST_P(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
// This tests async validation with a strike register works.
CryptoHandshakeMessage msg = CryptoTestUtils::Message(
"CHLO",
@@ -440,6 +688,7 @@ TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
"#004b5453", srct_hex_.c_str(),
"PUBS", pub_hex_.c_str(),
"NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
NULL);
@@ -447,7 +696,7 @@ TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
out_.set_tag(0);
bool called = false;
- ShouldSucceed(msg, &called);
+ RunValidate(msg, new ValidateCallback(this, true, "", &called));
// The verification request was queued.
ASSERT_FALSE(called);
EXPECT_EQ(0u, out_.tag());
@@ -461,7 +710,7 @@ TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
EXPECT_EQ(kSHLO, out_.tag());
// Rejected if replayed.
- ShouldSucceed(msg, &called);
+ RunValidate(msg, new ValidateCallback(this, true, "", &called));
// The verification request was queued.
ASSERT_FALSE(called);
EXPECT_EQ(1, strike_register_client_->PendingVerifications());
diff --git a/chromium/net/quic/crypto/crypto_utils.cc b/chromium/net/quic/crypto/crypto_utils.cc
index eec8a9d05a2..29433960489 100644
--- a/chromium/net/quic/crypto/crypto_utils.cc
+++ b/chromium/net/quic/crypto/crypto_utils.cc
@@ -51,7 +51,7 @@ bool CryptoUtils::IsValidSNI(StringPiece sni) {
// based on the above spec, we may be losing some hostnames that windows
// would consider valid. By far the most common hostname character NOT
// accepted by the above spec is '_'.
- url_canon::CanonHostInfo host_info;
+ url::CanonHostInfo host_info;
string canonicalized_host(CanonicalizeHost(sni.as_string(), &host_info));
return !host_info.IsIPAddress() &&
IsCanonicalizedHostCompliant(canonicalized_host, std::string()) &&
@@ -60,7 +60,7 @@ bool CryptoUtils::IsValidSNI(StringPiece sni) {
// static
string CryptoUtils::NormalizeHostname(const char* hostname) {
- url_canon::CanonHostInfo host_info;
+ url::CanonHostInfo host_info;
string host(CanonicalizeHost(hostname, &host_info));
// Walk backwards over the string, stopping at the first trailing dot.
diff --git a/chromium/net/quic/crypto/crypto_utils.h b/chromium/net/quic/crypto/crypto_utils.h
index 147e41436f8..ec6384c131e 100644
--- a/chromium/net/quic/crypto/crypto_utils.h
+++ b/chromium/net/quic/crypto/crypto_utils.h
@@ -62,6 +62,9 @@ class NET_EXPORT_PRIVATE CryptoUtils {
const std::string& hkdf_input,
Perspective perspective,
CrypterPair* out);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CryptoUtils);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/curve25519_key_exchange.cc b/chromium/net/quic/crypto/curve25519_key_exchange.cc
index 3b8880453a3..8ed95aa26bc 100644
--- a/chromium/net/quic/crypto/curve25519_key_exchange.cc
+++ b/chromium/net/quic/crypto/curve25519_key_exchange.cc
@@ -4,6 +4,7 @@
#include "net/quic/crypto/curve25519_key_exchange.h"
+#include "base/basictypes.h"
#include "base/logging.h"
#include "crypto/curve25519.h"
#include "net/quic/crypto/quic_random.h"
diff --git a/chromium/net/quic/crypto/local_strike_register_client.cc b/chromium/net/quic/crypto/local_strike_register_client.cc
index c3751450397..1f78d9833d5 100644
--- a/chromium/net/quic/crypto/local_strike_register_client.cc
+++ b/chromium/net/quic/crypto/local_strike_register_client.cc
@@ -21,10 +21,12 @@ LocalStrikeRegisterClient::LocalStrikeRegisterClient(
orbit, startup) {
}
-string LocalStrikeRegisterClient::orbit() {
+bool LocalStrikeRegisterClient::IsKnownOrbit(StringPiece orbit) const {
base::AutoLock lock(m_);
- return string(reinterpret_cast<const char*>(strike_register_.orbit()),
- kOrbitSize);
+ if (orbit.length() != kOrbitSize) {
+ return false;
+ }
+ return memcmp(orbit.data(), strike_register_.orbit(), kOrbitSize) == 0;
}
void LocalStrikeRegisterClient::VerifyNonceIsValidAndUnique(
diff --git a/chromium/net/quic/crypto/local_strike_register_client.h b/chromium/net/quic/crypto/local_strike_register_client.h
index 37020b7b420..fe8ae93d3f4 100644
--- a/chromium/net/quic/crypto/local_strike_register_client.h
+++ b/chromium/net/quic/crypto/local_strike_register_client.h
@@ -5,6 +5,7 @@
#ifndef NET_QUIC_CRYPTO_LOCAL_STRIKE_REGISTER_CLIENT_H_
#define NET_QUIC_CRYPTO_LOCAL_STRIKE_REGISTER_CLIENT_H_
+#include "base/basictypes.h"
#include "base/strings/string_piece.h"
#include "base/synchronization/lock.h"
#include "net/base/net_export.h"
@@ -25,13 +26,13 @@ class NET_EXPORT_PRIVATE LocalStrikeRegisterClient
const uint8 orbit[8],
StrikeRegister::StartupType startup);
- virtual std::string orbit() OVERRIDE;
+ virtual bool IsKnownOrbit(base::StringPiece orbit) const OVERRIDE;
virtual void VerifyNonceIsValidAndUnique(base::StringPiece nonce,
QuicWallTime now,
ResultCallback* cb) OVERRIDE;
private:
- base::Lock m_;
+ mutable base::Lock m_;
StrikeRegister strike_register_;
DISALLOW_COPY_AND_ASSIGN(LocalStrikeRegisterClient);
diff --git a/chromium/net/quic/crypto/local_strike_register_client_test.cc b/chromium/net/quic/crypto/local_strike_register_client_test.cc
index 0227e92c6dc..268e714d612 100644
--- a/chromium/net/quic/crypto/local_strike_register_client_test.cc
+++ b/chromium/net/quic/crypto/local_strike_register_client_test.cc
@@ -26,7 +26,8 @@ class RecordResultCallback : public StrikeRegisterClient::ResultCallback {
// |*saved_value| and sets |*called| to true. The callback is self
// deleting.
RecordResultCallback(bool* called, bool* saved_value)
- : called_(called), saved_value_(saved_value) {
+ : called_(called),
+ saved_value_(saved_value) {
*called_ = false;
}
@@ -63,15 +64,21 @@ class LocalStrikeRegisterClientTest : public ::testing::Test {
};
TEST_F(LocalStrikeRegisterClientTest, CheckOrbit) {
- EXPECT_EQ(StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize),
- strike_register_->orbit());
+ EXPECT_TRUE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize)));
+ EXPECT_FALSE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize - 1)));
+ EXPECT_FALSE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize + 1)));
+ EXPECT_FALSE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit) + 1, kOrbitSize)));
}
TEST_F(LocalStrikeRegisterClientTest, IncorrectNonceLength) {
string valid_nonce;
uint32 norder = htonl(kCurrentTimeExternalSecs);
valid_nonce.assign(reinterpret_cast<const char*>(&norder), sizeof(norder));
- valid_nonce.append(strike_register_->orbit());
+ valid_nonce.append(string(reinterpret_cast<const char*>(kOrbit), kOrbitSize));
valid_nonce.append(string(20, '\x17')); // 20 'random' bytes.
{
diff --git a/chromium/net/quic/crypto/null_decrypter.h b/chromium/net/quic/crypto/null_decrypter.h
index e85e1247582..2bc2fe8cd8a 100644
--- a/chromium/net/quic/crypto/null_decrypter.h
+++ b/chromium/net/quic/crypto/null_decrypter.h
@@ -38,6 +38,8 @@ class NET_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter {
private:
bool ReadHash(QuicDataReader* reader, uint128* hash);
uint128 ComputeHash(const std::string& data) const;
+
+ DISALLOW_COPY_AND_ASSIGN(NullDecrypter);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/null_encrypter.h b/chromium/net/quic/crypto/null_encrypter.h
index 44e6f5555e6..1bcdff5a2c2 100644
--- a/chromium/net/quic/crypto/null_encrypter.h
+++ b/chromium/net/quic/crypto/null_encrypter.h
@@ -38,6 +38,8 @@ class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter {
private:
size_t GetHashLength() const;
+
+ DISALLOW_COPY_AND_ASSIGN(NullEncrypter);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/p256_key_exchange.h b/chromium/net/quic/crypto/p256_key_exchange.h
index 8145cc05249..49a66cec3de 100644
--- a/chromium/net/quic/crypto/p256_key_exchange.h
+++ b/chromium/net/quic/crypto/p256_key_exchange.h
@@ -73,6 +73,8 @@ class NET_EXPORT_PRIVATE P256KeyExchange : public KeyExchange {
#endif
// The public key stored as an uncompressed P-256 point.
uint8 public_key_[kUncompressedP256PointBytes];
+
+ DISALLOW_COPY_AND_ASSIGN(P256KeyExchange);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/proof_test.cc b/chromium/net/quic/crypto/proof_test.cc
index cc9e0992c1a..56e9abcfb61 100644
--- a/chromium/net/quic/crypto/proof_test.cc
+++ b/chromium/net/quic/crypto/proof_test.cc
@@ -24,74 +24,7 @@ using std::vector;
namespace net {
namespace test {
-
-TEST(ProofTest, Verify) {
- // TODO(rtenneti): Enable testing of ProofVerifier.
-#if 0
- scoped_ptr<ProofSource> source(CryptoTestUtils::ProofSourceForTesting());
- scoped_ptr<ProofVerifier> verifier(
- CryptoTestUtils::ProofVerifierForTesting());
-
- const string server_config = "server config bytes";
- const string hostname = "test.example.com";
- const vector<string>* certs;
- const vector<string>* first_certs;
- string error_details, signature, first_signature;
- CertVerifyResult cert_verify_result;
-
- ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */,
- &first_certs, &first_signature));
- ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */,
- &certs, &signature));
-
- // Check that the proof source is caching correctly:
- ASSERT_EQ(first_certs, certs);
- ASSERT_EQ(signature, first_signature);
-
- int rv;
- TestCompletionCallback callback;
- rv = verifier->VerifyProof(hostname, server_config, *certs, signature,
- &error_details, &cert_verify_result,
- callback.callback());
- rv = callback.GetResult(rv);
- ASSERT_EQ(OK, rv);
- ASSERT_EQ("", error_details);
- ASSERT_FALSE(IsCertStatusError(cert_verify_result.cert_status));
-
- rv = verifier->VerifyProof("foo.com", server_config, *certs, signature,
- &error_details, &cert_verify_result,
- callback.callback());
- rv = callback.GetResult(rv);
- ASSERT_EQ(ERR_FAILED, rv);
- ASSERT_NE("", error_details);
-
- rv = verifier->VerifyProof(hostname, server_config.substr(1, string::npos),
- *certs, signature, &error_details,
- &cert_verify_result, callback.callback());
- rv = callback.GetResult(rv);
- ASSERT_EQ(ERR_FAILED, rv);
- ASSERT_NE("", error_details);
-
- const string corrupt_signature = "1" + signature;
- rv = verifier->VerifyProof(hostname, server_config, *certs,
- corrupt_signature, &error_details,
- &cert_verify_result, callback.callback());
- rv = callback.GetResult(rv);
- ASSERT_EQ(ERR_FAILED, rv);
- ASSERT_NE("", error_details);
-
- vector<string> wrong_certs;
- for (size_t i = 1; i < certs->size(); i++) {
- wrong_certs.push_back((*certs)[i]);
- }
- rv = verifier->VerifyProof("foo.com", server_config, wrong_certs, signature,
- &error_details, &cert_verify_result,
- callback.callback());
- rv = callback.GetResult(rv);
- ASSERT_EQ(ERR_FAILED, rv);
- ASSERT_NE("", error_details);
-#endif // 0
-}
+namespace {
// TestProofVerifierCallback is a simple callback for a ProofVerifier that
// signals a TestCompletionCallback when called and stores the results from the
@@ -100,13 +33,13 @@ class TestProofVerifierCallback : public ProofVerifierCallback {
public:
TestProofVerifierCallback(TestCompletionCallback* comp_callback,
bool* ok,
- std::string* error_details)
+ string* error_details)
: comp_callback_(comp_callback),
ok_(ok),
error_details_(error_details) {}
virtual void Run(bool ok,
- const std::string& error_details,
+ const string& error_details,
scoped_ptr<ProofVerifyDetails>* details) OVERRIDE {
*ok_ = ok;
*error_details_ = error_details;
@@ -117,48 +50,54 @@ class TestProofVerifierCallback : public ProofVerifierCallback {
private:
TestCompletionCallback* const comp_callback_;
bool* const ok_;
- std::string* const error_details_;
+ string* const error_details_;
};
// RunVerification runs |verifier->VerifyProof| and asserts that the result
// matches |expected_ok|.
-static void RunVerification(ProofVerifier* verifier,
- const std::string& hostname,
- const std::string& server_config,
- const vector<std::string>& certs,
- const std::string& proof,
- bool expected_ok) {
+void RunVerification(ProofVerifier* verifier,
+ const string& hostname,
+ const string& server_config,
+ const vector<string>& certs,
+ const string& proof,
+ bool expected_ok) {
scoped_ptr<ProofVerifyDetails> details;
TestCompletionCallback comp_callback;
bool ok;
- std::string error_details;
+ string error_details;
+ scoped_ptr<ProofVerifyContext> verify_context(
+ CryptoTestUtils::ProofVerifyContextForTesting());
TestProofVerifierCallback* callback =
new TestProofVerifierCallback(&comp_callback, &ok, &error_details);
- ProofVerifier::Status status = verifier->VerifyProof(
- hostname, server_config, certs, proof, &error_details, &details,
- callback);
+ QuicAsyncStatus status = verifier->VerifyProof(
+ hostname, server_config, certs, proof, verify_context.get(),
+ &error_details, &details, callback);
switch (status) {
- case ProofVerifier::FAILURE:
+ case QUIC_FAILURE:
+ delete callback;
ASSERT_FALSE(expected_ok);
ASSERT_NE("", error_details);
return;
- case ProofVerifier::SUCCESS:
+ case QUIC_SUCCESS:
+ delete callback;
ASSERT_TRUE(expected_ok);
ASSERT_EQ("", error_details);
return;
- case ProofVerifier::PENDING:
+ case QUIC_PENDING:
comp_callback.WaitForResult();
ASSERT_EQ(expected_ok, ok);
break;
}
}
-static string PEMCertFileToDER(const string& file_name) {
+// Reads the certificate named "quic_" + |file_name| in the test data directory.
+// The certificate must be PEM encoded. Returns the DER-encoded certificate.
+string LoadTestCert(const string& file_name) {
base::FilePath certs_dir = GetTestCertsDirectory();
scoped_refptr<X509Certificate> cert =
- ImportCertFromFile(certs_dir, file_name);
+ ImportCertFromFile(certs_dir, "quic_" + file_name);
CHECK_NE(static_cast<X509Certificate*>(NULL), cert);
string der_bytes;
@@ -166,12 +105,58 @@ static string PEMCertFileToDER(const string& file_name) {
return der_bytes;
}
+} // namespace
+
+// TODO(rtenneti): Enable testing of ProofVerifier.
+TEST(ProofTest, DISABLED_Verify) {
+ scoped_ptr<ProofSource> source(CryptoTestUtils::ProofSourceForTesting());
+ scoped_ptr<ProofVerifier> verifier(
+ CryptoTestUtils::ProofVerifierForTesting());
+
+ const string server_config = "server config bytes";
+ const string hostname = "test.example.com";
+ const vector<string>* certs;
+ const vector<string>* first_certs;
+ string error_details, signature, first_signature;
+
+ ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */,
+ &first_certs, &first_signature));
+ ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */,
+ &certs, &signature));
+
+ // Check that the proof source is caching correctly:
+ ASSERT_EQ(first_certs, certs);
+ ASSERT_EQ(signature, first_signature);
+
+ RunVerification(
+ verifier.get(), hostname, server_config, *certs, signature, true);
+
+ RunVerification(
+ verifier.get(), "foo.com", server_config, *certs, signature, false);
+
+ RunVerification(
+ verifier.get(), server_config.substr(1, string::npos), server_config,
+ *certs, signature, false);
+
+ const string corrupt_signature = "1" + signature;
+ RunVerification(
+ verifier.get(), hostname, server_config, *certs, corrupt_signature,
+ false);
+
+ vector<string> wrong_certs;
+ for (size_t i = 1; i < certs->size(); i++) {
+ wrong_certs.push_back((*certs)[i]);
+ }
+ RunVerification(
+ verifier.get(), "foo.com", server_config, wrong_certs, corrupt_signature,
+ false);
+}
+
// A known answer test that allows us to test ProofVerifier without a working
// ProofSource.
TEST(ProofTest, VerifyRSAKnownAnswerTest) {
// These sample signatures were generated by running the Proof.Verify test
// and dumping the bytes of the |signature| output of ProofSource::GetProof().
- // sLen = special value -2 used by OpenSSL.
static const unsigned char signature_data_0[] = {
0x31, 0xd5, 0xfb, 0x40, 0x30, 0x75, 0xd2, 0x7d, 0x61, 0xf9, 0xd7, 0x54,
0x30, 0x06, 0xaf, 0x54, 0x0d, 0xb0, 0x0a, 0xda, 0x63, 0xca, 0x7e, 0x9e,
@@ -250,11 +235,10 @@ TEST(ProofTest, VerifyRSAKnownAnswerTest) {
const string server_config = "server config bytes";
const string hostname = "test.example.com";
- CertVerifyResult cert_verify_result;
vector<string> certs(2);
- certs[0] = PEMCertFileToDER("quic_test.example.com.crt");
- certs[1] = PEMCertFileToDER("quic_intermediate.crt");
+ certs[0] = LoadTestCert("test.example.com.crt");
+ certs[1] = LoadTestCert("intermediate.crt");
// Signatures are nondeterministic, so we test multiple signatures on the
// same server_config.
@@ -333,11 +317,10 @@ TEST(ProofTest, VerifyECDSAKnownAnswerTest) {
const string server_config = "server config bytes";
const string hostname = "test.example.com";
- CertVerifyResult cert_verify_result;
vector<string> certs(2);
- certs[0] = PEMCertFileToDER("quic_test_ecc.example.com.crt");
- certs[1] = PEMCertFileToDER("quic_intermediate.crt");
+ certs[0] = LoadTestCert("test_ecc.example.com.crt");
+ certs[1] = LoadTestCert("intermediate.crt");
// Signatures are nondeterministic, so we test multiple signatures on the
// same server_config.
diff --git a/chromium/net/quic/crypto/proof_verifier.cc b/chromium/net/quic/crypto/proof_verifier.cc
deleted file mode 100644
index 7bccba2bdb8..00000000000
--- a/chromium/net/quic/crypto/proof_verifier.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/crypto/proof_verifier.h"
-
-namespace net {
-
-ProofVerifyDetails::~ProofVerifyDetails() {}
-
-ProofVerifierCallback::~ProofVerifierCallback() {}
-
-ProofVerifier::~ProofVerifier() {}
-
-} // namespace net
diff --git a/chromium/net/quic/crypto/proof_verifier.h b/chromium/net/quic/crypto/proof_verifier.h
index f469c552959..83f7c4366fd 100644
--- a/chromium/net/quic/crypto/proof_verifier.h
+++ b/chromium/net/quic/crypto/proof_verifier.h
@@ -8,30 +8,36 @@
#include <string>
#include <vector>
-#include "net/base/completion_callback.h"
+#include "base/memory/scoped_ptr.h"
#include "net/base/net_export.h"
+#include "net/quic/quic_types.h"
namespace net {
-class CertVerifyResult;
-
// ProofVerifyDetails is an abstract class that acts as a container for any
// implementation specific details that a ProofVerifier wishes to return. These
-// details are saved in the CachedInfo for the origin in question.
-class ProofVerifyDetails {
+// details are saved in the CachedState for the origin in question.
+class NET_EXPORT_PRIVATE ProofVerifyDetails {
+ public:
+ virtual ~ProofVerifyDetails() {}
+};
+
+// ProofVerifyContext is an abstract class that acts as a container for any
+// implementation specific context that a ProofVerifier needs.
+class NET_EXPORT_PRIVATE ProofVerifyContext {
public:
- virtual ~ProofVerifyDetails();
+ virtual ~ProofVerifyContext() {}
};
// ProofVerifierCallback provides a generic mechanism for a ProofVerifier to
// call back after an asynchronous verification.
class NET_EXPORT_PRIVATE ProofVerifierCallback {
public:
- virtual ~ProofVerifierCallback();
+ virtual ~ProofVerifierCallback() {}
// Run is called on the original thread to mark the completion of an
// asynchonous verification. If |ok| is true then the certificate is valid
- // and |*error_details| is unused. Otherwise, |*error_details| contains a
+ // and |error_details| is unused. Otherwise, |error_details| contains a
// description of the error. |details| contains implementation-specific
// details of the verification. |Run| may take ownership of |details| by
// calling |release| on it.
@@ -44,39 +50,33 @@ class NET_EXPORT_PRIVATE ProofVerifierCallback {
// chain that backs the public key.
class NET_EXPORT_PRIVATE ProofVerifier {
public:
- // Status enumerates the possible results of verifying a proof.
- enum Status {
- SUCCESS = 0,
- FAILURE = 1,
- // PENDING results from a verification which will occur asynchonously. When
- // the verification is complete, |callback|'s |Run| method will be called.
- PENDING = 2,
- };
-
- virtual ~ProofVerifier();
+ virtual ~ProofVerifier() {}
// VerifyProof checks that |signature| is a valid signature of
// |server_config| by the public key in the leaf certificate of |certs|, and
// that |certs| is a valid chain for |hostname|. On success, it returns
- // SUCCESS. On failure, it returns ERROR and sets |*error_details| to a
- // description of the problem. In either case it may set |*details|, which the
- // caller takes ownership of.
+ // QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and sets |*error_details|
+ // to a description of the problem. In either case it may set |*details|,
+ // which the caller takes ownership of.
//
- // This function may also return PENDING, in which case the ProofVerifier
- // will call back, on the original thread, via |callback| when complete.
+ // |context| specifies an implementation specific struct (which may be NULL
+ // for some implementations) that provides useful information for the
+ // verifier, e.g. logging handles.
//
- // This function takes ownership of |callback|. It will be deleted even if
- // the call returns immediately.
+ // This function may also return QUIC_PENDING, in which case the ProofVerifier
+ // will call back, on the original thread, via |callback| when complete.
+ // In this case, the ProofVerifier will take ownership of |callback|.
//
// The signature uses SHA-256 as the hash function and PSS padding in the
// case of RSA.
- virtual Status VerifyProof(const std::string& hostname,
- const std::string& server_config,
- const std::vector<std::string>& certs,
- const std::string& signature,
- std::string* error_details,
- scoped_ptr<ProofVerifyDetails>* details,
- ProofVerifierCallback* callback) = 0;
+ virtual QuicAsyncStatus VerifyProof(const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* details,
+ ProofVerifierCallback* callback) = 0;
};
} // namespace net
diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.cc b/chromium/net/quic/crypto/proof_verifier_chromium.cc
index 68ad25ca12d..4b118da198c 100644
--- a/chromium/net/quic/crypto/proof_verifier_chromium.cc
+++ b/chromium/net/quic/crypto/proof_verifier_chromium.cc
@@ -9,6 +9,7 @@
#include "base/callback_helpers.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "crypto/signature_verifier.h"
#include "net/base/net_errors.h"
@@ -30,37 +31,91 @@ using std::vector;
namespace net {
-ProofVerifierChromium::ProofVerifierChromium(CertVerifier* cert_verifier,
- const BoundNetLog& net_log)
- : cert_verifier_(cert_verifier),
- next_state_(STATE_NONE),
- net_log_(net_log) {
-}
+// A Job handles the verification of a single proof. It is owned by the
+// ProofVerifier. If the verification can not complete synchronously, it
+// will notify the ProofVerifier upon completion.
+class ProofVerifierChromium::Job {
+ public:
+ Job(ProofVerifierChromium* proof_verifier,
+ CertVerifier* cert_verifier,
+ const BoundNetLog& net_log);
-ProofVerifierChromium::~ProofVerifierChromium() {
- verifier_.reset();
+ // Starts the proof verification. If |QUIC_PENDING| is returned, then
+ // |callback| will be invoked asynchronously when the verification completes.
+ QuicAsyncStatus VerifyProof(const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback);
+
+ private:
+ enum State {
+ STATE_NONE,
+ STATE_VERIFY_CERT,
+ STATE_VERIFY_CERT_COMPLETE,
+ };
+
+ int DoLoop(int last_io_result);
+ void OnIOComplete(int result);
+ int DoVerifyCert(int result);
+ int DoVerifyCertComplete(int result);
+
+ bool VerifySignature(const std::string& signed_data,
+ const std::string& signature,
+ const std::string& cert);
+
+ // Proof verifier to notify when this jobs completes.
+ ProofVerifierChromium* proof_verifier_;
+
+ // The underlying verifier used for verifying certificates.
+ scoped_ptr<SingleRequestCertVerifier> verifier_;
+
+ // |hostname| specifies the hostname for which |certs| is a valid chain.
+ std::string hostname_;
+
+ scoped_ptr<ProofVerifierCallback> callback_;
+ scoped_ptr<ProofVerifyDetailsChromium> verify_details_;
+ std::string error_details_;
+
+ // X509Certificate from a chain of DER encoded certificates.
+ scoped_refptr<X509Certificate> cert_;
+
+ State next_state_;
+
+ BoundNetLog net_log_;
+
+ DISALLOW_COPY_AND_ASSIGN(Job);
+};
+
+ProofVerifierChromium::Job::Job(ProofVerifierChromium* proof_verifier,
+ CertVerifier* cert_verifier,
+ const BoundNetLog& net_log)
+ : proof_verifier_(proof_verifier),
+ verifier_(new SingleRequestCertVerifier(cert_verifier)),
+ next_state_(STATE_NONE),
+ net_log_(net_log) {
}
-ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
+QuicAsyncStatus ProofVerifierChromium::Job::VerifyProof(
const string& hostname,
const string& server_config,
const vector<string>& certs,
const string& signature,
std::string* error_details,
- scoped_ptr<ProofVerifyDetails>* details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
ProofVerifierCallback* callback) {
DCHECK(error_details);
- DCHECK(details);
+ DCHECK(verify_details);
DCHECK(callback);
- callback_.reset(callback);
error_details->clear();
- DCHECK_EQ(STATE_NONE, next_state_);
if (STATE_NONE != next_state_) {
*error_details = "Certificate is already set and VerifyProof has begun";
- DLOG(WARNING) << *error_details;
- return FAILURE;
+ DLOG(DFATAL) << *error_details;
+ return QUIC_FAILURE;
}
verify_details_.reset(new ProofVerifyDetailsChromium);
@@ -69,8 +124,8 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
*error_details = "Failed to create certificate chain. Certs are empty.";
DLOG(WARNING) << *error_details;
verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
- details->reset(verify_details_.release());
- return FAILURE;
+ verify_details->reset(verify_details_.release());
+ return QUIC_FAILURE;
}
// Convert certs to X509Certificate.
@@ -83,8 +138,8 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
*error_details = "Failed to create certificate chain";
DLOG(WARNING) << *error_details;
verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
- details->reset(verify_details_.release());
- return FAILURE;
+ verify_details->reset(verify_details_.release());
+ return QUIC_FAILURE;
}
// We call VerifySignature first to avoid copying of server_config and
@@ -93,8 +148,8 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
*error_details = "Failed to verify signature of server config";
DLOG(WARNING) << *error_details;
verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
- details->reset(verify_details_.release());
- return FAILURE;
+ verify_details->reset(verify_details_.release());
+ return QUIC_FAILURE;
}
hostname_ = hostname;
@@ -102,18 +157,19 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
next_state_ = STATE_VERIFY_CERT;
switch (DoLoop(OK)) {
case OK:
- details->reset(verify_details_.release());
- return SUCCESS;
+ verify_details->reset(verify_details_.release());
+ return QUIC_SUCCESS;
case ERR_IO_PENDING:
- return PENDING;
+ callback_.reset(callback);
+ return QUIC_PENDING;
default:
*error_details = error_details_;
- details->reset(verify_details_.release());
- return FAILURE;
+ verify_details->reset(verify_details_.release());
+ return QUIC_FAILURE;
}
}
-int ProofVerifierChromium::DoLoop(int last_result) {
+int ProofVerifierChromium::Job::DoLoop(int last_result) {
int rv = last_result;
do {
State state = next_state_;
@@ -136,32 +192,34 @@ int ProofVerifierChromium::DoLoop(int last_result) {
return rv;
}
-void ProofVerifierChromium::OnIOComplete(int result) {
+void ProofVerifierChromium::Job::OnIOComplete(int result) {
int rv = DoLoop(result);
if (rv != ERR_IO_PENDING) {
- scoped_ptr<ProofVerifyDetails> scoped_details(verify_details_.release());
- callback_->Run(rv == OK, error_details_, &scoped_details);
- callback_.reset();
+ scoped_ptr<ProofVerifierCallback> callback(callback_.release());
+ // Callback expects ProofVerifyDetails not ProofVerifyDetailsChromium.
+ scoped_ptr<ProofVerifyDetails> verify_details(verify_details_.release());
+ callback->Run(rv == OK, error_details_, &verify_details);
+ // Will delete |this|.
+ proof_verifier_->OnJobComplete(this);
}
}
-int ProofVerifierChromium::DoVerifyCert(int result) {
+int ProofVerifierChromium::Job::DoVerifyCert(int result) {
next_state_ = STATE_VERIFY_CERT_COMPLETE;
int flags = 0;
- verifier_.reset(new SingleRequestCertVerifier(cert_verifier_));
return verifier_->Verify(
cert_.get(),
hostname_,
flags,
SSLConfigService::GetCRLSet().get(),
&verify_details_->cert_verify_result,
- base::Bind(&ProofVerifierChromium::OnIOComplete,
+ base::Bind(&ProofVerifierChromium::Job::OnIOComplete,
base::Unretained(this)),
net_log_);
}
-int ProofVerifierChromium::DoVerifyCertComplete(int result) {
+int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) {
verifier_.reset();
if (result <= ERR_FAILED) {
@@ -176,7 +234,7 @@ int ProofVerifierChromium::DoVerifyCertComplete(int result) {
return result;
}
-bool ProofVerifierChromium::VerifySignature(const string& signed_data,
+bool ProofVerifierChromium::Job::VerifySignature(const string& signed_data,
const string& signature,
const string& cert) {
StringPiece spki;
@@ -252,4 +310,41 @@ bool ProofVerifierChromium::VerifySignature(const string& signed_data,
return true;
}
+ProofVerifierChromium::ProofVerifierChromium(CertVerifier* cert_verifier)
+ : cert_verifier_(cert_verifier) {}
+
+ProofVerifierChromium::~ProofVerifierChromium() {
+ STLDeleteElements(&active_jobs_);
+}
+
+QuicAsyncStatus ProofVerifierChromium::VerifyProof(
+ const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ const ProofVerifyContext* verify_context,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback) {
+ if (!verify_context) {
+ *error_details = "Missing context";
+ return QUIC_FAILURE;
+ }
+ const ProofVerifyContextChromium* chromium_context =
+ reinterpret_cast<const ProofVerifyContextChromium*>(verify_context);
+ scoped_ptr<Job> job(new Job(this, cert_verifier_, chromium_context->net_log));
+ QuicAsyncStatus status = job->VerifyProof(hostname, server_config, certs,
+ signature, error_details,
+ verify_details, callback);
+ if (status == QUIC_PENDING) {
+ active_jobs_.insert(job.release());
+ }
+ return status;
+}
+
+void ProofVerifierChromium::OnJobComplete(Job* job) {
+ active_jobs_.erase(job);
+ delete job;
+}
+
} // namespace net
diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.h b/chromium/net/quic/crypto/proof_verifier_chromium.h
index 4969cc8aa55..6f8a23131f3 100644
--- a/chromium/net/quic/crypto/proof_verifier_chromium.h
+++ b/chromium/net/quic/crypto/proof_verifier_chromium.h
@@ -5,13 +5,13 @@
#ifndef NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_
#define NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_
+#include <set>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
-#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
#include "net/base/net_log.h"
#include "net/cert/cert_verify_result.h"
@@ -30,56 +30,45 @@ struct ProofVerifyDetailsChromium : public ProofVerifyDetails {
CertVerifyResult cert_verify_result;
};
-// ProofVerifierChromium implements the QUIC ProofVerifier interface.
-// TODO(rtenneti): Add support for multiple requests for one ProofVerifier.
+// ProofVerifyContextChromium is the implementation-specific information that a
+// ProofVerifierChromium needs in order to log correctly.
+struct ProofVerifyContextChromium : public ProofVerifyContext {
+ public:
+ explicit ProofVerifyContextChromium(const BoundNetLog& net_log)
+ : net_log(net_log) {}
+
+ BoundNetLog net_log;
+};
+
+// ProofVerifierChromium implements the QUIC ProofVerifier interface. It is
+// capable of handling multiple simultaneous requests.
class NET_EXPORT_PRIVATE ProofVerifierChromium : public ProofVerifier {
public:
- ProofVerifierChromium(CertVerifier* cert_verifier,
- const BoundNetLog& net_log);
+ explicit ProofVerifierChromium(CertVerifier* cert_verifier);
virtual ~ProofVerifierChromium();
// ProofVerifier interface
- virtual Status VerifyProof(const std::string& hostname,
- const std::string& server_config,
- const std::vector<std::string>& certs,
- const std::string& signature,
- std::string* error_details,
- scoped_ptr<ProofVerifyDetails>* details,
- ProofVerifierCallback* callback) OVERRIDE;
+ virtual QuicAsyncStatus VerifyProof(
+ const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ const ProofVerifyContext* verify_context,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback) OVERRIDE;
private:
- enum State {
- STATE_NONE,
- STATE_VERIFY_CERT,
- STATE_VERIFY_CERT_COMPLETE,
- };
-
- int DoLoop(int last_io_result);
- void OnIOComplete(int result);
- int DoVerifyCert(int result);
- int DoVerifyCertComplete(int result);
-
- bool VerifySignature(const std::string& signed_data,
- const std::string& signature,
- const std::string& cert);
-
- // |cert_verifier_| and |verifier_| are used for verifying certificates.
- CertVerifier* const cert_verifier_;
- scoped_ptr<SingleRequestCertVerifier> verifier_;
-
- // |hostname| specifies the hostname for which |certs| is a valid chain.
- std::string hostname_;
+ class Job;
- scoped_ptr<ProofVerifierCallback> callback_;
- scoped_ptr<ProofVerifyDetailsChromium> verify_details_;
- std::string error_details_;
+ void OnJobComplete(Job* job);
- // X509Certificate from a chain of DER encoded certificates.
- scoped_refptr<X509Certificate> cert_;
+ // Set owning pointers to active jobs.
+ typedef std::set<Job*> JobSet;
+ JobSet active_jobs_;
- State next_state_;
-
- BoundNetLog net_log_;
+ // Underlying verifier used to verify certificates.
+ CertVerifier* const cert_verifier_;
DISALLOW_COPY_AND_ASSIGN(ProofVerifierChromium);
};
diff --git a/chromium/net/quic/crypto/quic_crypto_client_config.cc b/chromium/net/quic/crypto/quic_crypto_client_config.cc
index a3eeeb62d22..9d5e704e1df 100644
--- a/chromium/net/quic/crypto/quic_crypto_client_config.cc
+++ b/chromium/net/quic/crypto/quic_crypto_client_config.cc
@@ -5,7 +5,9 @@
#include "net/quic/crypto/quic_crypto_client_config.h"
#include "base/stl_util.h"
+#include "base/strings/string_util.h"
#include "net/quic/crypto/cert_compressor.h"
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
#include "net/quic/crypto/channel_id.h"
#include "net/quic/crypto/common_cert_set.h"
#include "net/quic/crypto/crypto_framer.h"
@@ -17,18 +19,17 @@
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_utils.h"
-#if defined(OS_WIN)
-#include "base/win/windows_version.h"
-#endif
-
using base::StringPiece;
+using std::find;
+using std::make_pair;
using std::map;
using std::string;
using std::vector;
namespace net {
-QuicCryptoClientConfig::QuicCryptoClientConfig() {}
+QuicCryptoClientConfig::QuicCryptoClientConfig()
+ : disable_ecdsa_(false) {}
QuicCryptoClientConfig::~QuicCryptoClientConfig() {
STLDeleteValues(&cached_states_);
@@ -61,6 +62,10 @@ bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const {
return true;
}
+bool QuicCryptoClientConfig::CachedState::IsEmpty() const {
+ return server_config_.empty();
+}
+
const CryptoHandshakeMessage*
QuicCryptoClientConfig::CachedState::GetServerConfig() const {
if (server_config_.empty()) {
@@ -144,6 +149,17 @@ void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs,
server_config_sig_ = signature.as_string();
}
+void QuicCryptoClientConfig::CachedState::Clear() {
+ server_config_.clear();
+ source_address_token_.clear();
+ certs_.clear();
+ server_config_sig_.clear();
+ server_config_valid_ = false;
+ proof_verify_details_.reset();
+ scfg_.reset();
+ ++generation_counter_;
+}
+
void QuicCryptoClientConfig::CachedState::ClearProof() {
SetProofInvalid();
certs_.clear();
@@ -159,6 +175,32 @@ void QuicCryptoClientConfig::CachedState::SetProofInvalid() {
++generation_counter_;
}
+bool QuicCryptoClientConfig::CachedState::Initialize(
+ StringPiece server_config,
+ StringPiece source_address_token,
+ const vector<string>& certs,
+ StringPiece signature,
+ QuicWallTime now) {
+ DCHECK(server_config_.empty());
+
+ if (server_config.empty()) {
+ return false;
+ }
+
+ string error_details;
+ QuicErrorCode error = SetServerConfig(server_config, now,
+ &error_details);
+ if (error != QUIC_NO_ERROR) {
+ DVLOG(1) << "SetServerConfig failed with " << error_details;
+ return false;
+ }
+
+ signature.CopyToString(&server_config_sig_);
+ source_address_token.CopyToString(&source_address_token_);
+ certs_ = certs;
+ return true;
+}
+
const string& QuicCryptoClientConfig::CachedState::server_config() const {
return server_config_;
}
@@ -208,6 +250,7 @@ void QuicCryptoClientConfig::CachedState::InitializeFrom(
certs_ = other.certs_;
server_config_sig_ = other.server_config_sig_;
server_config_valid_ = other.server_config_valid_;
+ ++generation_counter_;
}
void QuicCryptoClientConfig::SetDefaults() {
@@ -216,26 +259,38 @@ void QuicCryptoClientConfig::SetDefaults() {
kexs[0] = kC255;
kexs[1] = kP256;
- // Authenticated encryption algorithms.
- aead.resize(1);
- aead[0] = kAESG;
+ // Authenticated encryption algorithms. Prefer ChaCha20 by default.
+ aead.clear();
+ if (ChaCha20Poly1305Encrypter::IsSupported()) {
+ aead.push_back(kCC12);
+ }
+ aead.push_back(kAESG);
+
+ disable_ecdsa_ = false;
}
QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate(
- const string& server_hostname) {
- map<string, CachedState*>::const_iterator it =
- cached_states_.find(server_hostname);
+ const QuicServerId& server_id) {
+ CachedStateMap::const_iterator it = cached_states_.find(server_id);
if (it != cached_states_.end()) {
return it->second;
}
CachedState* cached = new CachedState;
- cached_states_.insert(make_pair(server_hostname, cached));
+ cached_states_.insert(make_pair(server_id, cached));
+ PopulateFromCanonicalConfig(server_id, cached);
return cached;
}
+void QuicCryptoClientConfig::ClearCachedStates() {
+ for (CachedStateMap::const_iterator it = cached_states_.begin();
+ it != cached_states_.end(); ++it) {
+ it->second->Clear();
+ }
+}
+
void QuicCryptoClientConfig::FillInchoateClientHello(
- const string& server_hostname,
+ const QuicServerId& server_id,
const QuicVersion preferred_version,
const CachedState* cached,
QuicCryptoNegotiatedParameters* out_params,
@@ -245,26 +300,21 @@ void QuicCryptoClientConfig::FillInchoateClientHello(
// Server name indication. We only send SNI if it's a valid domain name, as
// per the spec.
- if (CryptoUtils::IsValidSNI(server_hostname)) {
- out->SetStringPiece(kSNI, server_hostname);
+ if (CryptoUtils::IsValidSNI(server_id.host())) {
+ out->SetStringPiece(kSNI, server_id.host());
}
- // TODO(rch): Remove once we remove QUIC_VERSION_12.
- out->SetValue(kVERS, static_cast<uint16>(0));
out->SetValue(kVER, QuicVersionToQuicTag(preferred_version));
+ if (!user_agent_id_.empty()) {
+ out->SetStringPiece(kUAID, user_agent_id_);
+ }
+
if (!cached->source_address_token().empty()) {
out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token());
}
- if (proof_verifier_.get()) {
- // Don't request ECDSA proofs on platforms that do not support ECDSA
- // certificates.
- bool disableECDSA = false;
-#if defined(OS_WIN)
- if (base::win::GetVersion() < base::win::VERSION_VISTA)
- disableECDSA = true;
-#endif
- if (disableECDSA) {
+ if (server_id.is_https()) {
+ if (disable_ecdsa_) {
out->SetTaglist(kPDMD, kX59R, 0);
} else {
out->SetTaglist(kPDMD, kX509, 0);
@@ -293,18 +343,19 @@ void QuicCryptoClientConfig::FillInchoateClientHello(
}
QuicErrorCode QuicCryptoClientConfig::FillClientHello(
- const string& server_hostname,
- QuicGuid guid,
+ const QuicServerId& server_id,
+ QuicConnectionId connection_id,
const QuicVersion preferred_version,
const CachedState* cached,
QuicWallTime now,
QuicRandom* rand,
+ const ChannelIDKey* channel_id_key,
QuicCryptoNegotiatedParameters* out_params,
CryptoHandshakeMessage* out,
string* error_details) const {
DCHECK(error_details != NULL);
- FillInchoateClientHello(server_hostname, preferred_version, cached,
+ FillInchoateClientHello(server_id, preferred_version, cached,
out_params, out);
const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
@@ -333,13 +384,18 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
+ // AEAD: the work loads on the client and server are symmetric. Since the
+ // client is more likely to be CPU-constrained, break the tie by favoring
+ // the client's preference.
+ // Key exchange: the client does more work than the server, so favor the
+ // client's preference.
size_t key_exchange_index;
if (!QuicUtils::FindMutualTag(
- aead, their_aeads, num_their_aeads, QuicUtils::PEER_PRIORITY,
+ aead, their_aeads, num_their_aeads, QuicUtils::LOCAL_PRIORITY,
&out_params->aead, NULL) ||
!QuicUtils::FindMutualTag(
kexs, their_key_exchanges, num_their_key_exchanges,
- QuicUtils::PEER_PRIORITY, &out_params->key_exchange,
+ QuicUtils::LOCAL_PRIORITY, &out_params->key_exchange,
&key_exchange_index)) {
*error_details = "Unsupported AEAD or KEXS";
return QUIC_CRYPTO_NO_SUPPORT;
@@ -388,22 +444,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
}
out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value());
- bool do_channel_id = false;
- if (channel_id_signer_.get()) {
- const QuicTag* their_proof_demands;
- size_t num_their_proof_demands;
- if (scfg->GetTaglist(kPDMD, &their_proof_demands,
- &num_their_proof_demands) == QUIC_NO_ERROR) {
- for (size_t i = 0; i < num_their_proof_demands; i++) {
- if (their_proof_demands[i] == kCHID) {
- do_channel_id = true;
- break;
- }
- }
- }
- }
-
- if (do_channel_id) {
+ if (channel_id_key) {
// In order to calculate the encryption key for the CETV block we need to
// serialise the client hello as it currently is (i.e. without the CETV
// block). For this, the client hello is serialized without padding.
@@ -417,14 +458,15 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
const QuicData& client_hello_serialized = out->GetSerialized();
hkdf_input.append(QuicCryptoConfig::kCETVLabel,
strlen(QuicCryptoConfig::kCETVLabel) + 1);
- hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid));
+ hkdf_input.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
hkdf_input.append(client_hello_serialized.data(),
client_hello_serialized.length());
hkdf_input.append(cached->server_config());
- string key, signature;
- if (!channel_id_signer_->Sign(server_hostname, hkdf_input,
- &key, &signature)) {
+ string key = channel_id_key->SerializeKey();
+ string signature;
+ if (!channel_id_key->Sign(hkdf_input, &signature)) {
*error_details = "Channel ID signature failed";
return QUIC_INVALID_CHANNEL_ID_SIGNATURE;
}
@@ -457,9 +499,13 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
out->set_minimum_size(orig_min_size);
}
+ // Derive the symmetric keys and set up the encrypters and decrypters.
+ // Set the following members of out_params:
+ // out_params->hkdf_input_suffix
+ // out_params->initial_crypters
out_params->hkdf_input_suffix.clear();
- out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&guid),
- sizeof(guid));
+ out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
const QuicData& client_hello_serialized = out->GetSerialized();
out_params->hkdf_input_suffix.append(client_hello_serialized.data(),
client_hello_serialized.length());
@@ -541,12 +587,23 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
}
}
+ const QuicTag* reject_reasons;
+ size_t num_reject_reasons;
+ if (rej.GetTaglist(kRREJ, &reject_reasons,
+ &num_reject_reasons) == QUIC_NO_ERROR) {
+#if defined(DEBUG)
+ for (size_t i = 0; i < num_reject_reasons; ++i) {
+ DVLOG(1) << "Reasons for rejection: " << reject_reasons[i];
+ }
+#endif
+ }
+
return QUIC_NO_ERROR;
}
QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
const CryptoHandshakeMessage& server_hello,
- QuicGuid guid,
+ QuicConnectionId connection_id,
const QuicVersionVector& negotiated_versions,
CachedState* cached,
QuicCryptoNegotiatedParameters* out_params,
@@ -560,23 +617,24 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
const QuicTag* supported_version_tags;
size_t num_supported_versions;
- // TODO(rch): Once QUIC_VERSION_12 is removed, then make it a failure
- // if the server does not have a version list.
+
if (server_hello.GetTaglist(kVER, &supported_version_tags,
- &num_supported_versions) == QUIC_NO_ERROR) {
- if (!negotiated_versions.empty()) {
- bool mismatch = num_supported_versions != negotiated_versions.size();
- for (size_t i = 0; i < num_supported_versions && !mismatch; ++i) {
- mismatch = QuicTagToQuicVersion(supported_version_tags[i]) !=
- negotiated_versions[i];
- }
- // The server sent a list of supported versions, and the connection
- // reports that there was a version negotiation during the handshake.
+ &num_supported_versions) != QUIC_NO_ERROR) {
+ *error_details = "server hello missing version list";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ if (!negotiated_versions.empty()) {
+ bool mismatch = num_supported_versions != negotiated_versions.size();
+ for (size_t i = 0; i < num_supported_versions && !mismatch; ++i) {
+ mismatch = QuicTagToQuicVersion(supported_version_tags[i]) !=
+ negotiated_versions[i];
+ }
+ // The server sent a list of supported versions, and the connection
+ // reports that there was a version negotiation during the handshake.
// Ensure that these two lists are identical.
- if (mismatch) {
- *error_details = "Downgrade attack detected";
- return QUIC_VERSION_NEGOTIATION_MISMATCH;
- }
+ if (mismatch) {
+ *error_details = "Downgrade attack detected";
+ return QUIC_VERSION_NEGOTIATION_MISMATCH;
}
}
@@ -626,25 +684,81 @@ void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) {
proof_verifier_.reset(verifier);
}
-ChannelIDSigner* QuicCryptoClientConfig::channel_id_signer() const {
- return channel_id_signer_.get();
+ChannelIDSource* QuicCryptoClientConfig::channel_id_source() const {
+ return channel_id_source_.get();
}
-void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) {
- channel_id_signer_.reset(signer);
+void QuicCryptoClientConfig::SetChannelIDSource(ChannelIDSource* source) {
+ channel_id_source_.reset(source);
}
void QuicCryptoClientConfig::InitializeFrom(
- const std::string& server_hostname,
- const std::string& canonical_server_hostname,
+ const QuicServerId& server_id,
+ const QuicServerId& canonical_server_id,
QuicCryptoClientConfig* canonical_crypto_config) {
CachedState* canonical_cached =
- canonical_crypto_config->LookupOrCreate(canonical_server_hostname);
+ canonical_crypto_config->LookupOrCreate(canonical_server_id);
if (!canonical_cached->proof_valid()) {
return;
}
- CachedState* cached = LookupOrCreate(server_hostname);
+ CachedState* cached = LookupOrCreate(server_id);
cached->InitializeFrom(*canonical_cached);
}
+void QuicCryptoClientConfig::AddCanonicalSuffix(const string& suffix) {
+ canoncial_suffixes_.push_back(suffix);
+}
+
+void QuicCryptoClientConfig::PreferAesGcm() {
+ DCHECK(!aead.empty());
+ if (aead.size() <= 1) {
+ return;
+ }
+ QuicTagVector::iterator pos = find(aead.begin(), aead.end(), kAESG);
+ if (pos != aead.end()) {
+ aead.erase(pos);
+ aead.insert(aead.begin(), kAESG);
+ }
+}
+
+void QuicCryptoClientConfig::DisableEcdsa() {
+ disable_ecdsa_ = true;
+}
+
+void QuicCryptoClientConfig::PopulateFromCanonicalConfig(
+ const QuicServerId& server_id,
+ CachedState* server_state) {
+ DCHECK(server_state->IsEmpty());
+ size_t i = 0;
+ for (; i < canoncial_suffixes_.size(); ++i) {
+ if (EndsWith(server_id.host(), canoncial_suffixes_[i], false)) {
+ break;
+ }
+ }
+ if (i == canoncial_suffixes_.size())
+ return;
+
+ QuicServerId suffix_server_id(canoncial_suffixes_[i], server_id.port(),
+ server_id.is_https(),
+ server_id.privacy_mode());
+ if (!ContainsKey(canonical_server_map_, suffix_server_id)) {
+ // This is the first host we've seen which matches the suffix, so make it
+ // canonical.
+ canonical_server_map_[suffix_server_id] = server_id;
+ return;
+ }
+
+ const QuicServerId& canonical_server_id =
+ canonical_server_map_[suffix_server_id];
+ CachedState* canonical_state = cached_states_[canonical_server_id];
+ if (!canonical_state->proof_valid()) {
+ return;
+ }
+
+ // Update canonical version to point at the "most recent" entry.
+ canonical_server_map_[suffix_server_id] = server_id;
+
+ server_state->InitializeFrom(*canonical_state);
+}
+
} // namespace net
diff --git a/chromium/net/quic/crypto/quic_crypto_client_config.h b/chromium/net/quic/crypto/quic_crypto_client_config.h
index 30f4cf6f09e..8aa4527bd73 100644
--- a/chromium/net/quic/crypto/quic_crypto_client_config.h
+++ b/chromium/net/quic/crypto/quic_crypto_client_config.h
@@ -14,9 +14,17 @@
#include "net/base/net_export.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
namespace net {
+class ChannelIDKey;
+class ChannelIDSource;
+class CryptoHandshakeMessage;
+class ProofVerifier;
+class ProofVerifyDetails;
+class QuicRandom;
+
// QuicCryptoClientConfig contains crypto-related configuration settings for a
// client. Note that this object isn't thread-safe. It's designed to be used on
// a single thread at a time.
@@ -35,6 +43,9 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// cached server config has expired.
bool IsComplete(QuicWallTime now) const;
+ // IsEmpty returns true if |server_config_| is empty.
+ bool IsEmpty() const;
+
// GetServerConfig returns the parsed contents of |server_config|, or NULL
// if |server_config| is empty. The return value is owned by this object
// and is destroyed when this object is.
@@ -54,6 +65,9 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
void SetProof(const std::vector<std::string>& certs,
base::StringPiece signature);
+ // Clears all the data.
+ void Clear();
+
// Clears the certificate chain and signature and invalidates the proof.
void ClearProof();
@@ -86,8 +100,15 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// unchanged.
void InitializeFrom(const CachedState& other);
+ // Initializes this cached state based on the arguments provided.
+ // Returns false if there is a problem parsing the server config.
+ bool Initialize(base::StringPiece server_config,
+ base::StringPiece source_address_token,
+ const std::vector<std::string>& certs,
+ base::StringPiece signature,
+ QuicWallTime now);
+
private:
- std::string server_config_id_; // An opaque id from the server.
std::string server_config_; // A serialized handshake message.
std::string source_address_token_; // An opaque proof of IP ownership.
std::vector<std::string> certs_; // A list of certificates in leaf-first
@@ -115,18 +136,21 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// Sets the members to reasonable, default values.
void SetDefaults();
- // LookupOrCreate returns a CachedState for the given hostname. If no such
+ // LookupOrCreate returns a CachedState for the given |server_id|. If no such
// CachedState currently exists, it will be created and cached.
- CachedState* LookupOrCreate(const std::string& server_hostname);
+ CachedState* LookupOrCreate(const QuicServerId& server_id);
+
+ // Delete all CachedState objects from cached_states_.
+ void ClearCachedStates();
// FillInchoateClientHello sets |out| to be a CHLO message that elicits a
// source-address token or SCFG from a server. If |cached| is non-NULL, the
// source-address token will be taken from it. |out_params| is used in order
// to store the cached certs that were sent as hints to the server in
- // |out_params->cached_certs|. |preferred_version| is the version of the QUIC
- // protocol that this client chose to use initially. This allows the server to
- // detect downgrade attacks.
- void FillInchoateClientHello(const std::string& server_hostname,
+ // |out_params->cached_certs|. |preferred_version| is the version of the
+ // QUIC protocol that this client chose to use initially. This allows the
+ // server to detect downgrade attacks.
+ void FillInchoateClientHello(const QuicServerId& server_id,
const QuicVersion preferred_version,
const CachedState* cached,
QuicCryptoNegotiatedParameters* out_params,
@@ -134,20 +158,25 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// FillClientHello sets |out| to be a CHLO message based on the configuration
// of this object. This object must have cached enough information about
- // |server_hostname| in order to perform a handshake. This can be checked
+ // the server's hostname in order to perform a handshake. This can be checked
// with the |IsComplete| member of |CachedState|.
//
- // |clock| and |rand| are used to generate the nonce and |out_params| is
+ // |now| and |rand| are used to generate the nonce and |out_params| is
// filled with the results of the handshake that the server is expected to
// accept. |preferred_version| is the version of the QUIC protocol that this
// client chose to use initially. This allows the server to detect downgrade
// attacks.
- QuicErrorCode FillClientHello(const std::string& server_hostname,
- QuicGuid guid,
+ //
+ // If |channel_id_key| is not null, it is used to sign a secret value derived
+ // from the client and server's keys, and the Channel ID public key and the
+ // signature are placed in the CETV value of the CHLO.
+ QuicErrorCode FillClientHello(const QuicServerId& server_id,
+ QuicConnectionId connection_id,
const QuicVersion preferred_version,
const CachedState* cached,
QuicWallTime now,
QuicRandom* rand,
+ const ChannelIDKey* channel_id_key,
QuicCryptoNegotiatedParameters* out_params,
CryptoHandshakeMessage* out,
std::string* error_details) const;
@@ -173,7 +202,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// server. The contents of this list will be compared against the list of
// versions provided in the VER tag of the server hello.
QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
- QuicGuid guid,
+ QuicConnectionId connection_id,
const QuicVersionVector& negotiated_versions,
CachedState* cached,
QuicCryptoNegotiatedParameters* out_params,
@@ -187,28 +216,73 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// the server.
void SetProofVerifier(ProofVerifier* verifier);
- ChannelIDSigner* channel_id_signer() const;
+ ChannelIDSource* channel_id_source() const;
- // SetChannelIDSigner sets a ChannelIDSigner that will be called when the
- // server supports channel IDs to sign a message proving possession of the
- // given ChannelID. This object takes ownership of |signer|.
- void SetChannelIDSigner(ChannelIDSigner* signer);
+ // SetChannelIDSource sets a ChannelIDSource that will be called, when the
+ // server supports channel IDs, to obtain a channel ID for signing a message
+ // proving possession of the channel ID. This object takes ownership of
+ // |source|.
+ void SetChannelIDSource(ChannelIDSource* source);
// Initialize the CachedState from |canonical_crypto_config| for the
- // |canonical_server_hostname| as the initial CachedState for
- // |server_hostname|. We will copy config data only if
- // |canonical_crypto_config| has valid proof.
- void InitializeFrom(const std::string& server_hostname,
- const std::string& canonical_server_hostname,
+ // |canonical_server_id| as the initial CachedState for |server_id|. We will
+ // copy config data only if |canonical_crypto_config| has valid proof.
+ void InitializeFrom(const QuicServerId& server_id,
+ const QuicServerId& canonical_server_id,
QuicCryptoClientConfig* canonical_crypto_config);
+ // Adds |suffix| as a domain suffix for which the server's crypto config
+ // is expected to be shared among servers with the domain suffix. If a server
+ // matches this suffix, then the server config from another server with the
+ // suffix will be used to initialize the cached state for this server.
+ void AddCanonicalSuffix(const std::string& suffix);
+
+ // Prefers AES-GCM (kAESG) over other AEAD algorithms. Call this method if
+ // the CPU has hardware acceleration for AES-GCM. This method can only be
+ // called after SetDefaults().
+ void PreferAesGcm();
+
+ // Disables the use of ECDSA for proof verification.
+ // Call this method on platforms that do not support ECDSA.
+ // TODO(rch): remove this method when we drop support for Windows XP.
+ void DisableEcdsa();
+
+ // Saves the |user_agent_id| that will be passed in QUIC's CHLO message.
+ void set_user_agent_id(const std::string& user_agent_id) {
+ user_agent_id_ = user_agent_id;
+ }
+
private:
- // cached_states_ maps from the server hostname to the cached information
- // about that server.
- std::map<std::string, CachedState*> cached_states_;
+ typedef std::map<QuicServerId, CachedState*> CachedStateMap;
+
+ // If the suffix of the hostname in |server_id| is in |canoncial_suffixes_|,
+ // then populate |cached| with the canonical cached state from
+ // |canonical_server_map_| for that suffix.
+ void PopulateFromCanonicalConfig(const QuicServerId& server_id,
+ CachedState* cached);
+
+ // cached_states_ maps from the server_id to the cached information about
+ // that server.
+ CachedStateMap cached_states_;
+
+ // Contains a map of servers which could share the same server config. Map
+ // from a canonical host suffix/port/scheme to a representative server with
+ // the canonical suffix, which has a plausible set of initial certificates
+ // (or at least server public key).
+ std::map<QuicServerId, QuicServerId> canonical_server_map_;
+
+ // Contains list of suffixes (for exmaple ".c.youtube.com",
+ // ".googlevideo.com") of canoncial hostnames.
+ std::vector<std::string> canoncial_suffixes_;
scoped_ptr<ProofVerifier> proof_verifier_;
- scoped_ptr<ChannelIDSigner> channel_id_signer_;
+ scoped_ptr<ChannelIDSource> channel_id_source_;
+
+ // True if ECDSA should be disabled.
+ bool disable_ecdsa_;
+
+ // The |user_agent_id_| passed in QUIC's CHLO message.
+ std::string user_agent_id_;
DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientConfig);
};
diff --git a/chromium/net/quic/crypto/quic_crypto_client_config_test.cc b/chromium/net/quic/crypto/quic_crypto_client_config_test.cc
index 2893372dd1a..fdce91c1fc7 100644
--- a/chromium/net/quic/crypto/quic_crypto_client_config_test.cc
+++ b/chromium/net/quic/crypto/quic_crypto_client_config_test.cc
@@ -4,30 +4,131 @@
#include "net/quic/crypto/quic_crypto_client_config.h"
+#include "net/quic/crypto/proof_verifier.h"
+#include "net/quic/quic_server_id.h"
+#include "net/quic/test_tools/mock_random.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
using std::string;
+using std::vector;
namespace net {
namespace test {
+TEST(QuicCryptoClientConfigTest, CachedState_IsEmpty) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_TRUE(state.IsEmpty());
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_IsComplete) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_FALSE(state.IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_GenerationCounter) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_EQ(0u, state.generation_counter());
+ state.SetProofInvalid();
+ EXPECT_EQ(1u, state.generation_counter());
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_SetProofVerifyDetails) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_TRUE(state.proof_verify_details() == NULL);
+ ProofVerifyDetails* details = new ProofVerifyDetails;
+ state.SetProofVerifyDetails(details);
+ EXPECT_EQ(details, state.proof_verify_details());
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_InitializeFrom) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig::CachedState other;
+ state.set_source_address_token("TOKEN");
+ // TODO(rch): Populate other fields of |state|.
+ other.InitializeFrom(state);
+ EXPECT_EQ(state.server_config(), other.server_config());
+ EXPECT_EQ(state.source_address_token(), other.source_address_token());
+ EXPECT_EQ(state.certs(), other.certs());
+ EXPECT_EQ(1u, other.generation_counter());
+}
+
TEST(QuicCryptoClientConfigTest, InchoateChlo) {
QuicCryptoClientConfig::CachedState state;
QuicCryptoClientConfig config;
QuicCryptoNegotiatedParameters params;
CryptoHandshakeMessage msg;
- config.FillInchoateClientHello("www.google.com", QuicVersionMax(), &state,
+ QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED);
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state,
&params, &msg);
QuicTag cver;
EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kVER, &cver));
EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver);
+}
+
+TEST(QuicCryptoClientConfigTest, PreferAesGcm) {
+ QuicCryptoClientConfig config;
+ config.SetDefaults();
+ if (config.aead.size() > 1)
+ EXPECT_NE(kAESG, config.aead[0]);
+ config.PreferAesGcm();
+ EXPECT_EQ(kAESG, config.aead[0]);
+}
+
+TEST(QuicCryptoClientConfigTest, InchoateChloSecure) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config;
+ QuicCryptoNegotiatedParameters params;
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED);
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state,
+ &params, &msg);
+
+ QuicTag pdmd;
+ EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd));
+ EXPECT_EQ(kX509, pdmd);
+}
+
+TEST(QuicCryptoClientConfigTest, InchoateChloSecureNoEcdsa) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config;
+ config.DisableEcdsa();
+ QuicCryptoNegotiatedParameters params;
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED);
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state,
+ &params, &msg);
+
+ QuicTag pdmd;
+ EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd));
+ EXPECT_EQ(kX59R, pdmd);
+}
+
+TEST(QuicCryptoClientConfigTest, FillClientHello) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config;
+ QuicCryptoNegotiatedParameters params;
+ QuicConnectionId kConnectionId = 1234;
+ string error_details;
+ MockRandom rand;
+ CryptoHandshakeMessage chlo;
+ QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED);
+ config.FillClientHello(server_id,
+ kConnectionId,
+ QuicVersionMax(),
+ &state,
+ QuicWallTime::Zero(),
+ &rand,
+ NULL, // channel_id_key
+ &params,
+ &chlo,
+ &error_details);
- // TODO(rch): Remove once we remove QUIC_VERSION_12.
- uint16 vers;
- EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint16(kVERS, &vers));
- EXPECT_EQ(0u, vers);
+ // Verify that certain QuicTags have been set correctly in the CHLO.
+ QuicTag cver;
+ EXPECT_EQ(QUIC_NO_ERROR, chlo.GetUint32(kVER, &cver));
+ EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver);
}
TEST(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) {
@@ -55,5 +156,104 @@ TEST(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) {
EXPECT_EQ("Downgrade attack detected", error);
}
+TEST(QuicCryptoClientConfigTest, InitializeFrom) {
+ QuicCryptoClientConfig config;
+ QuicServerId canonical_server_id("www.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_server_id);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ QuicServerId other_server_id("mail.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ config.InitializeFrom(other_server_id, canonical_server_id, &config);
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(other_server_id);
+
+ EXPECT_EQ(state->server_config(), other->server_config());
+ EXPECT_EQ(state->source_address_token(), other->source_address_token());
+ EXPECT_EQ(state->certs(), other->certs());
+ EXPECT_EQ(1u, other->generation_counter());
+}
+
+TEST(QuicCryptoClientConfigTest, Canonical) {
+ QuicCryptoClientConfig config;
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_id1("www.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId canonical_id2("mail.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_id1);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(canonical_id2);
+
+ EXPECT_TRUE(state->IsEmpty());
+ EXPECT_EQ(state->server_config(), other->server_config());
+ EXPECT_EQ(state->source_address_token(), other->source_address_token());
+ EXPECT_EQ(state->certs(), other->certs());
+ EXPECT_EQ(1u, other->generation_counter());
+
+ QuicServerId different_id("mail.google.org", 80, false,
+ PRIVACY_MODE_DISABLED);
+ EXPECT_TRUE(config.LookupOrCreate(different_id)->IsEmpty());
+}
+
+TEST(QuicCryptoClientConfigTest, CanonicalNotUsedIfNotValid) {
+ QuicCryptoClientConfig config;
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_id1("www.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId canonical_id2("mail.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_id1);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+
+ // Do not set the proof as valid, and check that it is not used
+ // as a canonical entry.
+ EXPECT_TRUE(config.LookupOrCreate(canonical_id2)->IsEmpty());
+}
+
+TEST(QuicCryptoClientConfigTest, ClearCachedStates) {
+ QuicCryptoClientConfig config;
+ QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state = config.LookupOrCreate(server_id);
+ // TODO(rch): Populate other fields of |state|.
+ vector<string> certs(1);
+ certs[0] = "Hello Cert";
+ state->SetProof(certs, "signature");
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+ EXPECT_EQ(1u, state->generation_counter());
+
+ // Verify LookupOrCreate returns the same data.
+ QuicCryptoClientConfig::CachedState* other = config.LookupOrCreate(server_id);
+
+ EXPECT_EQ(state, other);
+ EXPECT_EQ(1u, other->generation_counter());
+
+ // Clear the cached states.
+ config.ClearCachedStates();
+
+ // Verify LookupOrCreate doesn't have any data.
+ QuicCryptoClientConfig::CachedState* cleared_cache =
+ config.LookupOrCreate(server_id);
+
+ EXPECT_EQ(state, cleared_cache);
+ EXPECT_FALSE(cleared_cache->proof_valid());
+ EXPECT_TRUE(cleared_cache->server_config().empty());
+ EXPECT_TRUE(cleared_cache->certs().empty());
+ EXPECT_TRUE(cleared_cache->signature().empty());
+ EXPECT_EQ(2u, cleared_cache->generation_counter());
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/crypto/quic_crypto_server_config.cc b/chromium/net/quic/crypto/quic_crypto_server_config.cc
index 31651dbf633..8e48fbb32ca 100644
--- a/chromium/net/quic/crypto/quic_crypto_server_config.cc
+++ b/chromium/net/quic/crypto/quic_crypto_server_config.cc
@@ -15,6 +15,7 @@
#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
#include "net/quic/crypto/cert_compressor.h"
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
#include "net/quic/crypto/channel_id.h"
#include "net/quic/crypto/crypto_framer.h"
#include "net/quic/crypto/crypto_server_config_protobuf.h"
@@ -32,17 +33,33 @@
#include "net/quic/crypto/strike_register.h"
#include "net/quic/crypto/strike_register_client.h"
#include "net/quic/quic_clock.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_socket_address_coder.h"
#include "net/quic/quic_utils.h"
using base::StringPiece;
using crypto::SecureHash;
using std::map;
+using std::sort;
using std::string;
using std::vector;
namespace net {
+namespace {
+
+string DeriveSourceAddressTokenKey(StringPiece source_address_token_secret) {
+ crypto::HKDF hkdf(source_address_token_secret,
+ StringPiece() /* no salt */,
+ "QUIC source address token key",
+ CryptoSecretBoxer::GetKeySize(),
+ 0 /* no fixed IV needed */);
+ return hkdf.server_write_key().as_string();
+}
+
+} // namespace
+
// ClientHelloInfo contains information about a client hello message that is
// only kept for as long as it's being processed.
struct ClientHelloInfo {
@@ -64,6 +81,10 @@ struct ClientHelloInfo {
StringPiece sni;
StringPiece client_nonce;
StringPiece server_nonce;
+ StringPiece user_agent_id;
+
+ // Errors from EvaluateClientHello.
+ vector<uint32> reject_reasons;
};
struct ValidateClientHelloResultCallback::Result {
@@ -89,10 +110,8 @@ class ValidateClientHelloHelper {
}
~ValidateClientHelloHelper() {
- if (done_cb_ != NULL) {
- LOG(DFATAL) <<
- "Deleting ValidateClientHelloHelper with a pending callback.";
- }
+ LOG_IF(DFATAL, done_cb_ != NULL)
+ << "Deleting ValidateClientHelloHelper with a pending callback.";
}
void ValidationComplete(QuicErrorCode error_code, const char* error_details) {
@@ -108,9 +127,7 @@ class ValidateClientHelloHelper {
private:
void DetachCallback() {
- if (done_cb_ == NULL) {
- LOG(DFATAL) << "Callback already detached.";
- }
+ LOG_IF(DFATAL, done_cb_ == NULL) << "Callback already detached.";
done_cb_ = NULL;
}
@@ -133,6 +150,12 @@ class VerifyNonceIsValidAndUniqueCallback
virtual void RunImpl(bool nonce_is_valid_and_unique) OVERRIDE {
DVLOG(1) << "Using client nonce, unique: " << nonce_is_valid_and_unique;
result_->info.unique = nonce_is_valid_and_unique;
+ // TODO(rtenneti): Implement capturing of error from strike register.
+ // Temporarily treat them as CLIENT_NONCE_UNKNOWN_FAILURE.
+ if (!nonce_is_valid_and_unique) {
+ result_->info.reject_reasons.push_back(
+ static_cast<uint32>(CLIENT_NONCE_UNKNOWN_FAILURE));
+ }
done_cb_->Run(result_);
}
@@ -146,6 +169,11 @@ class VerifyNonceIsValidAndUniqueCallback
// static
const char QuicCryptoServerConfig::TESTING[] = "secret string for testing";
+PrimaryConfigChangedCallback::PrimaryConfigChangedCallback() {
+}
+
+PrimaryConfigChangedCallback::~PrimaryConfigChangedCallback() {
+}
ValidateClientHelloResultCallback::ValidateClientHelloResultCallback() {
}
@@ -179,11 +207,8 @@ QuicCryptoServerConfig::QuicCryptoServerConfig(
source_address_token_lifetime_secs_(86400),
server_nonce_strike_register_max_entries_(1 << 10),
server_nonce_strike_register_window_secs_(120) {
- crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */,
- "QUIC source address token key",
- CryptoSecretBoxer::GetKeySize(),
- 0 /* no fixed IV needed */);
- source_address_token_boxer_.SetKey(hkdf.server_write_key());
+ default_source_address_token_boxer_.SetKey(
+ DeriveSourceAddressTokenKey(source_address_token_secret));
// Generate a random key and orbit for server nonces.
rand->RandBytes(server_nonce_orbit_, sizeof(server_nonce_orbit_));
@@ -200,7 +225,7 @@ QuicCryptoServerConfig::~QuicCryptoServerConfig() {
}
// static
-QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig(
+QuicServerConfigProtobuf* QuicCryptoServerConfig::GenerateConfig(
QuicRandom* rand,
const QuicClock* clock,
const ConfigOptions& options) {
@@ -239,9 +264,11 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig(
} else {
msg.SetTaglist(kKEXS, kC255, 0);
}
- msg.SetTaglist(kAEAD, kAESG, 0);
- // TODO(rch): Remove once we remove QUIC_VERSION_12.
- msg.SetValue(kVERS, static_cast<uint16>(0));
+ if (ChaCha20Poly1305Encrypter::IsSupported()) {
+ msg.SetTaglist(kAEAD, kAESG, kCC12, 0);
+ } else {
+ msg.SetTaglist(kAEAD, kAESG, 0);
+ }
msg.SetStringPiece(kPUBS, encoded_public_values);
if (options.expiry_time.IsZero()) {
@@ -331,6 +358,7 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig(
configs_[config->id] = config;
SelectNewPrimaryConfig(now);
DCHECK(primary_config_.get());
+ DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
}
return msg.release();
@@ -341,14 +369,14 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig(
const QuicClock* clock,
const ConfigOptions& options) {
scoped_ptr<QuicServerConfigProtobuf> config(
- DefaultConfig(rand, clock, options));
+ GenerateConfig(rand, clock, options));
return AddConfig(config.get(), clock->WallNow());
}
bool QuicCryptoServerConfig::SetConfigs(
const vector<QuicServerConfigProtobuf*>& protobufs,
const QuicWallTime now) {
- vector<scoped_refptr<Config> > new_configs;
+ vector<scoped_refptr<Config> > parsed_configs;
bool ok = true;
for (vector<QuicServerConfigProtobuf*>::const_iterator i = protobufs.begin();
@@ -358,95 +386,109 @@ bool QuicCryptoServerConfig::SetConfigs(
ok = false;
break;
}
- new_configs.push_back(config);
+
+ parsed_configs.push_back(config);
+ }
+
+ if (parsed_configs.empty()) {
+ LOG(WARNING) << "New config list is empty.";
+ ok = false;
}
if (!ok) {
LOG(WARNING) << "Rejecting QUIC configs because of above errors";
} else {
- base::AutoLock locked(configs_lock_);
- typedef ConfigMap::iterator ConfigMapIterator;
- vector<ConfigMapIterator> to_delete;
-
- DCHECK_EQ(protobufs.size(), new_configs.size());
-
- // First, look for any configs that have been removed.
- for (ConfigMapIterator i = configs_.begin();
- i != configs_.end(); ++i) {
- const scoped_refptr<Config> old_config = i->second;
- bool found = false;
-
- for (vector<scoped_refptr<Config> >::const_iterator j =
- new_configs.begin();
- j != new_configs.end(); ++j) {
- if ((*j)->id == old_config->id) {
- found = true;
- break;
- }
- }
+ VLOG(1) << "Updating configs:";
- if (!found) {
- // We cannot remove the primary config. This has probably happened
- // because our source of config information failed for a time and we're
- // suddenly seeing a jump in time. No matter - we'll configure a new
- // primary config and then we'll be able to delete it next time.
- if (!old_config->is_primary) {
- to_delete.push_back(i);
- }
- }
- }
+ base::AutoLock locked(configs_lock_);
+ ConfigMap new_configs;
- for (vector<ConfigMapIterator>::const_iterator i = to_delete.begin();
- i != to_delete.end(); ++i) {
- configs_.erase(*i);
- }
+ for (vector<scoped_refptr<Config> >::const_iterator i =
+ parsed_configs.begin();
+ i != parsed_configs.end(); ++i) {
+ scoped_refptr<Config> config = *i;
- // Find any configs that need to be added.
- for (vector<scoped_refptr<Config> >::const_iterator i = new_configs.begin();
- i != new_configs.end(); ++i) {
- const scoped_refptr<Config> new_config = *i;
- if (configs_.find(new_config->id) != configs_.end()) {
- continue;
+ ConfigMap::iterator it = configs_.find(config->id);
+ if (it != configs_.end()) {
+ VLOG(1)
+ << "Keeping scid: " << base::HexEncode(
+ config->id.data(), config->id.size())
+ << " orbit: " << base::HexEncode(
+ reinterpret_cast<const char *>(config->orbit), kOrbitSize)
+ << " new primary_time " << config->primary_time.ToUNIXSeconds()
+ << " old primary_time " << it->second->primary_time.ToUNIXSeconds()
+ << " new priority " << config->priority
+ << " old priority " << it->second->priority;
+ // Update primary_time and priority.
+ it->second->primary_time = config->primary_time;
+ it->second->priority = config->priority;
+ new_configs.insert(*it);
+ } else {
+ VLOG(1) << "Adding scid: " << base::HexEncode(
+ config->id.data(), config->id.size())
+ << " orbit: " << base::HexEncode(
+ reinterpret_cast<const char *>(config->orbit), kOrbitSize)
+ << " primary_time " << config->primary_time.ToUNIXSeconds()
+ << " priority " << config->priority;
+ new_configs.insert(make_pair(config->id, config));
}
-
- configs_[new_config->id] = new_config;
}
+ configs_.swap(new_configs);
SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_);
+ DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
}
return ok;
}
+void QuicCryptoServerConfig::GetConfigIds(vector<string>* scids) const {
+ base::AutoLock locked(configs_lock_);
+ for (ConfigMap::const_iterator it = configs_.begin();
+ it != configs_.end(); ++it) {
+ scids->push_back(it->first);
+ }
+}
+
void QuicCryptoServerConfig::ValidateClientHello(
const CryptoHandshakeMessage& client_hello,
IPEndPoint client_ip,
const QuicClock* clock,
ValidateClientHelloResultCallback* done_cb) const {
const QuicWallTime now(clock->WallNow());
+
ValidateClientHelloResultCallback::Result* result =
new ValidateClientHelloResultCallback::Result(
client_hello, client_ip, now);
+ StringPiece requested_scid;
+ client_hello.GetStringPiece(kSCID, &requested_scid);
+
uint8 primary_orbit[kOrbitSize];
+ scoped_refptr<Config> requested_config;
{
base::AutoLock locked(configs_lock_);
- if (!primary_config_) {
+ if (!primary_config_.get()) {
result->error_code = QUIC_CRYPTO_INTERNAL_ERROR;
result->error_details = "No configurations loaded";
} else {
if (!next_config_promotion_time_.IsZero() &&
next_config_promotion_time_.IsAfter(now)) {
SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_);
+ DCHECK(configs_.find(primary_config_->id)->second == primary_config_);
}
memcpy(primary_orbit, primary_config_->orbit, sizeof(primary_orbit));
}
+
+ requested_config = GetConfigWithScid(requested_scid);
}
if (result->error_code == QUIC_NO_ERROR) {
- EvaluateClientHello(primary_orbit, result, done_cb);
+ EvaluateClientHello(primary_orbit, requested_config, result, done_cb);
} else {
done_cb->Run(result);
}
@@ -454,8 +496,8 @@ void QuicCryptoServerConfig::ValidateClientHello(
QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
const ValidateClientHelloResultCallback::Result& validate_chlo_result,
- QuicGuid guid,
- IPEndPoint client_ip,
+ QuicConnectionId connection_id,
+ IPEndPoint client_address,
QuicVersion version,
const QuicVersionVector& supported_versions,
const QuicClock* clock,
@@ -474,18 +516,19 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
// case, we need to make sure that we actually do not support this version
// and that it wasn't a downgrade attack.
QuicTag client_version_tag;
- // TODO(rch): Make this check mandatory when we remove QUIC_VERSION_12.
- if (client_hello.GetUint32(kVER, &client_version_tag) == QUIC_NO_ERROR) {
- QuicVersion client_version = QuicTagToQuicVersion(client_version_tag);
- if (client_version != version) {
- // Just because client_version is a valid version enum doesn't mean that
- // this server actually supports that version, so we check to see if
- // it's actually in the supported versions list.
- for (size_t i = 0; i < supported_versions.size(); ++i) {
- if (client_version == supported_versions[i]) {
- *error_details = "Downgrade attack detected";
- return QUIC_VERSION_NEGOTIATION_MISMATCH;
- }
+ if (client_hello.GetUint32(kVER, &client_version_tag) != QUIC_NO_ERROR) {
+ *error_details = "client hello missing version list";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ QuicVersion client_version = QuicTagToQuicVersion(client_version_tag);
+ if (client_version != version) {
+ // Just because client_version is a valid version enum doesn't mean that
+ // this server actually supports that version, so we check to see if
+ // it's actually in the supported versions list.
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ if (client_version == supported_versions[i]) {
+ *error_details = "Downgrade attack detected";
+ return QUIC_VERSION_NEGOTIATION_MISMATCH;
}
}
}
@@ -507,19 +550,16 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
if (!next_config_promotion_time_.IsZero() &&
next_config_promotion_time_.IsAfter(now)) {
SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_);
+ DCHECK(configs_.find(primary_config_->id)->second == primary_config_);
}
+ // We'll use the config that the client requested in order to do
+ // key-agreement. Otherwise we'll give it a copy of |primary_config_|
+ // to use.
primary_config = primary_config_;
- if (!requested_scid.empty()) {
- ConfigMap::const_iterator it = configs_.find(requested_scid.as_string());
- if (it != configs_.end()) {
- // We'll use the config that the client requested in order to do
- // key-agreement. Otherwise we'll give it a copy of |primary_config_|
- // to use.
- requested_config = it->second;
- }
- }
+ requested_config = GetConfigWithScid(requested_scid);
}
if (validate_chlo_result.error_code != QUIC_NO_ERROR) {
@@ -533,7 +573,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
!info.client_nonce_well_formed ||
!info.unique ||
!requested_config.get()) {
- BuildRejection(primary_config.get(), client_hello, info, rand, out);
+ BuildRejection(*primary_config, client_hello, info, rand, out);
return QUIC_NO_ERROR;
}
@@ -585,9 +625,10 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
string hkdf_suffix;
const QuicData& client_hello_serialized = client_hello.GetSerialized();
- hkdf_suffix.reserve(sizeof(guid) + client_hello_serialized.length() +
+ hkdf_suffix.reserve(sizeof(connection_id) + client_hello_serialized.length() +
requested_config->serialized.size());
- hkdf_suffix.append(reinterpret_cast<char*>(&guid), sizeof(guid));
+ hkdf_suffix.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
hkdf_suffix.append(client_hello_serialized.data(),
client_hello_serialized.length());
hkdf_suffix.append(requested_config->serialized);
@@ -603,7 +644,8 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
string hkdf_input;
hkdf_input.append(QuicCryptoConfig::kCETVLabel,
strlen(QuicCryptoConfig::kCETVLabel) + 1);
- hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid));
+ hkdf_input.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
hkdf_input.append(client_hello_serialized.data(),
client_hello_serialized.length());
hkdf_input.append(requested_config->serialized);
@@ -698,65 +740,92 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
}
out->SetVector(kVER, supported_version_tags);
out->SetStringPiece(kSourceAddressTokenTag,
- NewSourceAddressToken(client_ip, rand, info.now));
+ NewSourceAddressToken(
+ *requested_config,
+ client_address, rand,
+ info.now));
+ QuicSocketAddressCoder address_coder(client_address);
+ out->SetStringPiece(kCADR, address_coder.Encode());
out->SetStringPiece(kPUBS, forward_secure_public_value);
+
return QUIC_NO_ERROR;
}
+scoped_refptr<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfig::GetConfigWithScid(StringPiece requested_scid) const {
+ // In Chromium, we will dead lock if the lock is held by the current thread.
+ // Chromium doesn't have AssertReaderHeld API call.
+ // configs_lock_.AssertReaderHeld();
+
+ if (!requested_scid.empty()) {
+ ConfigMap::const_iterator it = configs_.find(requested_scid.as_string());
+ if (it != configs_.end()) {
+ // We'll use the config that the client requested in order to do
+ // key-agreement.
+ return scoped_refptr<Config>(it->second);
+ }
+ }
+
+ return scoped_refptr<Config>();
+}
+
// ConfigPrimaryTimeLessThan is a comparator that implements "less than" for
// Config's based on their primary_time.
// static
bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan(
const scoped_refptr<Config>& a,
const scoped_refptr<Config>& b) {
- return a->primary_time.IsBefore(b->primary_time);
+ if (a->primary_time.IsBefore(b->primary_time) ||
+ b->primary_time.IsBefore(a->primary_time)) {
+ // Primary times differ.
+ return a->primary_time.IsBefore(b->primary_time);
+ } else if (a->priority != b->priority) {
+ // Primary times are equal, sort backwards by priority.
+ return a->priority < b->priority;
+ } else {
+ // Primary times and priorities are equal, sort by config id.
+ return a->id < b->id;
+ }
}
void QuicCryptoServerConfig::SelectNewPrimaryConfig(
const QuicWallTime now) const {
vector<scoped_refptr<Config> > configs;
configs.reserve(configs_.size());
- scoped_refptr<Config> first_config = NULL;
for (ConfigMap::const_iterator it = configs_.begin();
it != configs_.end(); ++it) {
- const scoped_refptr<Config> config(it->second);
- if (!first_config.get()) {
- first_config = config;
- }
- if (config->primary_time.IsZero()) {
- continue;
- }
+ // TODO(avd) Exclude expired configs?
configs.push_back(it->second);
}
if (configs.empty()) {
- // Tests don't set |primary_time_|. For that case we promote the first
- // Config and leave it as primary forever.
- if (!primary_config_.get() && first_config.get()) {
- primary_config_ = first_config;
- primary_config_->is_primary = true;
+ if (primary_config_.get()) {
+ LOG(DFATAL) << "No valid QUIC server config. Keeping the current config.";
+ } else {
+ LOG(DFATAL) << "No valid QUIC server config.";
}
return;
}
- std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan);
+ sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan);
+
+ Config* best_candidate = configs[0];
for (size_t i = 0; i < configs.size(); ++i) {
const scoped_refptr<Config> config(configs[i]);
-
if (!config->primary_time.IsAfter(now)) {
+ if (config->primary_time.IsAfter(best_candidate->primary_time)) {
+ best_candidate = config;
+ }
continue;
}
// This is the first config with a primary_time in the future. Thus the
// previous Config should be the primary and this one should determine the
// next_config_promotion_time_.
- scoped_refptr<Config> new_primary;
+ scoped_refptr<Config> new_primary(best_candidate);
if (i == 0) {
- // There was no previous Config, so this will have to be primary.
- new_primary = config;
-
// We need the primary_time of the next config.
if (configs.size() > 1) {
next_config_promotion_time_ = configs[1]->primary_time;
@@ -764,7 +833,6 @@ void QuicCryptoServerConfig::SelectNewPrimaryConfig(
next_config_promotion_time_ = QuicWallTime::Zero();
}
} else {
- new_primary = configs[i - 1];
next_config_promotion_time_ = config->primary_time;
}
@@ -773,23 +841,40 @@ void QuicCryptoServerConfig::SelectNewPrimaryConfig(
}
primary_config_ = new_primary;
new_primary->is_primary = true;
+ DVLOG(1) << "New primary config. orbit: "
+ << base::HexEncode(
+ reinterpret_cast<const char*>(primary_config_->orbit),
+ kOrbitSize);
+ if (primary_config_changed_cb_.get() != NULL) {
+ primary_config_changed_cb_->Run(primary_config_->id);
+ }
return;
}
// All config's primary times are in the past. We should make the most recent
- // primary.
- scoped_refptr<Config> new_primary = configs[configs.size() - 1];
+ // and highest priority candidate primary.
+ scoped_refptr<Config> new_primary(best_candidate);
if (primary_config_.get()) {
primary_config_->is_primary = false;
}
primary_config_ = new_primary;
new_primary->is_primary = true;
+ DVLOG(1) << "New primary config. orbit: "
+ << base::HexEncode(
+ reinterpret_cast<const char*>(primary_config_->orbit),
+ kOrbitSize)
+ << " scid: " << base::HexEncode(primary_config_->id.data(),
+ primary_config_->id.size());
next_config_promotion_time_ = QuicWallTime::Zero();
+ if (primary_config_changed_cb_.get() != NULL) {
+ primary_config_changed_cb_->Run(primary_config_->id);
+ }
}
void QuicCryptoServerConfig::EvaluateClientHello(
const uint8* primary_orbit,
+ scoped_refptr<Config> requested_config,
ValidateClientHelloResultCallback::Result* client_hello_state,
ValidateClientHelloResultCallback* done_cb) const {
ValidateClientHelloHelper helper(client_hello_state, done_cb);
@@ -798,7 +883,7 @@ void QuicCryptoServerConfig::EvaluateClientHello(
client_hello_state->client_hello;
ClientHelloInfo* info = &(client_hello_state->info);
- if (client_hello.size() < kClientHelloMinimumSizeOld) {
+ if (client_hello.size() < kClientHelloMinimumSize) {
helper.ValidationComplete(QUIC_CRYPTO_INVALID_VALUE_LENGTH,
"Client hello too small");
return;
@@ -811,28 +896,67 @@ void QuicCryptoServerConfig::EvaluateClientHello(
return;
}
+ client_hello.GetStringPiece(kUAID, &info->user_agent_id);
+
+ if (!requested_config.get()) {
+ StringPiece requested_scid;
+ if (client_hello.GetStringPiece(kSCID, &requested_scid)) {
+ info->reject_reasons.push_back(
+ static_cast<uint32>(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE));
+ } else {
+ info->reject_reasons.push_back(
+ static_cast<uint32>(SERVER_CONFIG_INCHOATE_HELLO_FAILURE));
+ }
+ // No server config with the requested ID.
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+
+ HandshakeFailureReason source_address_token_error;
StringPiece srct;
- if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct) &&
- ValidateSourceAddressToken(srct, info->client_ip, info->now)) {
- info->valid_source_address_token = true;
+ if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) {
+ source_address_token_error =
+ ValidateSourceAddressToken(*requested_config,
+ srct,
+ info->client_ip,
+ info->now);
+ info->valid_source_address_token =
+ (source_address_token_error == HANDSHAKE_OK);
} else {
+ source_address_token_error = SOURCE_ADDRESS_TOKEN_INVALID_FAILURE;
+ }
+
+ bool found_error = false;
+ if (source_address_token_error != HANDSHAKE_OK) {
+ info->reject_reasons.push_back(
+ static_cast<uint32>(source_address_token_error));
// No valid source address token.
- helper.ValidationComplete(QUIC_NO_ERROR, "");
- return;
+ if (FLAGS_use_early_return_when_verifying_chlo) {
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+ found_error = true;
}
if (client_hello.GetStringPiece(kNONC, &info->client_nonce) &&
info->client_nonce.size() == kNonceSize) {
info->client_nonce_well_formed = true;
} else {
+ info->reject_reasons.push_back(
+ static_cast<uint32>(CLIENT_NONCE_INVALID_FAILURE));
// Invalid client nonce.
DVLOG(1) << "Invalid client nonce.";
- helper.ValidationComplete(QUIC_NO_ERROR, "");
- return;
+ if (FLAGS_use_early_return_when_verifying_chlo) {
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+ found_error = true;
}
if (!replay_protection_) {
- info->unique = true;
+ if (!found_error) {
+ info->unique = true;
+ }
DVLOG(1) << "No replay protection.";
helper.ValidationComplete(QUIC_NO_ERROR, "");
return;
@@ -841,27 +965,45 @@ void QuicCryptoServerConfig::EvaluateClientHello(
client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce);
if (!info->server_nonce.empty()) {
// If the server nonce is present, use it to establish uniqueness.
- info->unique = ValidateServerNonce(info->server_nonce, info->now);
+ HandshakeFailureReason server_nonce_error =
+ ValidateServerNonce(info->server_nonce, info->now);
+ if (server_nonce_error == HANDSHAKE_OK) {
+ info->unique = true;
+ } else {
+ info->reject_reasons.push_back(static_cast<uint32>(server_nonce_error));
+ info->unique = false;
+ }
DVLOG(1) << "Using server nonce, unique: " << info->unique;
helper.ValidationComplete(QUIC_NO_ERROR, "");
return;
}
+ // We want to contact strike register if there are no errors because it is
+ // a RPC call and is expensive.
+ if (found_error) {
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+
// Use the client nonce to establish uniqueness.
- base::AutoLock locked(strike_register_client_lock_);
+ StrikeRegisterClient* strike_register_client;
+ {
+ base::AutoLock locked(strike_register_client_lock_);
- if (strike_register_client_.get() == NULL) {
- strike_register_client_.reset(new LocalStrikeRegisterClient(
- strike_register_max_entries_,
- static_cast<uint32>(info->now.ToUNIXSeconds()),
- strike_register_window_secs_,
- primary_orbit,
- strike_register_no_startup_period_ ?
- StrikeRegister::NO_STARTUP_PERIOD_NEEDED :
- StrikeRegister::DENY_REQUESTS_AT_STARTUP));
+ if (strike_register_client_.get() == NULL) {
+ strike_register_client_.reset(new LocalStrikeRegisterClient(
+ strike_register_max_entries_,
+ static_cast<uint32>(info->now.ToUNIXSeconds()),
+ strike_register_window_secs_,
+ primary_orbit,
+ strike_register_no_startup_period_ ?
+ StrikeRegister::NO_STARTUP_PERIOD_NEEDED :
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP));
+ }
+ strike_register_client = strike_register_client_.get();
}
- strike_register_client_->VerifyNonceIsValidAndUnique(
+ strike_register_client->VerifyNonceIsValidAndUnique(
info->client_nonce,
info->now,
new VerifyNonceIsValidAndUniqueCallback(client_hello_state, done_cb));
@@ -869,19 +1011,29 @@ void QuicCryptoServerConfig::EvaluateClientHello(
}
void QuicCryptoServerConfig::BuildRejection(
- const scoped_refptr<Config>& config,
+ const Config& config,
const CryptoHandshakeMessage& client_hello,
const ClientHelloInfo& info,
QuicRandom* rand,
CryptoHandshakeMessage* out) const {
out->set_tag(kREJ);
- out->SetStringPiece(kSCFG, config->serialized);
+ out->SetStringPiece(kSCFG, config.serialized);
out->SetStringPiece(kSourceAddressTokenTag,
- NewSourceAddressToken(info.client_ip, rand, info.now));
+ NewSourceAddressToken(
+ config,
+ info.client_ip,
+ rand,
+ info.now));
if (replay_protection_) {
out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, info.now));
}
+ if (FLAGS_send_quic_crypto_reject_reason) {
+ // Send client the reject reason for debugging purposes.
+ DCHECK_LT(0u, info.reject_reasons.size());
+ out->SetVector(kRREJ, info.reject_reasons);
+ }
+
// The client may have requested a certificate chain.
const QuicTag* their_proof_demands;
size_t num_their_proof_demands;
@@ -912,7 +1064,7 @@ void QuicCryptoServerConfig::BuildRejection(
const vector<string>* certs;
string signature;
- if (!proof_source_->GetProof(info.sni.as_string(), config->serialized,
+ if (!proof_source_->GetProof(info.sni.as_string(), config.serialized,
x509_ecdsa_supported, &certs, &signature)) {
return;
}
@@ -924,7 +1076,7 @@ void QuicCryptoServerConfig::BuildRejection(
const string compressed = CertCompressor::CompressChain(
*certs, their_common_set_hashes, their_cached_cert_hashes,
- config->common_cert_sets);
+ config.common_cert_sets);
// kREJOverheadBytes is a very rough estimate of how much of a REJ
// message is taken up by things other than the certificates.
@@ -943,7 +1095,7 @@ void QuicCryptoServerConfig::BuildRejection(
// token.
const size_t max_unverified_size =
client_hello.size() * kMultiplier - kREJOverheadBytes;
- COMPILE_ASSERT(kClientHelloMinimumSizeOld * kMultiplier >= kREJOverheadBytes,
+ COMPILE_ASSERT(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes,
overhead_calculation_may_underflow);
if (info.valid_source_address_token ||
signature.size() + compressed.size() < max_unverified_size) {
@@ -967,11 +1119,25 @@ QuicCryptoServerConfig::ParseConfigProtobuf(
scoped_refptr<Config> config(new Config);
config->serialized = protobuf->config();
+ if (!protobuf->has_source_address_token_secret_override()) {
+ // Use the default boxer.
+ config->source_address_token_boxer = &default_source_address_token_boxer_;
+ } else {
+ // Create override boxer instance.
+ CryptoSecretBoxer* boxer = new CryptoSecretBoxer;
+ boxer->SetKey(DeriveSourceAddressTokenKey(
+ protobuf->source_address_token_secret_override()));
+ config->source_address_token_boxer_storage.reset(boxer);
+ config->source_address_token_boxer = boxer;
+ }
+
if (protobuf->has_primary_time()) {
config->primary_time =
QuicWallTime::FromUNIXSeconds(protobuf->primary_time());
}
+ config->priority = protobuf->priority();
+
StringPiece scid;
if (!msg->GetStringPiece(kSCID, &scid)) {
LOG(WARNING) << "Server config message is missing SCID";
@@ -996,7 +1162,7 @@ QuicCryptoServerConfig::ParseConfigProtobuf(
StringPiece orbit;
if (!msg->GetStringPiece(kORBT, &orbit)) {
- LOG(WARNING) << "Server config message is missing OBIT";
+ LOG(WARNING) << "Server config message is missing ORBT";
return NULL;
}
@@ -1009,15 +1175,18 @@ QuicCryptoServerConfig::ParseConfigProtobuf(
memcpy(config->orbit, orbit.data(), sizeof(config->orbit));
{
- base::AutoLock locked(strike_register_client_lock_);
- if (strike_register_client_.get()) {
- const string& orbit = strike_register_client_->orbit();
- if (0 != memcmp(orbit.data(), config->orbit, kOrbitSize)) {
- LOG(WARNING)
- << "Server config has different orbit than current config. "
- "Switching orbits at run-time is not supported.";
- return NULL;
- }
+ StrikeRegisterClient* strike_register_client;
+ {
+ base::AutoLock locked(strike_register_client_lock_);
+ strike_register_client = strike_register_client_.get();
+ }
+
+ if (strike_register_client != NULL &&
+ !strike_register_client->IsKnownOrbit(orbit)) {
+ LOG(WARNING)
+ << "Rejecting server config with orbit that the strike register "
+ "client doesn't know about.";
+ return NULL;
}
}
@@ -1160,37 +1329,52 @@ void QuicCryptoServerConfig::set_server_nonce_strike_register_window_secs(
server_nonce_strike_register_window_secs_ = window_secs;
}
-string QuicCryptoServerConfig::NewSourceAddressToken(
- const IPEndPoint& ip,
- QuicRandom* rand,
- QuicWallTime now) const {
+void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb(
+ PrimaryConfigChangedCallback* cb) {
+ base::AutoLock locked(configs_lock_);
+ primary_config_changed_cb_.reset(cb);
+}
+
+string QuicCryptoServerConfig::NewSourceAddressToken(const Config& config,
+ const IPEndPoint& ip,
+ QuicRandom* rand,
+ QuicWallTime now) const {
SourceAddressToken source_address_token;
- source_address_token.set_ip(IPAddressToPackedString(ip.address()));
+ IPAddressNumber ip_address = ip.address();
+ if (ip.GetSockAddrFamily() == AF_INET) {
+ ip_address = ConvertIPv4NumberToIPv6Number(ip_address);
+ }
+ source_address_token.set_ip(IPAddressToPackedString(ip_address));
source_address_token.set_timestamp(now.ToUNIXSeconds());
- return source_address_token_boxer_.Box(
+ return config.source_address_token_boxer->Box(
rand, source_address_token.SerializeAsString());
}
-bool QuicCryptoServerConfig::ValidateSourceAddressToken(
+HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressToken(
+ const Config& config,
StringPiece token,
const IPEndPoint& ip,
QuicWallTime now) const {
string storage;
StringPiece plaintext;
- if (!source_address_token_boxer_.Unbox(token, &storage, &plaintext)) {
- return false;
+ if (!config.source_address_token_boxer->Unbox(token, &storage, &plaintext)) {
+ return SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE;
}
SourceAddressToken source_address_token;
if (!source_address_token.ParseFromArray(plaintext.data(),
plaintext.size())) {
- return false;
+ return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE;
}
- if (source_address_token.ip() != IPAddressToPackedString(ip.address())) {
+ IPAddressNumber ip_address = ip.address();
+ if (ip.GetSockAddrFamily() == AF_INET) {
+ ip_address = ConvertIPv4NumberToIPv6Number(ip_address);
+ }
+ if (source_address_token.ip() != IPAddressToPackedString(ip_address)) {
// It's for a different IP address.
- return false;
+ return SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE;
}
const QuicWallTime timestamp(
@@ -1199,15 +1383,15 @@ bool QuicCryptoServerConfig::ValidateSourceAddressToken(
if (now.IsBefore(timestamp) &&
delta.ToSeconds() > source_address_token_future_secs_) {
- return false;
+ return SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE;
}
if (now.IsAfter(timestamp) &&
delta.ToSeconds() > source_address_token_lifetime_secs_) {
- return false;
+ return SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE;
}
- return true;
+ return HANDSHAKE_OK;
}
// kServerNoncePlaintextSize is the number of bytes in an unencrypted server
@@ -1233,12 +1417,13 @@ string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand,
StringPiece(reinterpret_cast<char*>(server_nonce), sizeof(server_nonce)));
}
-bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token,
- QuicWallTime now) const {
+HandshakeFailureReason QuicCryptoServerConfig::ValidateServerNonce(
+ StringPiece token,
+ QuicWallTime now) const {
string storage;
StringPiece plaintext;
if (!server_nonce_boxer_.Unbox(token, &storage, &plaintext)) {
- return false;
+ return SERVER_NONCE_DECRYPTION_FAILURE;
}
// plaintext contains:
@@ -1248,7 +1433,7 @@ bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token,
if (plaintext.size() != kServerNoncePlaintextSize) {
// This should never happen because the value decrypted correctly.
LOG(DFATAL) << "Seemingly valid server nonce had incorrect length.";
- return false;
+ return SERVER_NONCE_INVALID_FAILURE;
}
uint8 server_nonce[32];
@@ -1273,13 +1458,15 @@ bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token,
server_nonce, static_cast<uint32>(now.ToUNIXSeconds()));
}
- return is_unique;
+ return is_unique ? HANDSHAKE_OK : SERVER_NONCE_NOT_UNIQUE_FAILURE;
}
QuicCryptoServerConfig::Config::Config()
: channel_id_enabled(false),
is_primary(false),
- primary_time(QuicWallTime::Zero()) {}
+ primary_time(QuicWallTime::Zero()),
+ priority(0),
+ source_address_token_boxer(NULL) {}
QuicCryptoServerConfig::Config::~Config() { STLDeleteElements(&key_exchanges); }
diff --git a/chromium/net/quic/crypto/quic_crypto_server_config.h b/chromium/net/quic/crypto/quic_crypto_server_config.h
index 6d59bff6a9f..3ece064376f 100644
--- a/chromium/net/quic/crypto/quic_crypto_server_config.h
+++ b/chromium/net/quic/crypto/quic_crypto_server_config.h
@@ -22,6 +22,7 @@
namespace net {
+class CryptoHandshakeMessage;
class EphemeralKeySource;
class KeyExchange;
class ProofSource;
@@ -39,6 +40,43 @@ namespace test {
class QuicCryptoServerConfigPeer;
} // namespace test
+enum HandshakeFailureReason {
+ HANDSHAKE_OK = 0,
+
+ // Failure reasons for an invalid client nonce.
+ // TODO(rtenneti): Implement capturing of error from strike register.
+ CLIENT_NONCE_UNKNOWN_FAILURE = 100,
+ CLIENT_NONCE_INVALID_FAILURE,
+
+ // Failure reasons for an invalid server nonce.
+ SERVER_NONCE_INVALID_FAILURE = 200,
+ SERVER_NONCE_DECRYPTION_FAILURE,
+ SERVER_NONCE_NOT_UNIQUE_FAILURE,
+
+ // Failure reasons for an invalid server config.
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE = 300,
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE,
+
+ // Failure reasons for an invalid source adddress token.
+ SOURCE_ADDRESS_TOKEN_INVALID_FAILURE = 400,
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ SOURCE_ADDRESS_TOKEN_PARSE_FAILURE,
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE,
+ SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE,
+};
+
+// Hook that allows application code to subscribe to primary config changes.
+class PrimaryConfigChangedCallback {
+ public:
+ PrimaryConfigChangedCallback();
+ virtual ~PrimaryConfigChangedCallback();
+ virtual void Run(const std::string& scid) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrimaryConfigChangedCallback);
+};
+
// Callback used to accept the result of the |client_hello| validation step.
class NET_EXPORT_PRIVATE ValidateClientHelloResultCallback {
public:
@@ -101,9 +139,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// TESTING is a magic parameter for passing to the constructor in tests.
static const char TESTING[];
- // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable for
- // using in tests.
- static QuicServerConfigProtobuf* DefaultConfig(
+ // Generates a QuicServerConfigProtobuf protobuf suitable for
+ // AddConfig and SetConfigs.
+ static QuicServerConfigProtobuf* GenerateConfig(
QuicRandom* rand,
const QuicClock* clock,
const ConfigOptions& options);
@@ -134,6 +172,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
bool SetConfigs(const std::vector<QuicServerConfigProtobuf*>& protobufs,
QuicWallTime now);
+ // Get the server config ids for all known configs.
+ void GetConfigIds(std::vector<std::string>* scids) const;
+
// Checks |client_hello| for gross errors and determines whether it
// can be shown to be fresh (i.e. not a replay). The result of the
// validation step must be interpreted by calling
@@ -167,12 +208,15 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// validate_chlo_result: Output from the asynchronous call to
// ValidateClientHello. Contains the client hello message and
// information about it.
- // guid: the GUID for the connection, which is used in key derivation.
- // client_ip: the IP address of the client, which is used to generate and
- // validate source-address tokens.
+ // connection_id: the ConnectionId for the connection, which is used in key
+ // derivation.
+ // client_address: the IP address and port of the client. The IP address is
+ // used to generate and validate source-address tokens.
// version: version of the QUIC protocol in use for this connection
// supported_versions: versions of the QUIC protocol that this server
// supports.
+ // initial_flow_control_window: size of initial flow control window this
+ // server uses for new streams.
// clock: used to validate client nonces and ephemeral keys.
// rand: an entropy source
// params: the state of the handshake. This may be updated with a server
@@ -182,8 +226,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// error_details: used to store a string describing any error.
QuicErrorCode ProcessClientHello(
const ValidateClientHelloResultCallback::Result& validate_chlo_result,
- QuicGuid guid,
- IPEndPoint client_ip,
+ QuicConnectionId connection_id,
+ IPEndPoint client_address,
QuicVersion version,
const QuicVersionVector& supported_versions,
const QuicClock* clock,
@@ -254,6 +298,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// uniqueness.
void set_server_nonce_strike_register_window_secs(uint32 window_secs);
+ // Set and take ownership of the callback to invoke on primary config changes.
+ void AcquirePrimaryConfigChangedCb(PrimaryConfigChangedCallback* cb);
+
private:
friend class test::QuicCryptoServerConfigPeer;
@@ -296,8 +343,25 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// will not be promoted at a specific time.
QuicWallTime primary_time;
+ // Secondary sort key for use when selecting primary configs and
+ // there are multiple configs with the same primary time.
+ // Smaller numbers mean higher priority.
+ uint64 priority;
+
+ // source_address_token_boxer_ is used to protect the
+ // source-address tokens that are given to clients.
+ // Points to either source_address_token_boxer_storage or the
+ // default boxer provided by QuicCryptoServerConfig.
+ const CryptoSecretBoxer* source_address_token_boxer;
+
+ // Holds the override source_address_token_boxer instance if the
+ // Config is not using the default source address token boxer
+ // instance provided by QuicCryptoServerConfig.
+ scoped_ptr<CryptoSecretBoxer> source_address_token_boxer_storage;
+
private:
friend class base::RefCounted<Config>;
+
virtual ~Config();
DISALLOW_COPY_AND_ASSIGN(Config);
@@ -305,6 +369,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
typedef std::map<ServerConfigID, scoped_refptr<Config> > ConfigMap;
+ // Get a ref to the config with a given server config id.
+ scoped_refptr<Config> GetConfigWithScid(
+ base::StringPiece requested_scid) const;
+
// ConfigPrimaryTimeLessThan returns true if a->primary_time <
// b->primary_time.
static bool ConfigPrimaryTimeLessThan(const scoped_refptr<Config>& a,
@@ -319,12 +387,13 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// written to |info|.
void EvaluateClientHello(
const uint8* primary_orbit,
+ scoped_refptr<Config> requested_config,
ValidateClientHelloResultCallback::Result* client_hello_state,
ValidateClientHelloResultCallback* done_cb) const;
// BuildRejection sets |out| to be a REJ message in reply to |client_hello|.
void BuildRejection(
- const scoped_refptr<Config>& config,
+ const Config& config,
const CryptoHandshakeMessage& client_hello,
const ClientHelloInfo& info,
QuicRandom* rand,
@@ -337,16 +406,18 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// NewSourceAddressToken returns a fresh source address token for the given
// IP address.
- std::string NewSourceAddressToken(const IPEndPoint& ip,
+ std::string NewSourceAddressToken(const Config& config,
+ const IPEndPoint& ip,
QuicRandom* rand,
QuicWallTime now) const;
- // ValidateSourceAddressToken returns true if the source address token in
- // |token| is a valid and timely token for the IP address |ip| given that the
- // current time is |now|.
- bool ValidateSourceAddressToken(base::StringPiece token,
- const IPEndPoint& ip,
- QuicWallTime now) const;
+ // ValidateSourceAddressToken returns HANDSHAKE_OK if the source address token
+ // in |token| is a valid and timely token for the IP address |ip| given that
+ // the current time is |now|. Otherwise it returns the reason for failure.
+ HandshakeFailureReason ValidateSourceAddressToken(const Config& config,
+ base::StringPiece token,
+ const IPEndPoint& ip,
+ QuicWallTime now) const;
// NewServerNonce generates and encrypts a random nonce.
std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const;
@@ -354,10 +425,11 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// ValidateServerNonce decrypts |token| and verifies that it hasn't been
// previously used and is recent enough that it is plausible that it was part
// of a very recently provided rejection ("recent" will be on the order of
- // 10-30 seconds). If so, it records that it has been used and returns true.
- // Otherwise it returns false.
- bool ValidateServerNonce(base::StringPiece echoed_server_nonce,
- QuicWallTime now) const;
+ // 10-30 seconds). If so, it records that it has been used and returns
+ // HANDSHAKE_OK. Otherwise it returns the reason for failure.
+ HandshakeFailureReason ValidateServerNonce(
+ base::StringPiece echoed_server_nonce,
+ QuicWallTime now) const;
// replay_protection_ controls whether the server enforces that handshakes
// aren't replays.
@@ -377,15 +449,19 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// next_config_promotion_time_ contains the nearest, future time when an
// active config will be promoted to primary.
mutable QuicWallTime next_config_promotion_time_;
+ // Callback to invoke when the primary config changes.
+ scoped_ptr<PrimaryConfigChangedCallback> primary_config_changed_cb_;
+ // Protects access to the pointer held by strike_register_client_.
mutable base::Lock strike_register_client_lock_;
// strike_register_ contains a data structure that keeps track of previously
// observed client nonces in order to prevent replay attacks.
mutable scoped_ptr<StrikeRegisterClient> strike_register_client_;
- // source_address_token_boxer_ is used to protect the source-address tokens
- // that are given to clients.
- CryptoSecretBoxer source_address_token_boxer_;
+ // Default source_address_token_boxer_ used to protect the
+ // source-address tokens that are given to clients. Individual
+ // configs may use boxers with alternate secrets.
+ CryptoSecretBoxer default_source_address_token_boxer_;
// server_nonce_boxer_ is used to encrypt and validate suggested server
// nonces.
@@ -419,6 +495,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
uint32 source_address_token_lifetime_secs_;
uint32 server_nonce_strike_register_max_entries_;
uint32 server_nonce_strike_register_window_secs_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerConfig);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/quic_crypto_server_config_test.cc b/chromium/net/quic/crypto/quic_crypto_server_config_test.cc
index fc8882cd85e..22bfd2c6cc6 100644
--- a/chromium/net/quic/crypto/quic_crypto_server_config_test.cc
+++ b/chromium/net/quic/crypto/quic_crypto_server_config_test.cc
@@ -8,11 +8,14 @@
#include "base/stl_util.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
-#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_secret_boxer.h"
#include "net/quic/crypto/crypto_server_config_protobuf.h"
#include "net/quic/crypto/quic_random.h"
+#include "net/quic/crypto/strike_register_client.h"
#include "net/quic/quic_time.h"
#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,16 +34,50 @@ class QuicCryptoServerConfigPeer {
explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config)
: server_config_(server_config) {}
- string NewSourceAddressToken(IPEndPoint ip,
- QuicRandom* rand,
- QuicWallTime now) {
- return server_config_->NewSourceAddressToken(ip, rand, now);
+ scoped_refptr<QuicCryptoServerConfig::Config> GetConfig(string config_id) {
+ base::AutoLock locked(server_config_->configs_lock_);
+ if (config_id == "<primary>") {
+ return scoped_refptr<QuicCryptoServerConfig::Config>(
+ server_config_->primary_config_);
+ } else {
+ return server_config_->GetConfigWithScid(config_id);
+ }
+ }
+
+ bool ConfigHasDefaultSourceAddressTokenBoxer(string config_id) {
+ scoped_refptr<QuicCryptoServerConfig::Config> config = GetConfig(config_id);
+ return config->source_address_token_boxer ==
+ &(server_config_->default_source_address_token_boxer_);
+ }
+
+ string NewSourceAddressToken(
+ string config_id,
+ IPEndPoint ip,
+ QuicRandom* rand,
+ QuicWallTime now) {
+ return server_config_->NewSourceAddressToken(
+ *GetConfig(config_id), ip, rand, now);
+ }
+
+ HandshakeFailureReason ValidateSourceAddressToken(string config_id,
+ StringPiece srct,
+ IPEndPoint ip,
+ QuicWallTime now) {
+ return server_config_->ValidateSourceAddressToken(
+ *GetConfig(config_id), srct, ip, now);
+ }
+
+ string NewServerNonce(QuicRandom* rand, QuicWallTime now) const {
+ return server_config_->NewServerNonce(rand, now);
+ }
+
+ HandshakeFailureReason ValidateServerNonce(StringPiece token,
+ QuicWallTime now) {
+ return server_config_->ValidateServerNonce(token, now);
}
- bool ValidateSourceAddressToken(StringPiece srct,
- IPEndPoint ip,
- QuicWallTime now) {
- return server_config_->ValidateSourceAddressToken(srct, ip, now);
+ base::Lock* GetStrikeRegisterClientLock() {
+ return &server_config_->strike_register_client_lock_;
}
// CheckConfigs compares the state of the Configs in |server_config_| to the
@@ -144,6 +181,40 @@ class QuicCryptoServerConfigPeer {
const QuicCryptoServerConfig* server_config_;
};
+class TestStrikeRegisterClient : public StrikeRegisterClient {
+ public:
+ explicit TestStrikeRegisterClient(QuicCryptoServerConfig* config)
+ : config_(config),
+ is_known_orbit_called_(false) {
+ }
+
+ virtual bool IsKnownOrbit(StringPiece orbit) const OVERRIDE {
+ // Ensure that the strike register client lock is not held.
+ QuicCryptoServerConfigPeer peer(config_);
+ base::Lock* m = peer.GetStrikeRegisterClientLock();
+ // In Chromium, we will dead lock if the lock is held by the current thread.
+ // Chromium doesn't have AssertNotHeld API call.
+ // m->AssertNotHeld();
+ base::AutoLock lock(*m);
+
+ is_known_orbit_called_ = true;
+ return true;
+ }
+
+ virtual void VerifyNonceIsValidAndUnique(
+ StringPiece nonce,
+ QuicWallTime now,
+ ResultCallback* cb) OVERRIDE {
+ LOG(FATAL) << "Not implemented";
+ }
+
+ bool is_known_orbit_called() { return is_known_orbit_called_; }
+
+ private:
+ QuicCryptoServerConfig* config_;
+ mutable bool is_known_orbit_called_;
+};
+
TEST(QuicCryptoServerConfigTest, ServerConfig) {
QuicRandom* rand = QuicRandom::GetInstance();
QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
@@ -154,32 +225,139 @@ TEST(QuicCryptoServerConfigTest, ServerConfig) {
QuicCryptoServerConfig::ConfigOptions()));
}
-TEST(QuicCryptoServerConfigTest, SourceAddressTokens) {
+TEST(QuicCryptoServerConfigTest, GetOrbitIsCalledWithoutTheStrikeRegisterLock) {
QuicRandom* rand = QuicRandom::GetInstance();
QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
- IPAddressNumber ip;
- CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip));
- IPEndPoint ip4 = IPEndPoint(ip, 1);
- CHECK(ParseIPLiteralToNumber("2001:db8:0::42", &ip));
- IPEndPoint ip6 = IPEndPoint(ip, 2);
+ MockClock clock;
+
+ TestStrikeRegisterClient* strike_register =
+ new TestStrikeRegisterClient(&server);
+ server.SetStrikeRegisterClient(strike_register);
+
+ QuicCryptoServerConfig::ConfigOptions options;
+ scoped_ptr<CryptoHandshakeMessage>(
+ server.AddDefaultConfig(rand, &clock, options));
+ EXPECT_TRUE(strike_register->is_known_orbit_called());
+}
+
+TEST(QuicCryptoServerConfigTest, SourceAddressTokens) {
+ const string kPrimary = "<primary>";
+ const string kOverride = "Config with custom source address token key";
+
MockClock clock;
clock.AdvanceTime(QuicTime::Delta::FromSeconds(1000000));
- QuicCryptoServerConfigPeer peer(&server);
QuicWallTime now = clock.WallNow();
const QuicWallTime original_time = now;
- const string token4 = peer.NewSourceAddressToken(ip4, rand, now);
- const string token6 = peer.NewSourceAddressToken(ip6, rand, now);
- EXPECT_TRUE(peer.ValidateSourceAddressToken(token4, ip4, now));
- EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip6, now));
- EXPECT_TRUE(peer.ValidateSourceAddressToken(token6, ip6, now));
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
+ QuicCryptoServerConfigPeer peer(&server);
+
+ scoped_ptr<CryptoHandshakeMessage>(
+ server.AddDefaultConfig(rand, &clock,
+ QuicCryptoServerConfig::ConfigOptions()));
+ // Add a config that overrides the default boxer.
+ QuicCryptoServerConfig::ConfigOptions options;
+ options.id = kOverride;
+ scoped_ptr<QuicServerConfigProtobuf> protobuf(
+ QuicCryptoServerConfig::GenerateConfig(rand, &clock, options));
+ protobuf->set_source_address_token_secret_override("a secret key");
+ // Lower priority than the default config.
+ protobuf->set_priority(1);
+ scoped_ptr<CryptoHandshakeMessage>(
+ server.AddConfig(protobuf.get(), now));
+
+ EXPECT_TRUE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kPrimary));
+ EXPECT_FALSE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kOverride));
+
+ IPEndPoint ip4 = IPEndPoint(Loopback4(), 1);
+ IPEndPoint ip4d = IPEndPoint(ConvertIPv4NumberToIPv6Number(ip4.address()), 1);
+ IPEndPoint ip6 = IPEndPoint(Loopback6(), 2);
+
+ // Primary config generates configs that validate successfully.
+ const string token4 = peer.NewSourceAddressToken(kPrimary, ip4, rand, now);
+ const string token4d = peer.NewSourceAddressToken(kPrimary, ip4d, rand, now);
+ const string token6 = peer.NewSourceAddressToken(kPrimary, ip6, rand, now);
+ EXPECT_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4, ip4, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4, ip4d, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4, ip6, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4d, ip4, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4d, ip4d, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4d, ip6, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token6, ip6, now));
+
+ // Override config generates configs that validate successfully.
+ const string override_token4 = peer.NewSourceAddressToken(
+ kOverride, ip4, rand, now);
+ const string override_token6 = peer.NewSourceAddressToken(
+ kOverride, ip6, rand, now);
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kOverride, override_token4, ip4, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ peer.ValidateSourceAddressToken(kOverride, override_token4, ip6,
+ now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kOverride, override_token6, ip6, now));
+
+ // Tokens generated by the primary config do not validate
+ // successfully against the override config, and vice versa.
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kOverride, token4, ip4, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kOverride, token6, ip6, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, override_token4, ip4,
+ now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, override_token6, ip6,
+ now));
+
+ // Validation fails after tokens expire.
now = original_time.Add(QuicTime::Delta::FromSeconds(86400 * 7));
- EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip4, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now));
now = original_time.Subtract(QuicTime::Delta::FromSeconds(3600 * 2));
- EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip4, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now));
+}
+
+TEST(QuicCryptoServerConfigTest, ValidateServerNonce) {
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
+ QuicCryptoServerConfigPeer peer(&server);
+
+ StringPiece message("hello world");
+ const size_t key_size = CryptoSecretBoxer::GetKeySize();
+ scoped_ptr<uint8[]> key(new uint8[key_size]);
+ memset(key.get(), 0x11, key_size);
+
+ CryptoSecretBoxer boxer;
+ boxer.SetKey(StringPiece(reinterpret_cast<char*>(key.get()), key_size));
+ const string box = boxer.Box(rand, message);
+ MockClock clock;
+ QuicWallTime now = clock.WallNow();
+ const QuicWallTime original_time = now;
+ EXPECT_EQ(SERVER_NONCE_DECRYPTION_FAILURE,
+ peer.ValidateServerNonce(box, now));
+
+ string server_nonce = peer.NewServerNonce(rand, now);
+ EXPECT_EQ(HANDSHAKE_OK, peer.ValidateServerNonce(server_nonce, now));
+ EXPECT_EQ(SERVER_NONCE_NOT_UNIQUE_FAILURE,
+ peer.ValidateServerNonce(server_nonce, now));
+
+ now = original_time.Add(QuicTime::Delta::FromSeconds(1000 * 7));
+ server_nonce = peer.NewServerNonce(rand, now);
+ EXPECT_EQ(HANDSHAKE_OK, peer.ValidateServerNonce(server_nonce, now));
}
class CryptoServerConfigsTest : public ::testing::Test {
@@ -202,18 +380,23 @@ class CryptoServerConfigsTest : public ::testing::Test {
// SetConfigs(NULL); // calls |config_.SetConfigs| with no protobufs.
//
// // Calls |config_.SetConfigs| with two protobufs: one for a Config with
- // // a |primary_time| of 900, and another with a |primary_time| of 1000.
+ // // a |primary_time| of 900 and priority 1, and another with
+ // // a |primary_time| of 1000 and priority 2.
+
// CheckConfigs(
- // "id1", 900,
- // "id2", 1000,
+ // "id1", 900, 1,
+ // "id2", 1000, 2,
// NULL);
//
// If the server config id starts with "INVALID" then the generated protobuf
// will be invalid.
void SetConfigs(const char* server_config_id1, ...) {
+ const char kOrbit[] = "12345678";
+
va_list ap;
va_start(ap, server_config_id1);
bool has_invalid = false;
+ bool is_empty = true;
vector<QuicServerConfigProtobuf*> protobufs;
bool first = true;
@@ -230,13 +413,17 @@ class CryptoServerConfigsTest : public ::testing::Test {
break;
}
+ is_empty = false;
int primary_time = va_arg(ap, int);
+ int priority = va_arg(ap, int);
QuicCryptoServerConfig::ConfigOptions options;
options.id = server_config_id;
+ options.orbit = kOrbit;
QuicServerConfigProtobuf* protobuf(
- QuicCryptoServerConfig::DefaultConfig(rand_, &clock_, options));
+ QuicCryptoServerConfig::GenerateConfig(rand_, &clock_, options));
protobuf->set_primary_time(primary_time);
+ protobuf->set_priority(priority);
if (string(server_config_id).find("INVALID") == 0) {
protobuf->clear_key();
has_invalid = true;
@@ -244,7 +431,8 @@ class CryptoServerConfigsTest : public ::testing::Test {
protobufs.push_back(protobuf);
}
- ASSERT_EQ(!has_invalid, config_.SetConfigs(protobufs, clock_.WallNow()));
+ ASSERT_EQ(!has_invalid && !is_empty,
+ config_.SetConfigs(protobufs, clock_.WallNow()));
STLDeleteElements(&protobufs);
}
@@ -261,8 +449,8 @@ TEST_F(CryptoServerConfigsTest, NoConfigs) {
TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) {
// Make sure that "b" is primary even though "a" comes first.
- SetConfigs("a", 1100,
- "b", 900,
+ SetConfigs("a", 1100, 1,
+ "b", 900, 1,
NULL);
test_peer_.CheckConfigs(
"a", false,
@@ -272,8 +460,8 @@ TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) {
TEST_F(CryptoServerConfigsTest, MakePrimarySecond) {
// Make sure that a remains primary after b is added.
- SetConfigs("a", 900,
- "b", 1100,
+ SetConfigs("a", 900, 1,
+ "b", 1100, 1,
NULL);
test_peer_.CheckConfigs(
"a", true,
@@ -283,12 +471,17 @@ TEST_F(CryptoServerConfigsTest, MakePrimarySecond) {
TEST_F(CryptoServerConfigsTest, Delete) {
// Ensure that configs get deleted when removed.
- SetConfigs("a", 800,
- "b", 900,
- "c", 1100,
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
+ "c", 1100, 1,
NULL);
- SetConfigs("b", 900,
- "c", 1100,
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ "c", false,
+ NULL);
+ SetConfigs("b", 900, 1,
+ "c", 1100, 1,
NULL);
test_peer_.CheckConfigs(
"b", true,
@@ -296,26 +489,148 @@ TEST_F(CryptoServerConfigsTest, Delete) {
NULL);
}
-TEST_F(CryptoServerConfigsTest, DontDeletePrimary) {
- // Ensure that the primary config isn't deleted when removed.
- SetConfigs("a", 800,
- "b", 900,
- "c", 1100,
+TEST_F(CryptoServerConfigsTest, DeletePrimary) {
+ // Ensure that deleting the primary config works.
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
+ "c", 1100, 1,
NULL);
- SetConfigs("a", 800,
- "c", 1100,
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ "c", false,
+ NULL);
+ SetConfigs("a", 800, 1,
+ "c", 1100, 1,
+ NULL);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "c", false,
+ NULL);
+}
+
+TEST_F(CryptoServerConfigsTest, FailIfDeletingAllConfigs) {
+ // Ensure that configs get deleted when removed.
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
NULL);
test_peer_.CheckConfigs(
"a", false,
"b", true,
+ NULL);
+ SetConfigs(NULL);
+ // Config change is rejected, still using old configs.
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ NULL);
+}
+
+TEST_F(CryptoServerConfigsTest, ChangePrimaryTime) {
+ // Check that updates to primary time get picked up.
+ SetConfigs("a", 400, 1,
+ "b", 800, 1,
+ "c", 1200, 1,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(500);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ NULL);
+ SetConfigs("a", 1200, 1,
+ "b", 800, 1,
+ "c", 400, 1,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(500);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ NULL);
+}
+
+TEST_F(CryptoServerConfigsTest, AllConfigsInThePast) {
+ // Check that the most recent config is selected.
+ SetConfigs("a", 400, 1,
+ "b", 800, 1,
+ "c", 1200, 1,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(1500);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ NULL);
+}
+
+TEST_F(CryptoServerConfigsTest, AllConfigsInTheFuture) {
+ // Check that the first config is selected.
+ SetConfigs("a", 400, 1,
+ "b", 800, 1,
+ "c", 1200, 1,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(100);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
"c", false,
NULL);
}
+TEST_F(CryptoServerConfigsTest, SortByPriority) {
+ // Check that priority is used to decide on a primary config when
+ // configs have the same primary time.
+ SetConfigs("a", 900, 1,
+ "b", 900, 2,
+ "c", 900, 3,
+ NULL);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(800);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ NULL);
+
+ // Change priorities and expect sort order to change.
+ SetConfigs("a", 900, 2,
+ "b", 900, 1,
+ "c", 900, 0,
+ NULL);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(800);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ NULL);
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ NULL);
+}
+
TEST_F(CryptoServerConfigsTest, AdvancePrimary) {
// Check that a new primary config is enabled at the right time.
- SetConfigs("a", 900,
- "b", 1100,
+ SetConfigs("a", 900, 1,
+ "b", 1100, 1,
NULL);
test_peer_.SelectNewPrimaryConfig(1000);
test_peer_.CheckConfigs(
@@ -331,18 +646,18 @@ TEST_F(CryptoServerConfigsTest, AdvancePrimary) {
TEST_F(CryptoServerConfigsTest, InvalidConfigs) {
// Ensure that invalid configs don't change anything.
- SetConfigs("a", 800,
- "b", 900,
- "c", 1100,
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
+ "c", 1100, 1,
NULL);
test_peer_.CheckConfigs(
"a", false,
"b", true,
"c", false,
NULL);
- SetConfigs("a", 800,
- "c", 1100,
- "INVALID1", 1000,
+ SetConfigs("a", 800, 1,
+ "c", 1100, 1,
+ "INVALID1", 1000, 1,
NULL);
test_peer_.CheckConfigs(
"a", false,
diff --git a/chromium/net/quic/crypto/quic_decrypter.cc b/chromium/net/quic/crypto/quic_decrypter.cc
index fb19d4c0d70..df1192a85af 100644
--- a/chromium/net/quic/crypto/quic_decrypter.cc
+++ b/chromium/net/quic/crypto/quic_decrypter.cc
@@ -5,6 +5,8 @@
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
+#include "net/quic/crypto/chacha20_poly1305_decrypter.h"
+#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/null_decrypter.h"
namespace net {
@@ -14,6 +16,8 @@ QuicDecrypter* QuicDecrypter::Create(QuicTag algorithm) {
switch (algorithm) {
case kAESG:
return new Aes128Gcm12Decrypter();
+ case kCC12:
+ return new ChaCha20Poly1305Decrypter();
case kNULL:
return new NullDecrypter();
default:
diff --git a/chromium/net/quic/crypto/quic_decrypter.h b/chromium/net/quic/crypto/quic_decrypter.h
index 124d98fea8d..30983493096 100644
--- a/chromium/net/quic/crypto/quic_decrypter.h
+++ b/chromium/net/quic/crypto/quic_decrypter.h
@@ -6,7 +6,6 @@
#define NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_
#include "net/base/net_export.h"
-#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/quic_protocol.h"
namespace net {
diff --git a/chromium/net/quic/crypto/quic_encrypter.cc b/chromium/net/quic/crypto/quic_encrypter.cc
index 489da8ed2ec..a817870174b 100644
--- a/chromium/net/quic/crypto/quic_encrypter.cc
+++ b/chromium/net/quic/crypto/quic_encrypter.cc
@@ -5,6 +5,8 @@
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/null_encrypter.h"
namespace net {
@@ -14,6 +16,8 @@ QuicEncrypter* QuicEncrypter::Create(QuicTag algorithm) {
switch (algorithm) {
case kAESG:
return new Aes128Gcm12Encrypter();
+ case kCC12:
+ return new ChaCha20Poly1305Encrypter();
case kNULL:
return new NullEncrypter();
default:
diff --git a/chromium/net/quic/crypto/quic_encrypter.h b/chromium/net/quic/crypto/quic_encrypter.h
index edddf3628b6..37bba1212d1 100644
--- a/chromium/net/quic/crypto/quic_encrypter.h
+++ b/chromium/net/quic/crypto/quic_encrypter.h
@@ -6,7 +6,6 @@
#define NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_
#include "net/base/net_export.h"
-#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/quic_protocol.h"
namespace net {
diff --git a/chromium/net/quic/crypto/quic_random.cc b/chromium/net/quic/crypto/quic_random.cc
index 73fce2ef2cf..6f460130660 100644
--- a/chromium/net/quic/crypto/quic_random.cc
+++ b/chromium/net/quic/crypto/quic_random.cc
@@ -23,7 +23,7 @@ class DefaultRandom : public QuicRandom {
size_t entropy_len) OVERRIDE;
private:
- DefaultRandom();
+ DefaultRandom() {};
virtual ~DefaultRandom() {}
friend struct DefaultSingletonTraits<DefaultRandom>;
@@ -48,9 +48,6 @@ void DefaultRandom::Reseed(const void* additional_entropy, size_t entropy_len) {
// No such function exists in crypto/random.h.
}
-DefaultRandom::DefaultRandom() {
-}
-
} // namespace
// static
diff --git a/chromium/net/quic/crypto/quic_server_info.cc b/chromium/net/quic/crypto/quic_server_info.cc
new file mode 100644
index 00000000000..519d7b5fd60
--- /dev/null
+++ b/chromium/net/quic/crypto/quic_server_info.cc
@@ -0,0 +1,141 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/quic_server_info.h"
+
+#include <limits>
+
+#include "base/pickle.h"
+
+using std::string;
+
+namespace {
+
+const int kQuicCryptoConfigVersion = 1;
+
+} // namespace
+
+namespace net {
+
+QuicServerInfo::State::State() {}
+
+QuicServerInfo::State::~State() {}
+
+void QuicServerInfo::State::Clear() {
+ server_config.clear();
+ source_address_token.clear();
+ server_config_sig.clear();
+ certs.clear();
+}
+
+QuicServerInfo::QuicServerInfo(const QuicServerId& server_id)
+ : server_id_(server_id) {
+}
+
+QuicServerInfo::~QuicServerInfo() {
+}
+
+const QuicServerInfo::State& QuicServerInfo::state() const {
+ return state_;
+}
+
+QuicServerInfo::State* QuicServerInfo::mutable_state() {
+ return &state_;
+}
+
+bool QuicServerInfo::Parse(const string& data) {
+ State* state = mutable_state();
+
+ state->Clear();
+
+ bool r = ParseInner(data);
+ if (!r)
+ state->Clear();
+ return r;
+}
+
+bool QuicServerInfo::ParseInner(const string& data) {
+ State* state = mutable_state();
+
+ // No data was read from the disk cache.
+ if (data.empty()) {
+ return false;
+ }
+
+ Pickle p(data.data(), data.size());
+ PickleIterator iter(p);
+
+ int version = -1;
+ if (!p.ReadInt(&iter, &version)) {
+ DVLOG(1) << "Missing version";
+ return false;
+ }
+
+ if (version != kQuicCryptoConfigVersion) {
+ DVLOG(1) << "Unsupported version";
+ return false;
+ }
+
+ if (!p.ReadString(&iter, &state->server_config)) {
+ DVLOG(1) << "Malformed server_config";
+ return false;
+ }
+ if (!p.ReadString(&iter, &state->source_address_token)) {
+ DVLOG(1) << "Malformed source_address_token";
+ return false;
+ }
+ if (!p.ReadString(&iter, &state->server_config_sig)) {
+ DVLOG(1) << "Malformed server_config_sig";
+ return false;
+ }
+
+ // Read certs.
+ uint32 num_certs;
+ if (!p.ReadUInt32(&iter, &num_certs)) {
+ DVLOG(1) << "Malformed num_certs";
+ return false;
+ }
+
+ for (uint32 i = 0; i < num_certs; i++) {
+ string cert;
+ if (!p.ReadString(&iter, &cert)) {
+ DVLOG(1) << "Malformed cert";
+ return false;
+ }
+ state->certs.push_back(cert);
+ }
+
+ return true;
+}
+
+string QuicServerInfo::Serialize() {
+ string pickled_data = SerializeInner();
+ state_.Clear();
+ return pickled_data;
+}
+
+string QuicServerInfo::SerializeInner() const {
+ Pickle p(sizeof(Pickle::Header));
+
+ if (!p.WriteInt(kQuicCryptoConfigVersion) ||
+ !p.WriteString(state_.server_config) ||
+ !p.WriteString(state_.source_address_token) ||
+ !p.WriteString(state_.server_config_sig) ||
+ state_.certs.size() > std::numeric_limits<uint32>::max() ||
+ !p.WriteUInt32(state_.certs.size())) {
+ return string();
+ }
+
+ for (size_t i = 0; i < state_.certs.size(); i++) {
+ if (!p.WriteString(state_.certs[i])) {
+ return string();
+ }
+ }
+
+ return string(reinterpret_cast<const char *>(p.data()), p.size());
+}
+
+QuicServerInfoFactory::~QuicServerInfoFactory() {}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/quic_server_info.h b/chromium/net/quic/crypto/quic_server_info.h
new file mode 100644
index 00000000000..fd54b94ad92
--- /dev/null
+++ b/chromium/net/quic/crypto/quic_server_info.h
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_QUIC_SERVER_INFO_H_
+#define NET_QUIC_CRYPTO_QUIC_SERVER_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_server_id.h"
+
+namespace net {
+
+class X509Certificate;
+
+// QuicServerInfo is an interface for fetching information about a QUIC server.
+// This information may be stored on disk so does not include keys or other
+// sensitive information. Primarily it's intended for caching the QUIC server's
+// crypto config.
+class NET_EXPORT_PRIVATE QuicServerInfo {
+ public:
+ QuicServerInfo(const QuicServerId& server_id);
+ virtual ~QuicServerInfo();
+
+ // Start will commence the lookup. This must be called before any other
+ // methods. By opportunistically calling this early, it may be possible to
+ // overlap this object's lookup and reduce latency.
+ virtual void Start() = 0;
+
+ // WaitForDataReady returns OK if the fetch of the requested data has
+ // completed. Otherwise it returns ERR_IO_PENDING and will call |callback| on
+ // the current thread when ready.
+ //
+ // Only a single callback can be outstanding at a given time and, in the
+ // event that WaitForDataReady returns OK, it's the caller's responsibility
+ // to delete |callback|.
+ //
+ // |callback| may be NULL, in which case ERR_IO_PENDING may still be returned
+ // but, obviously, a callback will never be made.
+ virtual int WaitForDataReady(const CompletionCallback& callback) = 0;
+
+ // Returns true if data is loaded from disk cache and ready (WaitForDataReady
+ // doesn't have a pending callback).
+ virtual bool IsDataReady() = 0;
+
+ // Returns true if the object is ready to persist data, in other words, if
+ // data is loaded from disk cache and ready and there are no pending writes.
+ virtual bool IsReadyToPersist() = 0;
+
+ // Persist allows for the server information to be updated for future users.
+ // This is a fire and forget operation: the caller may drop its reference
+ // from this object and the store operation will still complete. This can
+ // only be called once WaitForDataReady has returned OK or called its
+ // callback.
+ virtual void Persist() = 0;
+
+ struct State {
+ State();
+ ~State();
+
+ void Clear();
+
+ // This class matches QuicClientCryptoConfig::CachedState.
+ std::string server_config; // A serialized handshake message.
+ std::string source_address_token; // An opaque proof of IP ownership.
+ std::vector<std::string> certs; // A list of certificates in leaf-first
+ // order.
+ std::string server_config_sig; // A signature of |server_config_|.
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(State);
+ };
+
+ // Once the data is ready, it can be read using the following members. These
+ // members can then be updated before calling |Persist|.
+ const State& state() const;
+ State* mutable_state();
+
+ protected:
+ // Parse parses pickled data and fills out the public member fields of this
+ // object. It returns true iff the parse was successful. The public member
+ // fields will be set to something sane in any case.
+ bool Parse(const std::string& data);
+ std::string Serialize();
+ State state_;
+
+ private:
+ // ParseInner is a helper function for Parse.
+ bool ParseInner(const std::string& data);
+
+ // SerializeInner is a helper function for Serialize.
+ std::string SerializeInner() const;
+
+ // This is the QUIC server (hostname, port, is_https, privacy_mode) tuple for
+ // which we restore the crypto_config.
+ const QuicServerId server_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerInfo);
+};
+
+class QuicServerInfoFactory {
+ public:
+ virtual ~QuicServerInfoFactory();
+
+ // GetForServer returns a fresh, allocated QuicServerInfo for the given
+ // |server_id| or NULL on failure.
+ virtual QuicServerInfo* GetForServer(const QuicServerId& server_id) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_QUIC_SERVER_INFO_H_
diff --git a/chromium/net/quic/crypto/scoped_evp_aead_ctx.cc b/chromium/net/quic/crypto/scoped_evp_aead_ctx.cc
new file mode 100644
index 00000000000..2b5feb37fca
--- /dev/null
+++ b/chromium/net/quic/crypto/scoped_evp_aead_ctx.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/scoped_evp_aead_ctx.h"
+
+namespace net {
+
+ScopedEVPAEADCtx::ScopedEVPAEADCtx() {
+ ctx_.aead = NULL;
+}
+
+ScopedEVPAEADCtx::~ScopedEVPAEADCtx() {
+ if (ctx_.aead != NULL) {
+ EVP_AEAD_CTX_cleanup(&ctx_);
+ }
+}
+
+EVP_AEAD_CTX* ScopedEVPAEADCtx::get() {
+ return &ctx_;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/crypto/scoped_evp_aead_ctx.h b/chromium/net/quic/crypto/scoped_evp_aead_ctx.h
new file mode 100644
index 00000000000..f0f04c1caaf
--- /dev/null
+++ b/chromium/net/quic/crypto/scoped_evp_aead_ctx.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_SCOPED_EVP_AEAD_CTX_H_
+#define NET_QUIC_CRYPTO_SCOPED_EVP_AEAD_CTX_H_
+
+#include <openssl/evp.h>
+
+#include "base/basictypes.h"
+
+namespace net {
+
+// ScopedEVPAEADCtx manages an EVP_AEAD_CTX object and calls the needed cleanup
+// functions when it goes out of scope.
+class ScopedEVPAEADCtx {
+ public:
+ ScopedEVPAEADCtx();
+ ~ScopedEVPAEADCtx();
+
+ EVP_AEAD_CTX* get();
+
+ private:
+ EVP_AEAD_CTX ctx_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedEVPAEADCtx);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_SCOPED_EVP_AEAD_CTX_H_
diff --git a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc b/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc
deleted file mode 100644
index b904f870fdd..00000000000
--- a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/crypto/scoped_evp_cipher_ctx.h"
-
-#include <openssl/evp.h>
-
-namespace net {
-
-ScopedEVPCipherCtx::ScopedEVPCipherCtx()
- : ctx_(EVP_CIPHER_CTX_new()) { }
-
-ScopedEVPCipherCtx::~ScopedEVPCipherCtx() {
- EVP_CIPHER_CTX_free(ctx_);
-}
-
-EVP_CIPHER_CTX* ScopedEVPCipherCtx::get() const {
- return ctx_;
-}
-
-} // namespace net
diff --git a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h b/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h
deleted file mode 100644
index ec0fd51b92a..00000000000
--- a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_
-#define NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_
-
-typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
-
-namespace net {
-
-// TODO(wtc): this is the same as the ScopedCipherCTX class defined in
-// crypto/encryptor_openssl.cc. Eliminate the duplicate code.
-// crypto::ScopedOpenSSL is not suitable for EVP_CIPHER_CTX because
-// there are no EVP_CIPHER_CTX_create and EVP_CIPHER_CTX_destroy
-// functions.
-class ScopedEVPCipherCtx {
- public:
- ScopedEVPCipherCtx();
- ~ScopedEVPCipherCtx();
-
- EVP_CIPHER_CTX* get() const;
-
- private:
- EVP_CIPHER_CTX* const ctx_;
-};
-
-} // namespace net
-
-#endif // NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_
diff --git a/chromium/net/quic/crypto/source_address_token.h b/chromium/net/quic/crypto/source_address_token.h
index 8c509651e28..fbb50b1028e 100644
--- a/chromium/net/quic/crypto/source_address_token.h
+++ b/chromium/net/quic/crypto/source_address_token.h
@@ -41,6 +41,8 @@ class SourceAddressToken {
private:
std::string ip_;
int64 timestamp_;
+
+ DISALLOW_COPY_AND_ASSIGN(SourceAddressToken);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/strike_register.cc b/chromium/net/quic/crypto/strike_register.cc
index f45bfabd9f0..9aec6ffb812 100644
--- a/chromium/net/quic/crypto/strike_register.cc
+++ b/chromium/net/quic/crypto/strike_register.cc
@@ -60,6 +60,13 @@ class StrikeRegister::InternalNode {
// to consider times that are before the creation time.
static const uint32 kCreationTimeFromInternalEpoch = 63115200.0; // 2 years.
+void StrikeRegister::ValidateStrikeRegisterConfig(unsigned max_entries) {
+ // We only have 23 bits of index available.
+ CHECK_LT(max_entries, 1u << 23);
+ CHECK_GT(max_entries, 1u); // There must be at least two entries.
+ CHECK_EQ(sizeof(InternalNode), 8u); // in case of compiler changes.
+}
+
StrikeRegister::StrikeRegister(unsigned max_entries,
uint32 current_time,
uint32 window_secs,
@@ -78,10 +85,7 @@ StrikeRegister::StrikeRegister(unsigned max_entries,
horizon_valid_(startup == DENY_REQUESTS_AT_STARTUP) {
memcpy(orbit_, orbit, sizeof(orbit_));
- // We only have 23 bits of index available.
- CHECK_LT(max_entries, 1u << 23);
- CHECK_GT(max_entries, 1u); // There must be at least two entries.
- CHECK_EQ(sizeof(InternalNode), 8u); // in case of compiler changes.
+ ValidateStrikeRegisterConfig(max_entries);
internal_nodes_ = new InternalNode[max_entries];
external_nodes_.reset(new uint8[kExternalNodeSize * max_entries]);
diff --git a/chromium/net/quic/crypto/strike_register.h b/chromium/net/quic/crypto/strike_register.h
index fda62a802b2..3f49e92e3d5 100644
--- a/chromium/net/quic/crypto/strike_register.h
+++ b/chromium/net/quic/crypto/strike_register.h
@@ -81,6 +81,9 @@ class NET_EXPORT_PRIVATE StrikeRegister {
// external node. We flag the 24th bit to mark a pointer as external.
static const uint32 kExternalFlag;
+ // Allows early validation before a strike register is created.
+ static void ValidateStrikeRegisterConfig(unsigned max_entries);
+
// Construct a new set which can hold, at most, |max_entries| (which must be
// less than 2**23). See the comments around StartupType about initial
// behaviour. Otherwise, all nonces that are outside +/- |window_secs| from
@@ -178,6 +181,8 @@ class NET_EXPORT_PRIVATE StrikeRegister {
// this header.
InternalNode* internal_nodes_;
scoped_ptr<uint8[]> external_nodes_;
+
+ DISALLOW_COPY_AND_ASSIGN(StrikeRegister);
};
} // namespace net
diff --git a/chromium/net/quic/crypto/strike_register_client.h b/chromium/net/quic/crypto/strike_register_client.h
index 4a27bd7b85c..e37827a899d 100644
--- a/chromium/net/quic/crypto/strike_register_client.h
+++ b/chromium/net/quic/crypto/strike_register_client.h
@@ -7,6 +7,7 @@
#include <string>
+#include "base/basictypes.h"
#include "base/strings/string_piece.h"
#include "net/base/net_export.h"
#include "net/quic/quic_time.h"
@@ -38,8 +39,8 @@ class NET_EXPORT_PRIVATE StrikeRegisterClient {
StrikeRegisterClient() {}
virtual ~StrikeRegisterClient() {}
- // Returns the strike server orbit if known, else empty string.
- virtual std::string orbit() = 0;
+ // Returns true iff the strike register knows about the given orbit.
+ virtual bool IsKnownOrbit(base::StringPiece orbit) const = 0;
// Validate a nonce for freshness and uniqueness.
// Will invoke cb->Run(ValidateResponse::nonce_is_valid_and_unique())
// once the asynchronous operation is complete.
diff --git a/chromium/net/quic/iovector.h b/chromium/net/quic/iovector.h
index 74fcbef3087..22d2cc9a8cd 100644
--- a/chromium/net/quic/iovector.h
+++ b/chromium/net/quic/iovector.h
@@ -57,7 +57,6 @@ inline size_t TotalIovecLength(const struct iovec* iov, size_t iovcnt) {
// to avoid accidentally change an entry that is assembled by two or more
// Append()'s by simply an index access.
//
-
class NET_EXPORT_PRIVATE IOVector {
public:
// Provide a default constructor so it'll never be inhibited by adding other
diff --git a/chromium/net/quic/quic_ack_notifier.cc b/chromium/net/quic/quic_ack_notifier.cc
index 5eaf38581dc..3bd65e50a35 100644
--- a/chromium/net/quic/quic_ack_notifier.cc
+++ b/chromium/net/quic/quic_ack_notifier.cc
@@ -9,39 +9,54 @@
#include "base/logging.h"
#include "base/stl_util.h"
+using base::hash_map;
+using std::make_pair;
+
namespace net {
+QuicAckNotifier::PacketInfo::PacketInfo() : packet_payload_size(0) {
+}
+
+QuicAckNotifier::PacketInfo::PacketInfo(int payload_size)
+ : packet_payload_size(payload_size) {
+}
+
QuicAckNotifier::DelegateInterface::DelegateInterface() {}
QuicAckNotifier::DelegateInterface::~DelegateInterface() {}
QuicAckNotifier::QuicAckNotifier(DelegateInterface* delegate)
- : delegate_(delegate) {
- DCHECK(delegate_);
+ : delegate_(delegate),
+ original_packet_count_(0),
+ original_byte_count_(0),
+ retransmitted_packet_count_(0),
+ retransmitted_byte_count_(0) {
+ DCHECK(delegate);
}
-QuicAckNotifier::~QuicAckNotifier() {}
-
-void QuicAckNotifier::AddSequenceNumber(
- const QuicPacketSequenceNumber& sequence_number) {
- sequence_numbers_.insert(sequence_number);
+QuicAckNotifier::~QuicAckNotifier() {
}
-void QuicAckNotifier::AddSequenceNumbers(
- const SequenceNumberSet& sequence_numbers) {
- for (SequenceNumberSet::const_iterator it = sequence_numbers.begin();
- it != sequence_numbers.end(); ++it) {
- AddSequenceNumber(*it);
- }
+void QuicAckNotifier::AddSequenceNumber(
+ const QuicPacketSequenceNumber& sequence_number,
+ int packet_payload_size) {
+ sequence_numbers_.insert(make_pair(sequence_number,
+ PacketInfo(packet_payload_size)));
+ ++original_packet_count_;
+ original_byte_count_ += packet_payload_size;
}
-bool QuicAckNotifier::OnAck(QuicPacketSequenceNumber sequence_number) {
+bool QuicAckNotifier::OnAck(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed) {
DCHECK(ContainsKey(sequence_numbers_, sequence_number));
sequence_numbers_.erase(sequence_number);
if (IsEmpty()) {
// We have seen all the sequence numbers we were waiting for, trigger
// callback notification.
- delegate_->OnAckNotification();
+ delegate_->OnAckNotification(
+ original_packet_count_, original_byte_count_,
+ retransmitted_packet_count_, retransmitted_byte_count_,
+ delta_largest_observed);
return true;
}
return false;
@@ -50,8 +65,21 @@ bool QuicAckNotifier::OnAck(QuicPacketSequenceNumber sequence_number) {
void QuicAckNotifier::UpdateSequenceNumber(
QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number) {
- sequence_numbers_.erase(old_sequence_number);
- sequence_numbers_.insert(new_sequence_number);
+ DCHECK(!ContainsKey(sequence_numbers_, new_sequence_number));
+
+ PacketInfo packet_info;
+ hash_map<QuicPacketSequenceNumber, PacketInfo>::iterator it =
+ sequence_numbers_.find(old_sequence_number);
+ if (it != sequence_numbers_.end()) {
+ packet_info = it->second;
+ sequence_numbers_.erase(it);
+ } else {
+ DLOG(DFATAL) << "Old sequence number not found.";
+ }
+
+ ++retransmitted_packet_count_;
+ retransmitted_byte_count_ += packet_info.packet_payload_size;
+ sequence_numbers_.insert(make_pair(new_sequence_number, packet_info));
}
}; // namespace net
diff --git a/chromium/net/quic/quic_ack_notifier.h b/chromium/net/quic/quic_ack_notifier.h
index 5ec92134e78..f19d0f8c6b9 100644
--- a/chromium/net/quic/quic_ack_notifier.h
+++ b/chromium/net/quic/quic_ack_notifier.h
@@ -5,6 +5,7 @@
#ifndef NET_QUIC_QUIC_ACK_NOTIFIER_H_
#define NET_QUIC_QUIC_ACK_NOTIFIER_H_
+#include "base/memory/ref_counted.h"
#include "net/quic/quic_protocol.h"
namespace net {
@@ -16,22 +17,35 @@ namespace net {
// trigger a call to a provided Closure.
class NET_EXPORT_PRIVATE QuicAckNotifier {
public:
- class NET_EXPORT_PRIVATE DelegateInterface {
+ class NET_EXPORT_PRIVATE DelegateInterface
+ : public base::RefCounted<DelegateInterface> {
public:
DelegateInterface();
+ // Args:
+ // num_original_packets - Number of packets in the original transmission.
+ // num_original_bytes - Number of packets in the original transmission.
+ // num_retransmitted_packets - Number of packets that had to be
+ // retransmitted.
+ // num_retransmitted_bytes - Number of bytes that had to be retransmitted.
+ virtual void OnAckNotification(int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed) = 0;
+ protected:
+ friend class base::RefCounted<DelegateInterface>;
+
+ // Delegates are ref counted.
virtual ~DelegateInterface();
- virtual void OnAckNotification() = 0;
};
+ // QuicAckNotifier is expected to keep its own reference to the delegate.
explicit QuicAckNotifier(DelegateInterface* delegate);
virtual ~QuicAckNotifier();
// Register a sequence number that this AckNotifier should be interested in.
- void AddSequenceNumber(const QuicPacketSequenceNumber& sequence_number);
-
- // Register a set of sequence numbers that this AckNotifier should be
- // interested in.
- void AddSequenceNumbers(const SequenceNumberSet& sequence_numbers);
+ void AddSequenceNumber(const QuicPacketSequenceNumber& sequence_number,
+ int packet_payload_size);
// Called by the QuicConnection on receipt of new ACK frame, with the sequence
// number referenced by the ACK frame.
@@ -41,7 +55,8 @@ class NET_EXPORT_PRIVATE QuicAckNotifier {
//
// Returns true if the provided sequence_number caused the delegate to be
// called, false otherwise.
- bool OnAck(QuicPacketSequenceNumber sequence_number);
+ bool OnAck(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed);
bool IsEmpty() { return sequence_numbers_.empty(); }
@@ -52,14 +67,33 @@ class NET_EXPORT_PRIVATE QuicAckNotifier {
QuicPacketSequenceNumber new_sequence_number);
private:
+ struct PacketInfo {
+ PacketInfo();
+ explicit PacketInfo(int payload_size);
+
+ int packet_payload_size;
+ };
+
// The delegate's OnAckNotification() method will be called once we have been
// notified of ACKs for all the sequence numbers we are tracking.
// This is not owned by OnAckNotifier and must outlive it.
- DelegateInterface* delegate_;
+ scoped_refptr<DelegateInterface> delegate_;
+
+ // Sequence numbers this notifier is waiting to hear about. The
+ // delegate will not be called until this is empty.
+ base::hash_map<QuicPacketSequenceNumber, PacketInfo> sequence_numbers_;
+
+ // Transmission and retransmission stats.
+ // Number of packets in the original transmission.
+ int original_packet_count_;
+ // Number of packets in the original transmission.
+ int original_byte_count_;
+ // Number of packets that had to be retransmitted.
+ int retransmitted_packet_count_;
+ // Number of bytes that had to be retransmitted.
+ int retransmitted_byte_count_;
- // Set of sequence numbers this notifier is waiting to hear about. The
- // delegate will not be called until this is an empty set.
- SequenceNumberSet sequence_numbers_;
+ DISALLOW_COPY_AND_ASSIGN(QuicAckNotifier);
};
}; // namespace net
diff --git a/chromium/net/quic/quic_ack_notifier_manager.cc b/chromium/net/quic/quic_ack_notifier_manager.cc
index 8676315d22f..70cb927a86d 100644
--- a/chromium/net/quic/quic_ack_notifier_manager.cc
+++ b/chromium/net/quic/quic_ack_notifier_manager.cc
@@ -23,7 +23,8 @@ AckNotifierManager::~AckNotifierManager() {
}
void AckNotifierManager::OnPacketAcked(
- QuicPacketSequenceNumber sequence_number) {
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed) {
// Inform all the registered AckNotifiers of the new ACK.
AckNotifierMap::iterator map_it = ack_notifier_map_.find(sequence_number);
if (map_it == ack_notifier_map_.end()) {
@@ -36,7 +37,7 @@ void AckNotifierManager::OnPacketAcked(
for (AckNotifierSet::iterator set_it = map_it->second.begin();
set_it != map_it->second.end(); ++set_it) {
QuicAckNotifier* ack_notifier = *set_it;
- ack_notifier->OnAck(sequence_number);
+ ack_notifier->OnAck(sequence_number, delta_largest_observed);
// If this has resulted in an empty AckNotifer, erase it.
if (ack_notifier->IsEmpty()) {
@@ -90,7 +91,8 @@ void AckNotifierManager::OnSerializedPacket(
// The AckNotifier needs to know it is tracking this packet's sequence
// number.
- notifier->AddSequenceNumber(serialized_packet.sequence_number);
+ notifier->AddSequenceNumber(serialized_packet.sequence_number,
+ serialized_packet.packet->length());
// Update the mapping in the other direction, from sequence
// number to AckNotifier.
diff --git a/chromium/net/quic/quic_ack_notifier_manager.h b/chromium/net/quic/quic_ack_notifier_manager.h
index 5ddf794e7bb..bf5b345d106 100644
--- a/chromium/net/quic/quic_ack_notifier_manager.h
+++ b/chromium/net/quic/quic_ack_notifier_manager.h
@@ -31,7 +31,6 @@ class QuicAckNotifier;
// a set of AckNotifiers and a map from sequence number to AckNotifier the sake
// of efficiency - we can quickly check the map to see if any AckNotifiers are
// interested in a given sequence number.
-
class NET_EXPORT_PRIVATE AckNotifierManager {
public:
AckNotifierManager();
@@ -40,7 +39,8 @@ class NET_EXPORT_PRIVATE AckNotifierManager {
// Called when the connection receives a new AckFrame. If |sequence_number|
// exists in ack_notifier_map_ then the corresponding AckNotifiers will have
// their OnAck method called.
- void OnPacketAcked(QuicPacketSequenceNumber sequence_number);
+ void OnPacketAcked(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed);
// If a packet has been retransmitted with a new sequence number, then this
// will be called. It updates the mapping in ack_notifier_map_, and also
@@ -71,6 +71,8 @@ class NET_EXPORT_PRIVATE AckNotifierManager {
// number, call OnAck for all mapped AckNotifiers.
// Does not own the AckNotifiers.
AckNotifierMap ack_notifier_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(AckNotifierManager);
};
} // namespace net
diff --git a/chromium/net/quic/quic_ack_notifier_test.cc b/chromium/net/quic/quic_ack_notifier_test.cc
index 8facee340f0..63bc25405b4 100644
--- a/chromium/net/quic/quic_ack_notifier_test.cc
+++ b/chromium/net/quic/quic_ack_notifier_test.cc
@@ -8,40 +8,44 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using testing::_;
+
namespace net {
namespace test {
namespace {
class QuicAckNotifierTest : public ::testing::Test {
protected:
+ QuicAckNotifierTest() : zero_(QuicTime::Delta::Zero()) {}
+
virtual void SetUp() {
- notifier_.reset(new QuicAckNotifier(&delegate_));
+ delegate_ = new MockAckNotifierDelegate;
+ notifier_.reset(new QuicAckNotifier(delegate_));
- sequence_numbers_.insert(26);
- sequence_numbers_.insert(99);
- sequence_numbers_.insert(1234);
- notifier_->AddSequenceNumbers(sequence_numbers_);
+ notifier_->AddSequenceNumber(26, 100);
+ notifier_->AddSequenceNumber(99, 20);
+ notifier_->AddSequenceNumber(1234, 3);
}
- SequenceNumberSet sequence_numbers_;
- MockAckNotifierDelegate delegate_;
+ MockAckNotifierDelegate* delegate_;
scoped_ptr<QuicAckNotifier> notifier_;
+ QuicTime::Delta zero_;
};
// Should trigger callback when we receive acks for all the registered seqnums.
TEST_F(QuicAckNotifierTest, TriggerCallback) {
- EXPECT_CALL(delegate_, OnAckNotification()).Times(1);
- EXPECT_FALSE(notifier_->OnAck(26));
- EXPECT_FALSE(notifier_->OnAck(99));
- EXPECT_TRUE(notifier_->OnAck(1234));
+ EXPECT_CALL(*delegate_, OnAckNotification(3, 123, 0, 0, zero_)).Times(1);
+ EXPECT_FALSE(notifier_->OnAck(26, zero_));
+ EXPECT_FALSE(notifier_->OnAck(99, zero_));
+ EXPECT_TRUE(notifier_->OnAck(1234, zero_));
}
// Should not trigger callback if we never provide all the seqnums.
TEST_F(QuicAckNotifierTest, DoesNotTrigger) {
// Should not trigger callback as not all packets have been seen.
- EXPECT_CALL(delegate_, OnAckNotification()).Times(0);
- EXPECT_FALSE(notifier_->OnAck(26));
- EXPECT_FALSE(notifier_->OnAck(99));
+ EXPECT_CALL(*delegate_, OnAckNotification(_, _, _, _, _)).Times(0);
+ EXPECT_FALSE(notifier_->OnAck(26, zero_));
+ EXPECT_FALSE(notifier_->OnAck(99, zero_));
}
// Should trigger even after updating sequence numbers and receiving ACKs for
@@ -51,10 +55,24 @@ TEST_F(QuicAckNotifierTest, UpdateSeqNums) {
notifier_->UpdateSequenceNumber(99, 3000);
notifier_->UpdateSequenceNumber(1234, 3001);
- EXPECT_CALL(delegate_, OnAckNotification()).Times(1);
- EXPECT_FALSE(notifier_->OnAck(26)); // original
- EXPECT_FALSE(notifier_->OnAck(3000)); // updated
- EXPECT_TRUE(notifier_->OnAck(3001)); // updated
+ EXPECT_CALL(*delegate_, OnAckNotification(3, 123, 2, 20 + 3, _)).Times(1);
+ EXPECT_FALSE(notifier_->OnAck(26, zero_)); // original
+ EXPECT_FALSE(notifier_->OnAck(3000, zero_)); // updated
+ EXPECT_TRUE(notifier_->OnAck(3001, zero_)); // updated
+}
+
+// Make sure the delegate is called with the delta time from the last ACK.
+TEST_F(QuicAckNotifierTest, DeltaTime) {
+ const QuicTime::Delta first_delta = QuicTime::Delta::FromSeconds(5);
+ const QuicTime::Delta second_delta = QuicTime::Delta::FromSeconds(33);
+ const QuicTime::Delta third_delta = QuicTime::Delta::FromSeconds(10);
+
+ EXPECT_CALL(*delegate_,
+ OnAckNotification(3, 123, 0, 0, third_delta))
+ .Times(1);
+ EXPECT_FALSE(notifier_->OnAck(26, first_delta));
+ EXPECT_FALSE(notifier_->OnAck(99, second_delta));
+ EXPECT_TRUE(notifier_->OnAck(1234, third_delta));
}
} // namespace
diff --git a/chromium/net/quic/quic_address_mismatch.cc b/chromium/net/quic/quic_address_mismatch.cc
new file mode 100644
index 00000000000..96aaef41030
--- /dev/null
+++ b/chromium/net/quic/quic_address_mismatch.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_address_mismatch.h"
+
+#include "base/logging.h"
+#include "net/base/ip_endpoint.h"
+
+namespace net {
+
+int GetAddressMismatch(const IPEndPoint& first_address,
+ const IPEndPoint& second_address) {
+ if (first_address.address().empty() || second_address.address().empty()) {
+ return -1;
+ }
+ IPAddressNumber first_ip_address = first_address.address();
+ if (IsIPv4Mapped(first_ip_address)) {
+ first_ip_address = ConvertIPv4MappedToIPv4(first_ip_address);
+ }
+ IPAddressNumber second_ip_address = second_address.address();
+ if (IsIPv4Mapped(second_ip_address)) {
+ second_ip_address = ConvertIPv4MappedToIPv4(second_ip_address);
+ }
+
+ int sample;
+ if (first_ip_address != second_ip_address) {
+ sample = QUIC_ADDRESS_MISMATCH_BASE;
+ } else if (first_address.port() != second_address.port()) {
+ sample = QUIC_PORT_MISMATCH_BASE;
+ } else {
+ sample = QUIC_ADDRESS_AND_PORT_MATCH_BASE;
+ }
+
+ // Add an offset to |sample|:
+ // V4_V4: add 0
+ // V6_V6: add 1
+ // V4_V6: add 2
+ // V6_V4: add 3
+ bool first_ipv4 = (first_ip_address.size() == kIPv4AddressSize);
+ bool second_ipv4 = (second_ip_address.size() == kIPv4AddressSize);
+ if (first_ipv4 != second_ipv4) {
+ CHECK_EQ(sample, QUIC_ADDRESS_MISMATCH_BASE);
+ sample += 2;
+ }
+ if (!first_ipv4) {
+ sample += 1;
+ }
+ return sample;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_address_mismatch.h b/chromium/net/quic/quic_address_mismatch.h
new file mode 100644
index 00000000000..3179c20a93a
--- /dev/null
+++ b/chromium/net/quic/quic_address_mismatch.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_ADDRESS_MISMATCH_H_
+#define NET_QUIC_QUIC_ADDRESS_MISMATCH_H_
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+class IPEndPoint;
+
+enum QuicAddressMismatch {
+ // The addresses don't match.
+ QUIC_ADDRESS_MISMATCH_BASE = 0,
+ QUIC_ADDRESS_MISMATCH_V4_V4 = 0,
+ QUIC_ADDRESS_MISMATCH_V6_V6 = 1,
+ QUIC_ADDRESS_MISMATCH_V4_V6 = 2,
+ QUIC_ADDRESS_MISMATCH_V6_V4 = 3,
+
+ // The addresses match, but the ports don't match.
+ QUIC_PORT_MISMATCH_BASE = 4,
+ QUIC_PORT_MISMATCH_V4_V4 = 4,
+ QUIC_PORT_MISMATCH_V6_V6 = 5,
+
+ QUIC_ADDRESS_AND_PORT_MATCH_BASE = 6,
+ QUIC_ADDRESS_AND_PORT_MATCH_V4_V4 = 6,
+ QUIC_ADDRESS_AND_PORT_MATCH_V6_V6 = 7,
+
+ QUIC_ADDRESS_MISMATCH_MAX,
+};
+
+// Returns a value of the QuicAddressMismatch enum type that indicates how
+// |first_address| differs from |second_address|. Returns -1 if either address
+// is empty.
+//
+// Only used by the Net.QuicSession.PublicResetAddressMismatch histogram.
+NET_EXPORT_PRIVATE int GetAddressMismatch(const IPEndPoint& first_address,
+ const IPEndPoint& second_address);
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_ADDRESS_MISMATCH_H_
diff --git a/chromium/net/quic/quic_address_mismatch_test.cc b/chromium/net/quic/quic_address_mismatch_test.cc
new file mode 100644
index 00000000000..b7c683b5a48
--- /dev/null
+++ b/chromium/net/quic/quic_address_mismatch_test.cc
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_address_mismatch.h"
+
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// Test all cases of the GetAddressMismatch function.
+TEST(QuicAddressMismatchTest, GetAddressMismatch) {
+ IPAddressNumber ip4_1;
+ IPAddressNumber ip4_2;
+ IPAddressNumber ip6_1;
+ IPAddressNumber ip6_2;
+ IPAddressNumber ip4_mapped_1;
+ IPAddressNumber ip4_mapped_2;
+ ASSERT_TRUE(ParseIPLiteralToNumber("1.2.3.4", &ip4_1));
+ ASSERT_TRUE(ParseIPLiteralToNumber("5.6.7.8", &ip4_2));
+ ASSERT_TRUE(ParseIPLiteralToNumber("1234::1", &ip6_1));
+ ASSERT_TRUE(ParseIPLiteralToNumber("1234::2", &ip6_2));
+ ip4_mapped_1 = ConvertIPv4NumberToIPv6Number(ip4_1);
+ ip4_mapped_2 = ConvertIPv4NumberToIPv6Number(ip4_2);
+ ASSERT_NE(ip4_1, ip4_2);
+ ASSERT_NE(ip6_1, ip6_2);
+ ASSERT_NE(ip4_mapped_1, ip4_mapped_2);
+
+ EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(), IPEndPoint()));
+ EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(), IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(ip4_1, 443), IPEndPoint()));
+
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip6_1, 443)));
+
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip6_1, 443)));
+
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip6_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip6_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip4_mapped_1, 443)));
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_bandwidth.h b/chromium/net/quic/quic_bandwidth.h
index 6412aa5c68d..4ae953d00ec 100644
--- a/chromium/net/quic/quic_bandwidth.h
+++ b/chromium/net/quic/quic_bandwidth.h
@@ -15,7 +15,6 @@ namespace net {
typedef uint64 QuicByteCount;
class NET_EXPORT_PRIVATE QuicBandwidth {
-
public:
// Creates a new QuicBandwidth with an internal value of 0.
static QuicBandwidth Zero();
diff --git a/chromium/net/quic/quic_blocked_writer_interface.h b/chromium/net/quic/quic_blocked_writer_interface.h
index a694193fe31..a6f6faf8175 100644
--- a/chromium/net/quic/quic_blocked_writer_interface.h
+++ b/chromium/net/quic/quic_blocked_writer_interface.h
@@ -18,9 +18,8 @@ class NET_EXPORT_PRIVATE QuicBlockedWriterInterface {
virtual ~QuicBlockedWriterInterface() {}
// Called by the PacketWriter when the underlying socket becomes writable
- // so that the BlockedWriter can go ahead and try writing. This methods
- // should return false if the socket has become blocked while writing.
- virtual bool OnCanWrite() = 0;
+ // so that the BlockedWriter can go ahead and try writing.
+ virtual void OnCanWrite() = 0;
};
} // namespace net
diff --git a/chromium/net/quic/quic_client_session.cc b/chromium/net/quic/quic_client_session.cc
index ab70e7f445c..745692c2e12 100644
--- a/chromium/net/quic/quic_client_session.cc
+++ b/chromium/net/quic/quic_client_session.cc
@@ -13,10 +13,14 @@
#include "base/values.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
+#include "net/quic/crypto/proof_verifier_chromium.h"
+#include "net/quic/crypto/quic_server_info.h"
#include "net/quic/quic_connection_helper.h"
#include "net/quic/quic_crypto_client_stream_factory.h"
#include "net/quic/quic_default_packet_writer.h"
+#include "net/quic/quic_server_id.h"
#include "net/quic/quic_stream_factory.h"
+#include "net/ssl/ssl_connection_status_flags.h"
#include "net/ssl/ssl_info.h"
#include "net/udp/datagram_client_socket.h"
@@ -24,6 +28,53 @@ namespace net {
namespace {
+// The length of time to wait for a 0-RTT handshake to complete
+// before allowing the requests to possibly proceed over TCP.
+const int k0RttHandshakeTimeoutMs = 300;
+
+// Histograms for tracking down the crashes from http://crbug.com/354669
+// Note: these values must be kept in sync with the corresponding values in:
+// tools/metrics/histograms/histograms.xml
+enum Location {
+ DESTRUCTOR = 0,
+ ADD_OBSERVER = 1,
+ TRY_CREATE_STREAM = 2,
+ CREATE_OUTGOING_RELIABLE_STREAM = 3,
+ NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER = 4,
+ NOTIFY_FACTORY_OF_SESSION_CLOSED = 5,
+ NUM_LOCATIONS = 6,
+};
+
+void RecordUnexpectedOpenStreams(Location location) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedOpenStreams", location,
+ NUM_LOCATIONS);
+}
+
+void RecordUnexpectedObservers(Location location) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedObservers", location,
+ NUM_LOCATIONS);
+}
+
+void RecordUnexpectedNotGoingAway(Location location) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedNotGoingAway", location,
+ NUM_LOCATIONS);
+}
+
+// Histogram for recording the different reasons that a QUIC session is unable
+// to complete the handshake.
+enum HandshakeFailureReason {
+ HANDSHAKE_FAILURE_UNKNOWN = 0,
+ HANDSHAKE_FAILURE_BLACK_HOLE = 1,
+ HANDSHAKE_FAILURE_PUBLIC_RESET = 2,
+ NUM_HANDSHAKE_FAILURE_REASONS = 3,
+};
+
+void RecordHandshakeFailureReason(HandshakeFailureReason reason) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Net.QuicSession.ConnectionClose.HandshakeNotConfirmed.Reason",
+ reason, NUM_HANDSHAKE_FAILURE_REASONS);
+}
+
// Note: these values must be kept in sync with the corresponding values in:
// tools/metrics/histograms/histograms.xml
enum HandshakeState {
@@ -86,49 +137,74 @@ QuicClientSession::QuicClientSession(
scoped_ptr<QuicDefaultPacketWriter> writer,
QuicStreamFactory* stream_factory,
QuicCryptoClientStreamFactory* crypto_client_stream_factory,
- const string& server_hostname,
+ scoped_ptr<QuicServerInfo> server_info,
+ const QuicServerId& server_id,
const QuicConfig& config,
QuicCryptoClientConfig* crypto_config,
+ base::TaskRunner* task_runner,
NetLog* net_log)
- : QuicSession(connection, config),
+ : QuicClientSessionBase(connection,
+ config),
require_confirmation_(false),
stream_factory_(stream_factory),
socket_(socket.Pass()),
writer_(writer.Pass()),
read_buffer_(new IOBufferWithSize(kMaxPacketSize)),
+ server_info_(server_info.Pass()),
read_pending_(false),
num_total_streams_(0),
+ task_runner_(task_runner),
net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)),
logger_(net_log_),
num_packets_read_(0),
+ going_away_(false),
weak_factory_(this) {
crypto_stream_.reset(
crypto_client_stream_factory ?
crypto_client_stream_factory->CreateQuicCryptoClientStream(
- server_hostname, this, crypto_config) :
- new QuicCryptoClientStream(server_hostname, this, crypto_config));
+ server_id, this, crypto_config) :
+ new QuicCryptoClientStream(server_id, this,
+ new ProofVerifyContextChromium(net_log_),
+ crypto_config));
connection->set_debug_visitor(&logger_);
// TODO(rch): pass in full host port proxy pair
net_log_.BeginEvent(
NetLog::TYPE_QUIC_SESSION,
- NetLog::StringCallback("host", &server_hostname));
+ NetLog::StringCallback("host", &server_id.host()));
}
QuicClientSession::~QuicClientSession() {
- // The session must be closed before it is destroyed.
- DCHECK(streams()->empty());
- CloseAllStreams(ERR_UNEXPECTED);
- DCHECK(observers_.empty());
- CloseAllObservers(ERR_UNEXPECTED);
-
- connection()->set_debug_visitor(NULL);
- net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION);
+ if (!streams()->empty())
+ RecordUnexpectedOpenStreams(DESTRUCTOR);
+ if (!observers_.empty())
+ RecordUnexpectedObservers(DESTRUCTOR);
+ if (!going_away_)
+ RecordUnexpectedNotGoingAway(DESTRUCTOR);
+
+ while (!streams()->empty() ||
+ !observers_.empty() ||
+ !stream_requests_.empty()) {
+ // The session must be closed before it is destroyed.
+ DCHECK(streams()->empty());
+ CloseAllStreams(ERR_UNEXPECTED);
+ DCHECK(observers_.empty());
+ CloseAllObservers(ERR_UNEXPECTED);
+
+ connection()->set_debug_visitor(NULL);
+ net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION);
+
+ while (!stream_requests_.empty()) {
+ StreamRequest* request = stream_requests_.front();
+ stream_requests_.pop_front();
+ request->OnRequestCompleteFailure(ERR_ABORTED);
+ }
+ }
- while (!stream_requests_.empty()) {
- StreamRequest* request = stream_requests_.front();
- stream_requests_.pop_front();
- request->OnRequestCompleteFailure(ERR_ABORTED);
+ if (connection()->connected()) {
+ // Ensure that the connection is closed by the time the session is
+ // destroyed.
+ connection()->CloseConnection(QUIC_INTERNAL_ERROR, false);
}
if (IsEncryptionEstablished())
@@ -138,17 +214,59 @@ QuicClientSession::~QuicClientSession() {
else
RecordHandshakeState(STATE_FAILED);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_);
UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellos",
crypto_stream_->num_sent_client_hellos());
- if (IsCryptoHandshakeConfirmed()) {
- UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellosCryptoHandshakeConfirmed",
- crypto_stream_->num_sent_client_hellos());
- }
+ if (!IsCryptoHandshakeConfirmed())
+ return;
- UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_);
+ // Sending one client_hello means we had zero handshake-round-trips.
+ int round_trip_handshakes = crypto_stream_->num_sent_client_hellos() - 1;
+
+ // Don't bother with these histogram during tests, which mock out
+ // num_sent_client_hellos().
+ if (round_trip_handshakes < 0 || !stream_factory_)
+ return;
+
+ bool port_selected = stream_factory_->enable_port_selection();
+ SSLInfo ssl_info;
+ if (!GetSSLInfo(&ssl_info) || !ssl_info.cert) {
+ if (port_selected) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTP",
+ round_trip_handshakes, 0, 3, 4);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTP",
+ round_trip_handshakes, 0, 3, 4);
+ }
+ } else {
+ if (port_selected) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTPS",
+ round_trip_handshakes, 0, 3, 4);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTPS",
+ round_trip_handshakes, 0, 3, 4);
+ }
+ }
+ const QuicConnectionStats stats = connection()->GetStats();
+ if (stats.max_sequence_reordering == 0)
+ return;
+ const uint64 kMaxReordering = 100;
+ uint64 reordering = kMaxReordering;
+ if (stats.min_rtt_us > 0 ) {
+ reordering =
+ GG_UINT64_C(100) * stats.max_time_reordering_us / stats.min_rtt_us;
+ }
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTime",
+ reordering, 0, kMaxReordering, 50);
+ if (stats.min_rtt_us > 100 * 1000) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTimeLongRtt",
+ reordering, 0, kMaxReordering, 50);
+ }
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.MaxReordering",
+ stats.max_sequence_reordering);
}
-bool QuicClientSession::OnStreamFrames(
+void QuicClientSession::OnStreamFrames(
const std::vector<QuicStreamFrame>& frames) {
// Record total number of stream frames.
UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesInPacket", frames.size());
@@ -169,6 +287,12 @@ bool QuicClientSession::OnStreamFrames(
}
void QuicClientSession::AddObserver(Observer* observer) {
+ if (going_away_) {
+ RecordUnexpectedObservers(ADD_OBSERVER);
+ observer->OnSessionClosed(ERR_UNEXPECTED);
+ return;
+ }
+
DCHECK(!ContainsKey(observers_, observer));
observers_.insert(observer);
}
@@ -195,6 +319,11 @@ int QuicClientSession::TryCreateStream(StreamRequest* request,
return ERR_CONNECTION_CLOSED;
}
+ if (going_away_) {
+ RecordUnexpectedOpenStreams(TRY_CREATE_STREAM);
+ return ERR_CONNECTION_CLOSED;
+ }
+
if (GetNumOpenStreams() < get_max_open_streams()) {
*stream = CreateOutgoingReliableStreamImpl();
return OK;
@@ -221,15 +350,18 @@ QuicReliableClientStream* QuicClientSession::CreateOutgoingDataStream() {
}
if (GetNumOpenStreams() >= get_max_open_streams()) {
DVLOG(1) << "Failed to create a new outgoing stream. "
- << "Already " << GetNumOpenStreams() << " open.";
+ << "Already " << GetNumOpenStreams() << " open.";
return NULL;
}
if (goaway_received()) {
DVLOG(1) << "Failed to create a new outgoing stream. "
- << "Already received goaway.";
+ << "Already received goaway.";
+ return NULL;
+ }
+ if (going_away_) {
+ RecordUnexpectedOpenStreams(CREATE_OUTGOING_RELIABLE_STREAM);
return NULL;
}
-
return CreateOutgoingReliableStreamImpl();
}
@@ -248,14 +380,60 @@ QuicCryptoClientStream* QuicClientSession::GetCryptoStream() {
return crypto_stream_.get();
};
-bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) {
- DCHECK(crypto_stream_.get());
- return crypto_stream_->GetSSLInfo(ssl_info);
+// TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways
+// we learn about SSL info (sync vs async vs cached).
+bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) const {
+ ssl_info->Reset();
+ if (!cert_verify_result_) {
+ return false;
+ }
+
+ ssl_info->cert_status = cert_verify_result_->cert_status;
+ ssl_info->cert = cert_verify_result_->verified_cert;
+
+ // TODO(wtc): Define QUIC "cipher suites".
+ // Report the TLS cipher suite that most closely resembles the crypto
+ // parameters of the QUIC connection.
+ QuicTag aead = crypto_stream_->crypto_negotiated_params().aead;
+ int cipher_suite;
+ int security_bits;
+ switch (aead) {
+ case kAESG:
+ cipher_suite = 0xc02f; // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ security_bits = 128;
+ break;
+ case kCC12:
+ cipher_suite = 0xcc13; // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ security_bits = 256;
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+ int ssl_connection_status = 0;
+ ssl_connection_status |=
+ (cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK) <<
+ SSL_CONNECTION_CIPHERSUITE_SHIFT;
+ ssl_connection_status |=
+ (SSL_CONNECTION_VERSION_QUIC & SSL_CONNECTION_VERSION_MASK) <<
+ SSL_CONNECTION_VERSION_SHIFT;
+
+ ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes;
+ ssl_info->is_issued_by_known_root =
+ cert_verify_result_->is_issued_by_known_root;
+
+ ssl_info->connection_status = ssl_connection_status;
+ ssl_info->client_cert_sent = false;
+ ssl_info->channel_id_sent = false;
+ ssl_info->security_bits = security_bits;
+ ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL;
+ return true;
}
int QuicClientSession::CryptoConnect(bool require_confirmation,
const CompletionCallback& callback) {
require_confirmation_ = require_confirmation;
+ handshake_start_ = base::TimeTicks::Now();
RecordHandshakeState(STATE_STARTED);
if (!crypto_stream_->CryptoConnect()) {
// TODO(wtc): change crypto_stream_.CryptoConnect() to return a
@@ -263,20 +441,64 @@ int QuicClientSession::CryptoConnect(bool require_confirmation,
return ERR_CONNECTION_FAILED;
}
- bool can_notify = require_confirmation_ ?
- IsCryptoHandshakeConfirmed() : IsEncryptionEstablished();
- if (can_notify) {
+ if (IsCryptoHandshakeConfirmed())
+ return OK;
+
+ // Unless we require handshake confirmation, activate the session if
+ // we have established initial encryption.
+ if (!require_confirmation_ && IsEncryptionEstablished()) {
+ // To mitigate the effects of hanging 0-RTT connections, set up a timer to
+ // cancel any requests, if the handshake takes too long.
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&QuicClientSession::OnConnectTimeout,
+ weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(k0RttHandshakeTimeoutMs));
return OK;
+
}
callback_ = callback;
return ERR_IO_PENDING;
}
+int QuicClientSession::ResumeCryptoConnect(const CompletionCallback& callback) {
+
+ if (IsCryptoHandshakeConfirmed())
+ return OK;
+
+ if (!connection()->connected())
+ return ERR_QUIC_HANDSHAKE_FAILED;
+
+ callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
int QuicClientSession::GetNumSentClientHellos() const {
return crypto_stream_->num_sent_client_hellos();
}
+bool QuicClientSession::CanPool(const std::string& hostname) const {
+ // TODO(rch): When QUIC supports channel ID or client certificates, this
+ // logic will need to be revised.
+ DCHECK(connection()->connected());
+ SSLInfo ssl_info;
+ if (!GetSSLInfo(&ssl_info) || !ssl_info.cert) {
+ // We can always pool with insecure QUIC sessions.
+ return true;
+ }
+
+ // Disable pooling for secure sessions.
+ // TODO(rch): re-enable this.
+ return false;
+
+#if 0
+ bool unused = false;
+ // Only pool secure QUIC sessions if the cert matches the new hostname.
+ return ssl_info.cert->VerifyNameMatch(hostname, &unused);
+#endif
+}
+
QuicDataStream* QuicClientSession::CreateIncomingDataStream(
QuicStreamId id) {
DLOG(ERROR) << "Server push not supported";
@@ -284,13 +506,20 @@ QuicDataStream* QuicClientSession::CreateIncomingDataStream(
}
void QuicClientSession::CloseStream(QuicStreamId stream_id) {
+ ReliableQuicStream* stream = GetStream(stream_id);
+ if (stream) {
+ logger_.UpdateReceivedFrameCounts(
+ stream_id, stream->num_frames_received(),
+ stream->num_duplicate_frames_received());
+ }
QuicSession::CloseStream(stream_id);
OnClosedStream();
}
void QuicClientSession::SendRstStream(QuicStreamId id,
- QuicRstStreamErrorCode error) {
- QuicSession::SendRstStream(id, error);
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ QuicSession::SendRstStream(id, error, bytes_written);
OnClosedStream();
}
@@ -299,6 +528,7 @@ void QuicClientSession::OnClosedStream() {
!stream_requests_.empty() &&
crypto_stream_->encryption_established() &&
!goaway_received() &&
+ !going_away_ &&
connection()->connected()) {
StreamRequest* request = stream_requests_.front();
stream_requests_.pop_front();
@@ -320,6 +550,8 @@ void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
base::ResetAndReturn(&callback_).Run(OK);
}
if (event == HANDSHAKE_CONFIRMED) {
+ UMA_HISTOGRAM_TIMES("Net.QuicSession.HandshakeConfirmedTime",
+ base::TimeTicks::Now() - handshake_start_);
ObserverSet::iterator it = observers_.begin();
while (it != observers_.end()) {
Observer* observer = *it;
@@ -357,14 +589,31 @@ void QuicClientSession::OnConnectionClosed(QuicErrorCode error,
"Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut",
GetNumOpenStreams());
if (!IsCryptoHandshakeConfirmed()) {
- // If there have been any streams created, they were 0-RTT speculative
- // requests that have not be serviced.
+ UMA_HISTOGRAM_COUNTS(
+ "Net.QuicSession.ConnectionClose.NumOpenStreams.HandshakeTimedOut",
+ GetNumOpenStreams());
UMA_HISTOGRAM_COUNTS(
"Net.QuicSession.ConnectionClose.NumTotalStreams.HandshakeTimedOut",
num_total_streams_);
}
}
+ if (!IsCryptoHandshakeConfirmed()) {
+ if (error == QUIC_PUBLIC_RESET) {
+ RecordHandshakeFailureReason(HANDSHAKE_FAILURE_PUBLIC_RESET);
+ } else if (connection()->GetStats().packets_received == 0) {
+ RecordHandshakeFailureReason(HANDSHAKE_FAILURE_BLACK_HOLE);
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Net.QuicSession.ConnectionClose.HandshakeFailureBlackHole.QuicError",
+ error);
+ } else {
+ RecordHandshakeFailureReason(HANDSHAKE_FAILURE_UNKNOWN);
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Net.QuicSession.ConnectionClose.HandshakeFailureUnknown.QuicError",
+ error);
+ }
+ }
+
UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion",
connection()->version());
NotifyFactoryOfSessionGoingAway();
@@ -385,6 +634,34 @@ void QuicClientSession::OnSuccessfulVersionNegotiation(
QuicSession::OnSuccessfulVersionNegotiation(version);
}
+void QuicClientSession::OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) {
+ DCHECK(cached.proof_valid());
+
+ if (!server_info_ || !server_info_->IsReadyToPersist()) {
+ return;
+ }
+
+ QuicServerInfo::State* state = server_info_->mutable_state();
+
+ state->server_config = cached.server_config();
+ state->source_address_token = cached.source_address_token();
+ state->server_config_sig = cached.signature();
+ state->certs = cached.certs();
+
+ server_info_->Persist();
+}
+
+void QuicClientSession::OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) {
+ const CertVerifyResult* cert_verify_result_other =
+ &(reinterpret_cast<const ProofVerifyDetailsChromium*>(
+ &verify_details))->cert_verify_result;
+ CertVerifyResult* result_copy = new CertVerifyResult;
+ result_copy->CopyFrom(*cert_verify_result_other);
+ cert_verify_result_.reset(result_copy);
+}
+
void QuicClientSession::StartReading() {
if (read_pending_) {
return;
@@ -402,8 +679,8 @@ void QuicClientSession::StartReading() {
if (++num_packets_read_ > 32) {
num_packets_read_ = 0;
// Data was read, process it.
- // Schedule the work through the message loop to avoid recursive
- // callbacks.
+ // Schedule the work through the message loop to 1) prevent infinite
+ // recursion and 2) avoid blocking the thread for too long.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&QuicClientSession::OnReadComplete,
@@ -430,7 +707,8 @@ void QuicClientSession::CloseSessionOnErrorInner(int net_error,
NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR,
NetLog::IntegerCallback("net_error", net_error));
- connection()->CloseConnection(quic_error, false);
+ if (connection()->connected())
+ connection()->CloseConnection(quic_error, false);
DCHECK(!connection()->connected());
}
@@ -451,15 +729,41 @@ void QuicClientSession::CloseAllObservers(int net_error) {
}
}
-base::Value* QuicClientSession::GetInfoAsValue(const HostPortPair& pair) const {
+base::Value* QuicClientSession::GetInfoAsValue(
+ const std::set<HostPortPair>& aliases) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->SetString("host_port_pair", pair.ToString());
+ // TODO(rch): remove "host_port_pair" when Chrome 34 is stable.
+ dict->SetString("host_port_pair", aliases.begin()->ToString());
dict->SetString("version", QuicVersionToString(connection()->version()));
dict->SetInteger("open_streams", GetNumOpenStreams());
+ base::ListValue* stream_list = new base::ListValue();
+ for (base::hash_map<QuicStreamId, QuicDataStream*>::const_iterator it
+ = streams()->begin();
+ it != streams()->end();
+ ++it) {
+ stream_list->Append(new base::StringValue(
+ base::Uint64ToString(it->second->id())));
+ }
+ dict->Set("active_streams", stream_list);
+
dict->SetInteger("total_streams", num_total_streams_);
dict->SetString("peer_address", peer_address().ToString());
- dict->SetString("guid", base::Uint64ToString(guid()));
+ dict->SetString("connection_id", base::Uint64ToString(connection_id()));
dict->SetBoolean("connected", connection()->connected());
+ const QuicConnectionStats& stats = connection()->GetStats();
+ dict->SetInteger("packets_sent", stats.packets_sent);
+ dict->SetInteger("packets_received", stats.packets_received);
+ dict->SetInteger("packets_lost", stats.packets_lost);
+ SSLInfo ssl_info;
+ dict->SetBoolean("secure", GetSSLInfo(&ssl_info) && ssl_info.cert);
+
+ base::ListValue* alias_list = new base::ListValue();
+ for (std::set<HostPortPair>::const_iterator it = aliases.begin();
+ it != aliases.end(); it++) {
+ alias_list->Append(new base::StringValue(it->ToString()));
+ }
+ dict->Set("aliases", alias_list);
+
return dict;
}
@@ -481,9 +785,7 @@ void QuicClientSession::OnReadComplete(int result) {
return;
}
- scoped_refptr<IOBufferWithSize> buffer(read_buffer_);
- read_buffer_ = new IOBufferWithSize(kMaxPacketSize);
- QuicEncryptedPacket packet(buffer->data(), result);
+ QuicEncryptedPacket packet(read_buffer_->data(), result);
IPEndPoint local_address;
IPEndPoint peer_address;
socket_->GetLocalAddress(&local_address);
@@ -492,18 +794,26 @@ void QuicClientSession::OnReadComplete(int result) {
// use a weak pointer to be safe.
connection()->ProcessUdpPacket(local_address, peer_address, packet);
if (!connection()->connected()) {
- stream_factory_->OnSessionClosed(this);
+ NotifyFactoryOfSessionClosedLater();
return;
}
StartReading();
}
void QuicClientSession::NotifyFactoryOfSessionGoingAway() {
+ going_away_ = true;
if (stream_factory_)
stream_factory_->OnSessionGoingAway(this);
}
void QuicClientSession::NotifyFactoryOfSessionClosedLater() {
+ if (!streams()->empty())
+ RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER);
+
+ if (!going_away_)
+ RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER);
+
+ going_away_ = true;
DCHECK_EQ(0u, GetNumOpenStreams());
DCHECK(!connection()->connected());
base::MessageLoop::current()->PostTask(
@@ -513,10 +823,31 @@ void QuicClientSession::NotifyFactoryOfSessionClosedLater() {
}
void QuicClientSession::NotifyFactoryOfSessionClosed() {
+ if (!streams()->empty())
+ RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED);
+
+ if (!going_away_)
+ RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED);
+
+ going_away_ = true;
DCHECK_EQ(0u, GetNumOpenStreams());
// Will delete |this|.
if (stream_factory_)
stream_factory_->OnSessionClosed(this);
}
+void QuicClientSession::OnConnectTimeout() {
+ DCHECK(callback_.is_null());
+ DCHECK(IsEncryptionEstablished());
+
+ if (IsCryptoHandshakeConfirmed())
+ return;
+
+ // TODO(rch): re-enable this code once beta is cut.
+ // if (stream_factory_)
+ // stream_factory_->OnSessionConnectTimeout(this);
+ // CloseAllStreams(ERR_QUIC_HANDSHAKE_FAILED);
+ // DCHECK_EQ(0u, GetNumOpenStreams());
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_client_session.h b/chromium/net/quic/quic_client_session.h
index 07da96aa96f..7fbe3db5cd1 100644
--- a/chromium/net/quic/quic_client_session.h
+++ b/chromium/net/quic/quic_client_session.h
@@ -12,20 +12,27 @@
#include <string>
+#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
#include "net/base/completion_callback.h"
+#include "net/proxy/proxy_server.h"
+#include "net/quic/quic_client_session_base.h"
#include "net/quic/quic_connection_logger.h"
#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_protocol.h"
#include "net/quic/quic_reliable_client_stream.h"
-#include "net/quic/quic_session.h"
namespace net {
+class CertVerifyResult;
class DatagramClientSocket;
class QuicConnectionHelper;
class QuicCryptoClientStreamFactory;
class QuicDefaultPacketWriter;
+class QuicServerId;
+class QuicServerInfo;
class QuicStreamFactory;
class SSLInfo;
@@ -33,7 +40,7 @@ namespace test {
class QuicClientSessionPeer;
} // namespace test
-class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
+class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase {
public:
// An interface for observing events on a session.
class NET_EXPORT_PRIVATE Observer {
@@ -88,11 +95,12 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
scoped_ptr<QuicDefaultPacketWriter> writer,
QuicStreamFactory* stream_factory,
QuicCryptoClientStreamFactory* crypto_client_stream_factory,
- const std::string& server_hostname,
+ scoped_ptr<QuicServerInfo> server_info,
+ const QuicServerId& server_id,
const QuicConfig& config,
QuicCryptoClientConfig* crypto_config,
+ base::TaskRunner* task_runner,
NetLog* net_log);
-
virtual ~QuicClientSession();
void AddObserver(Observer* observer);
@@ -112,19 +120,26 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
void CancelRequest(StreamRequest* request);
// QuicSession methods:
- virtual bool OnStreamFrames(
+ virtual void OnStreamFrames(
const std::vector<QuicStreamFrame>& frames) OVERRIDE;
virtual QuicReliableClientStream* CreateOutgoingDataStream() OVERRIDE;
virtual QuicCryptoClientStream* GetCryptoStream() OVERRIDE;
virtual void CloseStream(QuicStreamId stream_id) OVERRIDE;
virtual void SendRstStream(QuicStreamId id,
- QuicRstStreamErrorCode error) OVERRIDE;
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) OVERRIDE;
virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) OVERRIDE;
virtual void OnCryptoHandshakeMessageSent(
const CryptoHandshakeMessage& message) OVERRIDE;
virtual void OnCryptoHandshakeMessageReceived(
const CryptoHandshakeMessage& message) OVERRIDE;
- virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE;
+ virtual bool GetSSLInfo(SSLInfo* ssl_info) const OVERRIDE;
+
+ // QuicClientSessionBase methods:
+ virtual void OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) OVERRIDE;
+ virtual void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) OVERRIDE;
// QuicConnectionVisitorInterface methods:
virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
@@ -135,6 +150,9 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
int CryptoConnect(bool require_confirmation,
const CompletionCallback& callback);
+ // Resumes a crypto handshake with the server after a timeout.
+ int ResumeCryptoConnect(const CompletionCallback& callback);
+
// Causes the QuicConnectionHelper to start reading from the socket
// and passing the data along to the QuicConnection.
void StartReading();
@@ -143,7 +161,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
// that this session has been closed, which will delete the session.
void CloseSessionOnError(int error);
- base::Value* GetInfoAsValue(const HostPortPair& pair) const;
+ base::Value* GetInfoAsValue(const std::set<HostPortPair>& aliases);
const BoundNetLog& net_log() const { return net_log_; }
@@ -154,6 +172,11 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
// than the number of round-trips needed for the handshake.
int GetNumSentClientHellos() const;
+ // Returns true if |hostname| may be pooled onto this session. If this
+ // is a secure QUIC session, then |hostname| must match the certificate
+ // presented during the handshake.
+ bool CanPool(const std::string& hostname) const;
+
protected:
// QuicSession methods:
virtual QuicDataStream* CreateIncomingDataStream(QuicStreamId id) OVERRIDE;
@@ -194,21 +217,30 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
// delete |this|.
void NotifyFactoryOfSessionClosed();
+ void OnConnectTimeout();
+
bool require_confirmation_;
scoped_ptr<QuicCryptoClientStream> crypto_stream_;
QuicStreamFactory* stream_factory_;
scoped_ptr<DatagramClientSocket> socket_;
scoped_ptr<QuicDefaultPacketWriter> writer_;
scoped_refptr<IOBufferWithSize> read_buffer_;
+ scoped_ptr<QuicServerInfo> server_info_;
+ scoped_ptr<CertVerifyResult> cert_verify_result_;
ObserverSet observers_;
StreamRequestQueue stream_requests_;
bool read_pending_;
CompletionCallback callback_;
size_t num_total_streams_;
+ base::TaskRunner* task_runner_;
BoundNetLog net_log_;
+ base::TimeTicks handshake_start_; // Time the handshake was started.
QuicConnectionLogger logger_;
// Number of packets read in the current read loop.
size_t num_packets_read_;
+ // True when the session is going away, and streams may no longer be created
+ // on this session. Existing stream will continue to be processed.
+ bool going_away_;
base::WeakPtrFactory<QuicClientSession> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(QuicClientSession);
diff --git a/chromium/net/quic/quic_client_session_base.cc b/chromium/net/quic/quic_client_session_base.cc
new file mode 100644
index 00000000000..4343011437e
--- /dev/null
+++ b/chromium/net/quic/quic_client_session_base.cc
@@ -0,0 +1,16 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_client_session_base.h"
+
+namespace net {
+
+QuicClientSessionBase::QuicClientSessionBase(
+ QuicConnection* connection,
+ const QuicConfig& config)
+ : QuicSession(connection, config) {}
+
+QuicClientSessionBase::~QuicClientSessionBase() {}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_client_session_base.h b/chromium/net/quic/quic_client_session_base.h
new file mode 100644
index 00000000000..eab5d08df98
--- /dev/null
+++ b/chromium/net/quic/quic_client_session_base.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_
+#define NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_
+
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_session.h"
+
+namespace net {
+
+// Base class for all client-specific QuicSession subclasses.
+class NET_EXPORT_PRIVATE QuicClientSessionBase : public QuicSession {
+ public:
+ QuicClientSessionBase(QuicConnection* connection,
+ const QuicConfig& config);
+
+ virtual ~QuicClientSessionBase();
+
+ // Called when the proof in |cached| is marked valid. If this is a secure
+ // QUIC session, then this will happen only after the proof verifier
+ // completes. If this is an insecure QUIC connection, this will happen
+ // as soon as a valid config is discovered (either from the cache or
+ // from the server).
+ virtual void OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) = 0;
+
+ // Called when proof verification details become available, either because
+ // proof verification is complete, or when cached details are used. This
+ // will only be called for secure QUIC connections.
+ virtual void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicClientSessionBase);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_
diff --git a/chromium/net/quic/quic_client_session_test.cc b/chromium/net/quic/quic_client_session_test.cc
index f0b4dd4ecab..264fbfa5270 100644
--- a/chromium/net/quic/quic_client_session_test.cc
+++ b/chromium/net/quic/quic_client_session_test.cc
@@ -13,10 +13,12 @@
#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_server_info.h"
#include "net/quic/quic_default_packet_writer.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_client_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simple_quic_framer.h"
#include "net/socket/socket_test_util.h"
#include "net/udp/datagram_client_socket.h"
@@ -27,24 +29,21 @@ namespace test {
namespace {
const char kServerHostname[] = "www.example.com";
+const uint16 kServerPort = 80;
class TestPacketWriter : public QuicDefaultPacketWriter {
public:
- TestPacketWriter() {
- }
+ TestPacketWriter(QuicVersion version) : version_(version) {}
// QuicPacketWriter
virtual WriteResult WritePacket(
const char* buffer, size_t buf_len,
const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) OVERRIDE {
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), true);
- FramerVisitorCapturingFrames visitor;
- framer.set_visitor(&visitor);
+ const IPEndPoint& peer_address) OVERRIDE {
+ SimpleQuicFramer framer(SupportedVersions(version_));
QuicEncryptedPacket packet(buffer, buf_len);
EXPECT_TRUE(framer.ProcessPacket(packet));
- header_ = *visitor.header();
+ header_ = framer.header();
return WriteResult(WRITE_STATUS_OK, packet.length());
}
@@ -58,16 +57,22 @@ class TestPacketWriter : public QuicDefaultPacketWriter {
const QuicPacketHeader& header() { return header_; }
private:
+ QuicVersion version_;
QuicPacketHeader header_;
};
-class QuicClientSessionTest : public ::testing::Test {
+class QuicClientSessionTest : public ::testing::TestWithParam<QuicVersion> {
protected:
QuicClientSessionTest()
- : writer_(new TestPacketWriter()),
- connection_(new PacketSavingConnection(false)),
+ : writer_(new TestPacketWriter(GetParam())),
+ connection_(
+ new PacketSavingConnection(false, SupportedVersions(GetParam()))),
session_(connection_, GetSocket().Pass(), writer_.Pass(), NULL, NULL,
- kServerHostname, DefaultQuicConfig(), &crypto_config_,
+ make_scoped_ptr((QuicServerInfo*)NULL),
+ QuicServerId(kServerHostname, kServerPort, false,
+ PRIVACY_MODE_DISABLED),
+ DefaultQuicConfig(), &crypto_config_,
+ base::MessageLoop::current()->message_loop_proxy().get(),
&net_log_) {
session_.config()->SetDefaults();
crypto_config_.SetDefaults();
@@ -105,11 +110,14 @@ class QuicClientSessionTest : public ::testing::Test {
QuicCryptoClientConfig crypto_config_;
};
-TEST_F(QuicClientSessionTest, CryptoConnect) {
+INSTANTIATE_TEST_CASE_P(Tests, QuicClientSessionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicClientSessionTest, CryptoConnect) {
CompleteCryptoHandshake();
}
-TEST_F(QuicClientSessionTest, MaxNumStreams) {
+TEST_P(QuicClientSessionTest, MaxNumStreams) {
CompleteCryptoHandshake();
std::vector<QuicReliableClientStream*> streams;
@@ -125,7 +133,7 @@ TEST_F(QuicClientSessionTest, MaxNumStreams) {
EXPECT_TRUE(session_.CreateOutgoingDataStream());
}
-TEST_F(QuicClientSessionTest, MaxNumStreamsViaRequest) {
+TEST_P(QuicClientSessionTest, MaxNumStreamsViaRequest) {
CompleteCryptoHandshake();
std::vector<QuicReliableClientStream*> streams;
@@ -149,7 +157,7 @@ TEST_F(QuicClientSessionTest, MaxNumStreamsViaRequest) {
EXPECT_TRUE(stream != NULL);
}
-TEST_F(QuicClientSessionTest, GoAwayReceived) {
+TEST_P(QuicClientSessionTest, GoAwayReceived) {
CompleteCryptoHandshake();
// After receiving a GoAway, I should no longer be able to create outgoing
diff --git a/chromium/net/quic/quic_clock.cc b/chromium/net/quic/quic_clock.cc
index 865562369fa..9d5ac6d17ab 100644
--- a/chromium/net/quic/quic_clock.cc
+++ b/chromium/net/quic/quic_clock.cc
@@ -14,7 +14,8 @@ QuicClock::QuicClock() {
QuicClock::~QuicClock() {}
QuicTime QuicClock::ApproximateNow() const {
- // Chrome does not have a distinct notion of ApproximateNow().
+ // At the moment, Chrome does not have a distinct notion of ApproximateNow().
+ // We should consider implementing this using MessageLoop::recent_time_.
return Now();
}
diff --git a/chromium/net/quic/quic_clock.h b/chromium/net/quic/quic_clock.h
index e6135a930d0..4e96d4c781e 100644
--- a/chromium/net/quic/quic_clock.h
+++ b/chromium/net/quic/quic_clock.h
@@ -30,6 +30,9 @@ class NET_EXPORT_PRIVATE QuicClock {
// WallNow returns the current wall-time - a time that is consistent across
// different clocks.
virtual QuicWallTime WallNow() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicClock);
};
} // namespace net
diff --git a/chromium/net/quic/quic_config.cc b/chromium/net/quic/quic_config.cc
index 152da46a80f..f69aa379494 100644
--- a/chromium/net/quic/quic_config.cc
+++ b/chromium/net/quic/quic_config.cc
@@ -7,23 +7,68 @@
#include <algorithm>
#include "base/logging.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_sent_packet_manager.h"
+#include "net/quic/quic_utils.h"
+using std::min;
using std::string;
namespace net {
-QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, Presence presence)
+// Reads the value corresponding to |name_| from |msg| into |out|. If the
+// |name_| is absent in |msg| and |presence| is set to OPTIONAL |out| is set
+// to |default_value|.
+QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg,
+ QuicTag tag,
+ QuicConfigPresence presence,
+ uint32 default_value,
+ uint32* out,
+ string* error_details) {
+ DCHECK(error_details != NULL);
+ QuicErrorCode error = msg.GetUint32(tag, out);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence == PRESENCE_REQUIRED) {
+ *error_details = "Missing " + QuicUtils::TagToString(tag);
+ break;
+ }
+ error = QUIC_NO_ERROR;
+ *out = default_value;
+ break;
+ case QUIC_NO_ERROR:
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag);
+ break;
+ }
+ return error;
+}
+
+
+QuicConfigValue::QuicConfigValue(QuicTag tag,
+ QuicConfigPresence presence)
: tag_(tag),
- presence_(presence),
+ presence_(presence) {
+}
+QuicConfigValue::~QuicConfigValue() {}
+
+QuicNegotiableValue::QuicNegotiableValue(QuicTag tag,
+ QuicConfigPresence presence)
+ : QuicConfigValue(tag, presence),
negotiated_(false) {
}
+QuicNegotiableValue::~QuicNegotiableValue() {}
-QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, Presence presence)
+QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag,
+ QuicConfigPresence presence)
: QuicNegotiableValue(tag, presence),
max_value_(0),
default_value_(0) {
}
+QuicNegotiableUint32::~QuicNegotiableUint32() {}
void QuicNegotiableUint32::set(uint32 max, uint32 default_value) {
DCHECK_LE(default_value, max);
@@ -47,70 +92,34 @@ void QuicNegotiableUint32::ToHandshakeMessage(
}
}
-QuicErrorCode QuicNegotiableUint32::ReadUint32(
- const CryptoHandshakeMessage& msg,
- uint32* out,
- string* error_details) const {
- DCHECK(error_details != NULL);
- QuicErrorCode error = msg.GetUint32(tag_, out);
- switch (error) {
- case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
- if (presence_ == QuicNegotiableValue::PRESENCE_REQUIRED) {
- *error_details = "Missing " + QuicUtils::TagToString(tag_);
- break;
- }
- error = QUIC_NO_ERROR;
- *out = default_value_;
-
- case QUIC_NO_ERROR:
- break;
- default:
- *error_details = "Bad " + QuicUtils::TagToString(tag_);
- break;
- }
- return error;
-}
-
-QuicErrorCode QuicNegotiableUint32::ProcessClientHello(
- const CryptoHandshakeMessage& client_hello,
- string* error_details) {
- DCHECK(!negotiated_);
- DCHECK(error_details != NULL);
- uint32 value;
- QuicErrorCode error = ReadUint32(client_hello, &value, error_details);
- if (error != QUIC_NO_ERROR) {
- return error;
- }
-
- negotiated_ = true;
- negotiated_value_ = std::min(value, max_value_);
-
- return QUIC_NO_ERROR;
-}
-
-QuicErrorCode QuicNegotiableUint32::ProcessServerHello(
- const CryptoHandshakeMessage& server_hello,
+QuicErrorCode QuicNegotiableUint32::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
string* error_details) {
DCHECK(!negotiated_);
DCHECK(error_details != NULL);
uint32 value;
- QuicErrorCode error = ReadUint32(server_hello, &value, error_details);
+ QuicErrorCode error = ReadUint32(peer_hello,
+ tag_,
+ presence_,
+ default_value_,
+ &value,
+ error_details);
if (error != QUIC_NO_ERROR) {
return error;
}
-
- if (value > max_value_) {
- *error_details = "Invalid value received for " +
- QuicUtils::TagToString(tag_);
+ if (hello_type == SERVER && value > max_value_) {
+ *error_details =
+ "Invalid value received for " + QuicUtils::TagToString(tag_);
return QUIC_INVALID_NEGOTIATED_VALUE;
}
negotiated_ = true;
- negotiated_value_ = value;
+ negotiated_value_ = min(value, max_value_);
return QUIC_NO_ERROR;
}
-QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, Presence presence)
+QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, QuicConfigPresence presence)
: QuicNegotiableValue(tag, presence),
negotiated_tag_(0),
default_value_(0) {
@@ -120,8 +129,7 @@ QuicNegotiableTag::~QuicNegotiableTag() {}
void QuicNegotiableTag::set(const QuicTagVector& possible,
QuicTag default_value) {
- DCHECK(std::find(possible.begin(), possible.end(), default_value) !=
- possible.end());
+ DCHECK(ContainsQuicTag(possible, default_value));
possible_values_ = possible;
default_value_ = default_value;
}
@@ -169,85 +177,309 @@ QuicErrorCode QuicNegotiableTag::ReadVector(
return error;
}
-QuicErrorCode QuicNegotiableTag::ProcessClientHello(
- const CryptoHandshakeMessage& client_hello,
+QuicErrorCode QuicNegotiableTag::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
string* error_details) {
DCHECK(!negotiated_);
DCHECK(error_details != NULL);
const QuicTag* received_tags;
size_t received_tags_length;
- QuicErrorCode error = ReadVector(client_hello, &received_tags,
+ QuicErrorCode error = ReadVector(peer_hello, &received_tags,
&received_tags_length, error_details);
if (error != QUIC_NO_ERROR) {
return error;
}
- QuicTag negotiated_tag;
- if (!QuicUtils::FindMutualTag(possible_values_,
- received_tags,
- received_tags_length,
- QuicUtils::LOCAL_PRIORITY,
- &negotiated_tag,
- NULL)) {
- *error_details = "Unsuported " + QuicUtils::TagToString(tag_);
- return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP;
+ if (hello_type == SERVER) {
+ if (received_tags_length != 1 ||
+ !ContainsQuicTag(possible_values_, *received_tags)) {
+ *error_details = "Invalid " + QuicUtils::TagToString(tag_);
+ return QUIC_INVALID_NEGOTIATED_VALUE;
+ }
+ negotiated_tag_ = *received_tags;
+ } else {
+ QuicTag negotiated_tag;
+ if (!QuicUtils::FindMutualTag(possible_values_,
+ received_tags,
+ received_tags_length,
+ QuicUtils::LOCAL_PRIORITY,
+ &negotiated_tag,
+ NULL)) {
+ *error_details = "Unsupported " + QuicUtils::TagToString(tag_);
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP;
+ }
+ negotiated_tag_ = negotiated_tag;
}
negotiated_ = true;
- negotiated_tag_ = negotiated_tag;
return QUIC_NO_ERROR;
}
-QuicErrorCode QuicNegotiableTag::ProcessServerHello(
- const CryptoHandshakeMessage& server_hello,
+QuicFixedUint32::QuicFixedUint32(QuicTag tag, QuicConfigPresence presence)
+ : QuicConfigValue(tag, presence),
+ has_send_value_(false),
+ has_receive_value_(false) {
+}
+QuicFixedUint32::~QuicFixedUint32() {}
+
+bool QuicFixedUint32::HasSendValue() const {
+ return has_send_value_;
+}
+
+uint32 QuicFixedUint32::GetSendValue() const {
+ LOG_IF(DFATAL, !has_send_value_)
+ << "No send value to get for tag:" << QuicUtils::TagToString(tag_);
+ return send_value_;
+}
+
+void QuicFixedUint32::SetSendValue(uint32 value) {
+ has_send_value_ = true;
+ send_value_ = value;
+}
+
+bool QuicFixedUint32::HasReceivedValue() const {
+ return has_receive_value_;
+}
+
+uint32 QuicFixedUint32::GetReceivedValue() const {
+ LOG_IF(DFATAL, !has_receive_value_)
+ << "No receive value to get for tag:" << QuicUtils::TagToString(tag_);
+ return receive_value_;
+}
+
+void QuicFixedUint32::SetReceivedValue(uint32 value) {
+ has_receive_value_ = true;
+ receive_value_ = value;
+}
+
+void QuicFixedUint32::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_value_) {
+ out->SetValue(tag_, send_value_);
+ }
+}
+
+QuicErrorCode QuicFixedUint32::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
string* error_details) {
- DCHECK(!negotiated_);
DCHECK(error_details != NULL);
- const QuicTag* received_tags;
- size_t received_tags_length;
- QuicErrorCode error = ReadVector(server_hello, &received_tags,
- &received_tags_length, error_details);
- if (error != QUIC_NO_ERROR) {
- return error;
+ QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ has_receive_value_ = true;
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
}
+ return error;
+}
- if (received_tags_length != 1 ||
- std::find(possible_values_.begin(), possible_values_.end(),
- *received_tags) == possible_values_.end()) {
- *error_details = "Invalid " + QuicUtils::TagToString(tag_);
- return QUIC_INVALID_NEGOTIATED_VALUE;
+QuicFixedTag::QuicFixedTag(QuicTag name,
+ QuicConfigPresence presence)
+ : QuicConfigValue(name, presence),
+ has_send_value_(false),
+ has_receive_value_(false) {
+}
+
+QuicFixedTag::~QuicFixedTag() {}
+
+bool QuicFixedTag::HasSendValue() const {
+ return has_send_value_;
+}
+
+uint32 QuicFixedTag::GetSendValue() const {
+ LOG_IF(DFATAL, !has_send_value_)
+ << "No send value to get for tag:" << QuicUtils::TagToString(tag_);
+ return send_value_;
+}
+
+void QuicFixedTag::SetSendValue(uint32 value) {
+ has_send_value_ = true;
+ send_value_ = value;
+}
+
+bool QuicFixedTag::HasReceivedValue() const {
+ return has_receive_value_;
+}
+
+uint32 QuicFixedTag::GetReceivedValue() const {
+ LOG_IF(DFATAL, !has_receive_value_)
+ << "No receive value to get for tag:" << QuicUtils::TagToString(tag_);
+ return receive_value_;
+}
+
+void QuicFixedTag::SetReceivedValue(uint32 value) {
+ has_receive_value_ = true;
+ receive_value_ = value;
+}
+
+void QuicFixedTag::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_value_) {
+ out->SetValue(tag_, send_value_);
}
+}
- negotiated_ = true;
- negotiated_tag_ = *received_tags;
- return QUIC_NO_ERROR;
+QuicErrorCode QuicFixedTag::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ string* error_details) {
+ DCHECK(error_details != NULL);
+ QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ has_receive_value_ = true;
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ return error;
}
-QuicConfig::QuicConfig() :
- congestion_control_(kCGST, QuicNegotiableValue::PRESENCE_REQUIRED),
- idle_connection_state_lifetime_seconds_(
- kICSL, QuicNegotiableValue::PRESENCE_REQUIRED),
- keepalive_timeout_seconds_(kKATO, QuicNegotiableValue::PRESENCE_OPTIONAL),
- max_streams_per_connection_(kMSPC, QuicNegotiableValue::PRESENCE_REQUIRED),
- max_time_before_crypto_handshake_(QuicTime::Delta::Zero()),
- server_initial_congestion_window_(
- kSWND, QuicNegotiableValue::PRESENCE_OPTIONAL),
- initial_round_trip_time_us_(kIRTT, QuicNegotiableValue::PRESENCE_OPTIONAL) {
- // All optional non-zero parameters should be initialized here.
- server_initial_congestion_window_.set(kMaxInitialWindow,
- kDefaultInitialWindow);
+QuicFixedTagVector::QuicFixedTagVector(QuicTag name,
+ QuicConfigPresence presence)
+ : QuicConfigValue(name, presence),
+ has_send_values_(false),
+ has_receive_values_(false) {
+}
+
+QuicFixedTagVector::~QuicFixedTagVector() {}
+
+bool QuicFixedTagVector::HasSendValues() const {
+ return has_send_values_;
+}
+
+QuicTagVector QuicFixedTagVector::GetSendValues() const {
+ LOG_IF(DFATAL, !has_send_values_)
+ << "No send values to get for tag:" << QuicUtils::TagToString(tag_);
+ return send_values_;
+}
+
+void QuicFixedTagVector::SetSendValues(const QuicTagVector& values) {
+ has_send_values_ = true;
+ send_values_ = values;
+}
+
+bool QuicFixedTagVector::HasReceivedValues() const {
+ return has_receive_values_;
+}
+
+QuicTagVector QuicFixedTagVector::GetReceivedValues() const {
+ LOG_IF(DFATAL, !has_receive_values_)
+ << "No receive value to get for tag:" << QuicUtils::TagToString(tag_);
+ return receive_values_;
+}
+
+void QuicFixedTagVector::SetReceivedValues(const QuicTagVector& values) {
+ has_receive_values_ = true;
+ receive_values_ = values;
+}
+
+void QuicFixedTagVector::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_values_) {
+ out->SetVector(tag_, send_values_);
+ }
+}
+
+QuicErrorCode QuicFixedTagVector::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ string* error_details) {
+ DCHECK(error_details != NULL);
+ const QuicTag* received_tags;
+ size_t received_tags_length;
+ QuicErrorCode error =
+ peer_hello.GetTaglist(tag_, &received_tags, &received_tags_length);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ has_receive_values_ = true;
+ for (size_t i = 0; i < received_tags_length; ++i) {
+ receive_values_.push_back(received_tags[i]);
+ }
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ return error;
+}
+
+QuicConfig::QuicConfig()
+ : congestion_feedback_(kCGST, PRESENCE_REQUIRED),
+ congestion_options_(kCOPT, PRESENCE_OPTIONAL),
+ loss_detection_(kLOSS, PRESENCE_OPTIONAL),
+ idle_connection_state_lifetime_seconds_(kICSL, PRESENCE_REQUIRED),
+ keepalive_timeout_seconds_(kKATO, PRESENCE_OPTIONAL),
+ max_streams_per_connection_(kMSPC, PRESENCE_REQUIRED),
+ max_time_before_crypto_handshake_(QuicTime::Delta::Zero()),
+ initial_congestion_window_(kSWND, PRESENCE_OPTIONAL),
+ initial_round_trip_time_us_(kIRTT, PRESENCE_OPTIONAL),
+ // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
+ // QUIC_VERSION_17.
+ initial_flow_control_window_bytes_(kIFCW, PRESENCE_OPTIONAL),
+ // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
+ // QUIC_VERSION_19.
+ initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL),
+ // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
+ // QUIC_VERSION_19.
+ initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL) {
}
QuicConfig::~QuicConfig() {}
-void QuicConfig::set_congestion_control(
- const QuicTagVector& congestion_control,
- QuicTag default_congestion_control) {
- congestion_control_.set(congestion_control, default_congestion_control);
+void QuicConfig::set_congestion_feedback(
+ const QuicTagVector& congestion_feedback,
+ QuicTag default_congestion_feedback) {
+ congestion_feedback_.set(congestion_feedback, default_congestion_feedback);
+}
+
+QuicTag QuicConfig::congestion_feedback() const {
+ return congestion_feedback_.GetTag();
+}
+
+void QuicConfig::SetCongestionOptionsToSend(
+ const QuicTagVector& congestion_options) {
+ congestion_options_.SetSendValues(congestion_options);
}
-QuicTag QuicConfig::congestion_control() const {
- return congestion_control_.GetTag();
+bool QuicConfig::HasReceivedCongestionOptions() const {
+ return congestion_options_.HasReceivedValues();
+}
+
+QuicTagVector QuicConfig::ReceivedCongestionOptions() const {
+ return congestion_options_.GetReceivedValues();
+}
+
+void QuicConfig::SetLossDetectionToSend(QuicTag loss_detection) {
+ loss_detection_.SetSendValue(loss_detection);
+}
+
+bool QuicConfig::HasReceivedLossDetection() const {
+ return loss_detection_.HasReceivedValue();
+}
+
+QuicTag QuicConfig::ReceivedLossDetection() const {
+ return loss_detection_.GetReceivedValue();
}
void QuicConfig::set_idle_connection_state_lifetime(
@@ -286,44 +518,113 @@ QuicTime::Delta QuicConfig::max_time_before_crypto_handshake() const {
return max_time_before_crypto_handshake_;
}
-void QuicConfig::set_server_initial_congestion_window(size_t max_initial_window,
- size_t default_initial_window) {
- server_initial_congestion_window_.set(max_initial_window,
- default_initial_window);
+void QuicConfig::SetInitialCongestionWindowToSend(size_t initial_window) {
+ initial_congestion_window_.SetSendValue(initial_window);
+}
+
+bool QuicConfig::HasReceivedInitialCongestionWindow() const {
+ return initial_congestion_window_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialCongestionWindow() const {
+ return initial_congestion_window_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialRoundTripTimeUsToSend(size_t rtt) {
+ initial_round_trip_time_us_.SetSendValue(rtt);
+}
+
+bool QuicConfig::HasReceivedInitialRoundTripTimeUs() const {
+ return initial_round_trip_time_us_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialRoundTripTimeUs() const {
+ return initial_round_trip_time_us_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialFlowControlWindowToSend(uint32 window_bytes) {
+ if (window_bytes < kDefaultFlowControlSendWindow) {
+ LOG(DFATAL) << "Initial flow control receive window (" << window_bytes
+ << ") cannot be set lower than default ("
+ << kDefaultFlowControlSendWindow << ").";
+ window_bytes = kDefaultFlowControlSendWindow;
+ }
+ initial_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32 QuicConfig::GetInitialFlowControlWindowToSend() const {
+ return initial_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialFlowControlWindowBytes() const {
+ return initial_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialFlowControlWindowBytes() const {
+ return initial_flow_control_window_bytes_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialStreamFlowControlWindowToSend(uint32 window_bytes) {
+ if (window_bytes < kDefaultFlowControlSendWindow) {
+ LOG(DFATAL) << "Initial stream flow control receive window ("
+ << window_bytes << ") cannot be set lower than default ("
+ << kDefaultFlowControlSendWindow << ").";
+ window_bytes = kDefaultFlowControlSendWindow;
+ }
+ initial_stream_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32 QuicConfig::GetInitialStreamFlowControlWindowToSend() const {
+ return initial_stream_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialStreamFlowControlWindowBytes() const {
+ return initial_stream_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialStreamFlowControlWindowBytes() const {
+ return initial_stream_flow_control_window_bytes_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialSessionFlowControlWindowToSend(uint32 window_bytes) {
+ if (window_bytes < kDefaultFlowControlSendWindow) {
+ LOG(DFATAL) << "Initial session flow control receive window ("
+ << window_bytes << ") cannot be set lower than default ("
+ << kDefaultFlowControlSendWindow << ").";
+ window_bytes = kDefaultFlowControlSendWindow;
+ }
+ initial_session_flow_control_window_bytes_.SetSendValue(window_bytes);
}
-uint32 QuicConfig::server_initial_congestion_window() const {
- return server_initial_congestion_window_.GetUint32();
+uint32 QuicConfig::GetInitialSessionFlowControlWindowToSend() const {
+ return initial_session_flow_control_window_bytes_.GetSendValue();
}
-void QuicConfig::set_initial_round_trip_time_us(size_t max_rtt,
- size_t default_rtt) {
- initial_round_trip_time_us_.set(max_rtt, default_rtt);
+bool QuicConfig::HasReceivedInitialSessionFlowControlWindowBytes() const {
+ return initial_session_flow_control_window_bytes_.HasReceivedValue();
}
-uint32 QuicConfig::initial_round_trip_time_us() const {
- return initial_round_trip_time_us_.GetUint32();
+uint32 QuicConfig::ReceivedInitialSessionFlowControlWindowBytes() const {
+ return initial_session_flow_control_window_bytes_.GetReceivedValue();
}
bool QuicConfig::negotiated() {
// TODO(ianswett): Add the negotiated parameters once and iterate over all
// of them in negotiated, ToHandshakeMessage, ProcessClientHello, and
// ProcessServerHello.
- return congestion_control_.negotiated() &&
+ return congestion_feedback_.negotiated() &&
idle_connection_state_lifetime_seconds_.negotiated() &&
keepalive_timeout_seconds_.negotiated() &&
- max_streams_per_connection_.negotiated() &&
- server_initial_congestion_window_.negotiated() &&
- initial_round_trip_time_us_.negotiated();
+ max_streams_per_connection_.negotiated();
}
void QuicConfig::SetDefaults() {
- QuicTagVector congestion_control;
+ QuicTagVector congestion_feedback;
if (FLAGS_enable_quic_pacing) {
- congestion_control.push_back(kPACE);
+ congestion_feedback.push_back(kPACE);
}
- congestion_control.push_back(kQBIC);
- congestion_control_.set(congestion_control, kQBIC);
+ congestion_feedback.push_back(kQBIC);
+ congestion_feedback_.set(congestion_feedback, kQBIC);
idle_connection_state_lifetime_seconds_.set(kDefaultTimeoutSecs,
kDefaultInitialTimeoutSecs);
// kKATO is optional. Return 0 if not negotiated.
@@ -332,80 +633,85 @@ void QuicConfig::SetDefaults() {
kDefaultMaxStreamsPerConnection);
max_time_before_crypto_handshake_ = QuicTime::Delta::FromSeconds(
kDefaultMaxTimeForCryptoHandshakeSecs);
- server_initial_congestion_window_.set(kDefaultInitialWindow,
- kDefaultInitialWindow);
+
+ SetInitialFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+ SetInitialStreamFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+ SetInitialSessionFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+}
+
+void QuicConfig::EnablePacing(bool enable_pacing) {
+ QuicTagVector congestion_feedback;
+ if (enable_pacing) {
+ congestion_feedback.push_back(kPACE);
+ }
+ congestion_feedback.push_back(kQBIC);
+ congestion_feedback_.set(congestion_feedback, kQBIC);
}
void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
- congestion_control_.ToHandshakeMessage(out);
+ congestion_feedback_.ToHandshakeMessage(out);
idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out);
keepalive_timeout_seconds_.ToHandshakeMessage(out);
max_streams_per_connection_.ToHandshakeMessage(out);
- server_initial_congestion_window_.ToHandshakeMessage(out);
- // TODO(ianswett): Don't transmit parameters which are optional and not set.
+ initial_congestion_window_.ToHandshakeMessage(out);
initial_round_trip_time_us_.ToHandshakeMessage(out);
+ loss_detection_.ToHandshakeMessage(out);
+ initial_flow_control_window_bytes_.ToHandshakeMessage(out);
+ initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out);
+ initial_session_flow_control_window_bytes_.ToHandshakeMessage(out);
+ congestion_options_.ToHandshakeMessage(out);
}
-QuicErrorCode QuicConfig::ProcessClientHello(
- const CryptoHandshakeMessage& client_hello,
+QuicErrorCode QuicConfig::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
string* error_details) {
DCHECK(error_details != NULL);
QuicErrorCode error = QUIC_NO_ERROR;
if (error == QUIC_NO_ERROR) {
- error = congestion_control_.ProcessClientHello(client_hello, error_details);
+ error = congestion_feedback_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = idle_connection_state_lifetime_seconds_.ProcessClientHello(
- client_hello, error_details);
+ error = idle_connection_state_lifetime_seconds_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = keepalive_timeout_seconds_.ProcessClientHello(
- client_hello, error_details);
+ error = keepalive_timeout_seconds_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = max_streams_per_connection_.ProcessClientHello(
- client_hello, error_details);
+ error = max_streams_per_connection_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = server_initial_congestion_window_.ProcessClientHello(
- client_hello, error_details);
+ error = initial_congestion_window_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = initial_round_trip_time_us_.ProcessClientHello(
- client_hello, error_details);
- }
- return error;
-}
-
-QuicErrorCode QuicConfig::ProcessServerHello(
- const CryptoHandshakeMessage& server_hello,
- string* error_details) {
- DCHECK(error_details != NULL);
-
- QuicErrorCode error = QUIC_NO_ERROR;
- if (error == QUIC_NO_ERROR) {
- error = congestion_control_.ProcessServerHello(server_hello, error_details);
+ error = initial_round_trip_time_us_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = idle_connection_state_lifetime_seconds_.ProcessServerHello(
- server_hello, error_details);
+ error = initial_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = keepalive_timeout_seconds_.ProcessServerHello(
- server_hello, error_details);
+ error = initial_stream_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = max_streams_per_connection_.ProcessServerHello(
- server_hello, error_details);
+ error = initial_session_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = server_initial_congestion_window_.ProcessServerHello(
- server_hello, error_details);
+ error = loss_detection_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
if (error == QUIC_NO_ERROR) {
- error = initial_round_trip_time_us_.ProcessServerHello(
- server_hello, error_details);
+ error = congestion_options_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
}
return error;
}
diff --git a/chromium/net/quic/quic_config.h b/chromium/net/quic/quic_config.h
index 6c0561b1ca4..8f46917450e 100644
--- a/chromium/net/quic/quic_config.h
+++ b/chromium/net/quic/quic_config.h
@@ -8,40 +8,74 @@
#include <string>
#include "base/basictypes.h"
-#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
-#include "net/quic/quic_utils.h"
namespace net {
-class NET_EXPORT_PRIVATE QuicNegotiableValue {
+namespace test {
+class QuicConfigPeer;
+} // namespace test
+
+class CryptoHandshakeMessage;
+
+// Describes whether or not a given QuicTag is required or optional in the
+// handshake message.
+enum QuicConfigPresence {
+ // This negotiable value can be absent from the handshake message. Default
+ // value is selected as the negotiated value in such a case.
+ PRESENCE_OPTIONAL,
+ // This negotiable value is required in the handshake message otherwise the
+ // Process*Hello function returns an error.
+ PRESENCE_REQUIRED,
+};
+
+// Whether the CryptoHandshakeMessage is from the client or server.
+enum HelloType {
+ CLIENT,
+ SERVER,
+};
+
+// An abstract base class that stores a value that can be sent in CHLO/SHLO
+// message. These values can be OPTIONAL or REQUIRED, depending on |presence_|.
+class NET_EXPORT_PRIVATE QuicConfigValue {
public:
- enum Presence {
- // This negotiable value can be absent from the handshake message. Default
- // value is selected as the negotiated value in such a case.
- PRESENCE_OPTIONAL,
- // This negotiable value is required in the handshake message otherwise the
- // Process*Hello function returns an error.
- PRESENCE_REQUIRED,
- };
+ QuicConfigValue(QuicTag tag, QuicConfigPresence presence);
+ virtual ~QuicConfigValue();
+
+ // Serialises tag name and value(s) to |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const = 0;
+
+ // Selects a mutually acceptable value from those offered in |peer_hello|
+ // and those defined in the subclass.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) = 0;
- QuicNegotiableValue(QuicTag tag, Presence presence);
+ protected:
+ const QuicTag tag_;
+ const QuicConfigPresence presence_;
+};
+
+class NET_EXPORT_PRIVATE QuicNegotiableValue : public QuicConfigValue {
+ public:
+ QuicNegotiableValue(QuicTag tag, QuicConfigPresence presence);
+ virtual ~QuicNegotiableValue();
bool negotiated() const {
return negotiated_;
}
protected:
- const QuicTag tag_;
- const Presence presence_;
bool negotiated_;
};
class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue {
public:
// Default and max values default to 0.
- QuicNegotiableUint32(QuicTag name, Presence presence);
+ QuicNegotiableUint32(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicNegotiableUint32();
// Sets the maximum possible value that can be achieved after negotiation and
// also the default values to be assumed if PRESENCE_OPTIONAL and the *HLO msg
@@ -55,30 +89,18 @@ class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue {
// Serialises |name_| and value to |out|. If |negotiated_| is true then
// |negotiated_value_| is serialised, otherwise |max_value_| is serialised.
- void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
// Sets |negotiated_value_| to the minimum of |max_value_| and the
- // corresponding value from |client_hello|. If the corresponding value is
+ // corresponding value from |peer_hello|. If the corresponding value is
// missing and PRESENCE_OPTIONAL then |negotiated_value_| is set to
// |default_value_|.
- QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
- std::string* error_details);
-
- // Sets the |negotiated_value_| to the corresponding value from
- // |server_hello|. Returns error if the value received in |server_hello| is
- // greater than |max_value_|. If the corresponding value is missing and
- // PRESENCE_OPTIONAL then |negotiated_value_| is set to |0|,
- QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
- std::string* error_details);
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
private:
- // Reads the value corresponding to |name_| from |msg| into |out|. If the
- // |name_| is absent in |msg| and |presence_| is set to OPTIONAL |out| is set
- // to |max_value_|.
- QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg,
- uint32* out,
- std::string* error_details) const;
-
uint32 max_value_;
uint32 default_value_;
uint32 negotiated_value_;
@@ -86,8 +108,8 @@ class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue {
class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue {
public:
- QuicNegotiableTag(QuicTag name, Presence presence);
- ~QuicNegotiableTag();
+ QuicNegotiableTag(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicNegotiableTag();
// Sets the possible values that |negotiated_tag_| can take after negotiation
// and the default value that |negotiated_tag_| takes if OPTIONAL and *HLO
@@ -101,19 +123,15 @@ class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue {
// Serialises |name_| and vector (either possible or negotiated) to |out|. If
// |negotiated_| is true then |negotiated_tag_| is serialised, otherwise
// |possible_values_| is serialised.
- void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
// Selects the tag common to both tags in |client_hello| for |name_| and
// |possible_values_| with preference to tag in |possible_values_|. The
// selected tag is set as |negotiated_tag_|.
- QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
- std::string* error_details);
-
- // Sets the value for |name_| tag in |server_hello| as |negotiated_value_|.
- // Returns error if the value received in |server_hello| isn't present in
- // |possible_values_|.
- QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
- std::string* error_details);
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
private:
// Reads the vector corresponding to |name_| from |msg| into |out|. If the
@@ -129,6 +147,110 @@ class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue {
QuicTag default_value_;
};
+// Stores uint32 from CHLO or SHLO messages that are not negotiated.
+class NET_EXPORT_PRIVATE QuicFixedUint32 : public QuicConfigValue {
+ public:
+ QuicFixedUint32(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicFixedUint32();
+
+ bool HasSendValue() const;
+
+ uint32 GetSendValue() const;
+
+ void SetSendValue(uint32 value);
+
+ bool HasReceivedValue() const;
+
+ uint32 GetReceivedValue() const;
+
+ void SetReceivedValue(uint32 value);
+
+ // If has_send_value is true, serialises |tag_| and |send_value_| to |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Sets |value_| to the corresponding value from |peer_hello_| if it exists.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ uint32 send_value_;
+ bool has_send_value_;
+ uint32 receive_value_;
+ bool has_receive_value_;
+};
+
+// Stores tag from CHLO or SHLO messages that are not negotiated.
+class NET_EXPORT_PRIVATE QuicFixedTag : public QuicConfigValue {
+ public:
+ QuicFixedTag(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicFixedTag();
+
+ bool HasSendValue() const;
+
+ QuicTag GetSendValue() const;
+
+ void SetSendValue(QuicTag value);
+
+ bool HasReceivedValue() const;
+
+ QuicTag GetReceivedValue() const;
+
+ void SetReceivedValue(QuicTag value);
+
+ // If has_send_value is true, serialises |tag_| and |send_value_| to |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Sets |value_| to the corresponding value from |client_hello_| if it exists.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ QuicTag send_value_;
+ bool has_send_value_;
+ QuicTag receive_value_;
+ bool has_receive_value_;
+};
+
+// Stores tag from CHLO or SHLO messages that are not negotiated.
+class NET_EXPORT_PRIVATE QuicFixedTagVector : public QuicConfigValue {
+ public:
+ QuicFixedTagVector(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicFixedTagVector();
+
+ bool HasSendValues() const;
+
+ QuicTagVector GetSendValues() const;
+
+ void SetSendValues(const QuicTagVector& values);
+
+ bool HasReceivedValues() const;
+
+ QuicTagVector GetReceivedValues() const;
+
+ void SetReceivedValues(const QuicTagVector& values);
+
+ // If has_send_value is true, serialises |tag_vector_| and |send_value_| to
+ // |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Sets |receive_values_| to the corresponding value from |client_hello_| if
+ // it exists.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ QuicTagVector send_values_;
+ bool has_send_values_;
+ QuicTagVector receive_values_;
+ bool has_receive_values_;
+};
+
// QuicConfig contains non-crypto configuration options that are negotiated in
// the crypto handshake.
class NET_EXPORT_PRIVATE QuicConfig {
@@ -136,10 +258,22 @@ class NET_EXPORT_PRIVATE QuicConfig {
QuicConfig();
~QuicConfig();
- void set_congestion_control(const QuicTagVector& congestion_control,
- QuicTag default_congestion_control);
+ void set_congestion_feedback(const QuicTagVector& congestion_feedback,
+ QuicTag default_congestion_feedback);
+
+ QuicTag congestion_feedback() const;
+
+ void SetCongestionOptionsToSend(const QuicTagVector& congestion_options);
+
+ bool HasReceivedCongestionOptions() const;
+
+ QuicTagVector ReceivedCongestionOptions() const;
+
+ void SetLossDetectionToSend(QuicTag loss_detection);
- QuicTag congestion_control() const;
+ bool HasReceivedLossDetection() const;
+
+ QuicTag ReceivedLossDetection() const;
void set_idle_connection_state_lifetime(
QuicTime::Delta max_idle_connection_state_lifetime,
@@ -159,40 +293,76 @@ class NET_EXPORT_PRIVATE QuicConfig {
QuicTime::Delta max_time_before_crypto_handshake() const;
- // Sets the server's TCP sender's max and default initial congestion window
- // in packets.
- void set_server_initial_congestion_window(size_t max_initial_window,
- size_t default_initial_window);
+ // Sets the peer's default initial congestion window in packets.
+ void SetInitialCongestionWindowToSend(size_t initial_window);
+
+ bool HasReceivedInitialCongestionWindow() const;
- uint32 server_initial_congestion_window() const;
+ uint32 ReceivedInitialCongestionWindow() const;
// Sets an estimated initial round trip time in us.
- void set_initial_round_trip_time_us(size_t max_rtt, size_t default_rtt);
+ void SetInitialRoundTripTimeUsToSend(size_t rtt_us);
+
+ bool HasReceivedInitialRoundTripTimeUs() const;
+
+ uint32 ReceivedInitialRoundTripTimeUs() const;
+
+ // TODO(rjshade): Remove all InitialFlowControlWindow methods when removing
+ // QUIC_VERSION_19.
+ // Sets an initial stream flow control window size to transmit to the peer.
+ void SetInitialFlowControlWindowToSend(uint32 window_bytes);
+
+ uint32 GetInitialFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialFlowControlWindowBytes() const;
+
+ uint32 ReceivedInitialFlowControlWindowBytes() const;
+
+ // Sets an initial stream flow control window size to transmit to the peer.
+ void SetInitialStreamFlowControlWindowToSend(uint32 window_bytes);
+
+ uint32 GetInitialStreamFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialStreamFlowControlWindowBytes() const;
- uint32 initial_round_trip_time_us() const;
+ uint32 ReceivedInitialStreamFlowControlWindowBytes() const;
+
+ // Sets an initial session flow control window size to transmit to the peer.
+ void SetInitialSessionFlowControlWindowToSend(uint32 window_bytes);
+
+ uint32 GetInitialSessionFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialSessionFlowControlWindowBytes() const;
+
+ uint32 ReceivedInitialSessionFlowControlWindowBytes() const;
bool negotiated();
// SetDefaults sets the members to sensible, default values.
void SetDefaults();
- // ToHandshakeMessage serializes the settings in this object as a series of
+ // Enabled pacing.
+ void EnablePacing(bool enable_pacing);
+
+ // ToHandshakeMessage serialises the settings in this object as a series of
// tags /value pairs and adds them to |out|.
void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
- // Calls ProcessClientHello on each negotiable parameter. On failure returns
+ // Calls ProcessPeerHello on each negotiable parameter. On failure returns
// the corresponding QuicErrorCode and sets detailed error in |error_details|.
- QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
- std::string* error_details);
-
- // Calls ProcessServerHello on each negotiable parameter. On failure returns
- // the corresponding QuicErrorCode and sets detailed error in |error_details|.
- QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
- std::string* error_details);
+ QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details);
private:
+ friend class test::QuicConfigPeer;
+
// Congestion control feedback type.
- QuicNegotiableTag congestion_control_;
+ QuicNegotiableTag congestion_feedback_;
+ // Congestion control option.
+ QuicFixedTagVector congestion_options_;
+ // Loss detection feedback type.
+ QuicFixedTag loss_detection_;
// Idle connection state lifetime
QuicNegotiableUint32 idle_connection_state_lifetime_seconds_;
// Keepalive timeout, or 0 to turn off keepalive probes
@@ -203,9 +373,18 @@ class NET_EXPORT_PRIVATE QuicConfig {
// finished. (Not negotiated).
QuicTime::Delta max_time_before_crypto_handshake_;
// Initial congestion window in packets.
- QuicNegotiableUint32 server_initial_congestion_window_;
+ QuicFixedUint32 initial_congestion_window_;
// Initial round trip time estimate in microseconds.
- QuicNegotiableUint32 initial_round_trip_time_us_;
+ QuicFixedUint32 initial_round_trip_time_us_;
+
+ // TODO(rjshade): Remove when removing QUIC_VERSION_19.
+ // Initial flow control receive window in bytes.
+ QuicFixedUint32 initial_flow_control_window_bytes_;
+
+ // Initial stream flow control receive window in bytes.
+ QuicFixedUint32 initial_stream_flow_control_window_bytes_;
+ // Initial session flow control receive window in bytes.
+ QuicFixedUint32 initial_session_flow_control_window_bytes_;
};
} // namespace net
diff --git a/chromium/net/quic/quic_config_test.cc b/chromium/net/quic/quic_config_test.cc
index 2434cd772fd..3cb2dd26d3a 100644
--- a/chromium/net/quic/quic_config_test.cc
+++ b/chromium/net/quic/quic_config_test.cc
@@ -4,12 +4,15 @@
#include "net/quic/quic_config.h"
-#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_sent_packet_manager.h"
#include "net/quic/quic_time.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
using std::string;
@@ -22,15 +25,20 @@ class QuicConfigTest : public ::testing::Test {
protected:
QuicConfigTest() {
config_.SetDefaults();
- config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, 0);
}
QuicConfig config_;
};
TEST_F(QuicConfigTest, ToHandshakeMessage) {
- FLAGS_enable_quic_pacing = false;
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_pacing, false);
config_.SetDefaults();
+ config_.SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
config_.set_idle_connection_state_lifetime(QuicTime::Delta::FromSeconds(5),
QuicTime::Delta::FromSeconds(2));
config_.set_max_streams_per_connection(4, 2);
@@ -46,6 +54,18 @@ TEST_F(QuicConfigTest, ToHandshakeMessage) {
EXPECT_EQ(QUIC_NO_ERROR, error);
EXPECT_EQ(4u, value);
+ error = msg.GetUint32(kIFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value);
+
+ error = msg.GetUint32(kSFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialStreamFlowControlWindowForTest, value);
+
+ error = msg.GetUint32(kCFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value);
+
const QuicTag* out;
size_t out_len;
error = msg.GetTaglist(kCGST, &out, &out_len);
@@ -73,65 +93,130 @@ TEST_F(QuicConfigTest, ProcessClientHello) {
QuicTagVector cgst;
cgst.push_back(kINAR);
cgst.push_back(kQBIC);
- client_config.set_congestion_control(cgst, kQBIC);
+ client_config.set_congestion_feedback(cgst, kQBIC);
client_config.set_idle_connection_state_lifetime(
QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs),
QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs));
client_config.set_max_streams_per_connection(
2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection);
- client_config.set_initial_round_trip_time_us(
- 10 * base::Time::kMicrosecondsPerMillisecond,
+ client_config.SetInitialRoundTripTimeUsToSend(
10 * base::Time::kMicrosecondsPerMillisecond);
-
+ client_config.SetInitialFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ client_config.SetInitialStreamFlowControlWindowToSend(
+ 2 * kInitialStreamFlowControlWindowForTest);
+ client_config.SetInitialSessionFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ QuicTagVector copt;
+ copt.push_back(kTBBR);
+ client_config.SetCongestionOptionsToSend(copt);
CryptoHandshakeMessage msg;
client_config.ToHandshakeMessage(&msg);
string error_details;
- const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
EXPECT_EQ(QUIC_NO_ERROR, error);
EXPECT_TRUE(config_.negotiated());
- EXPECT_EQ(kQBIC, config_.congestion_control());
+ EXPECT_EQ(kQBIC, config_.congestion_feedback());
EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs),
config_.idle_connection_state_lifetime());
EXPECT_EQ(kDefaultMaxStreamsPerConnection,
config_.max_streams_per_connection());
EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout());
EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond,
- config_.initial_round_trip_time_us());
+ config_.ReceivedInitialRoundTripTimeUs());
+ EXPECT_FALSE(config_.HasReceivedLossDetection());
+ EXPECT_TRUE(config_.HasReceivedCongestionOptions());
+ EXPECT_EQ(1u, config_.ReceivedCongestionOptions().size());
+ EXPECT_EQ(config_.ReceivedCongestionOptions()[0], kTBBR);
+ EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(),
+ 2 * kInitialStreamFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
}
TEST_F(QuicConfigTest, ProcessServerHello) {
QuicConfig server_config;
QuicTagVector cgst;
cgst.push_back(kQBIC);
- server_config.set_congestion_control(cgst, kQBIC);
+ server_config.set_congestion_feedback(cgst, kQBIC);
server_config.set_idle_connection_state_lifetime(
QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2),
QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2));
server_config.set_max_streams_per_connection(
kDefaultMaxStreamsPerConnection / 2,
kDefaultMaxStreamsPerConnection / 2);
- server_config.set_server_initial_congestion_window(kDefaultInitialWindow / 2,
- kDefaultInitialWindow / 2);
- server_config.set_initial_round_trip_time_us(
- 10 * base::Time::kMicrosecondsPerMillisecond,
+ server_config.SetInitialCongestionWindowToSend(kDefaultInitialWindow / 2);
+ server_config.SetInitialRoundTripTimeUsToSend(
10 * base::Time::kMicrosecondsPerMillisecond);
-
+ server_config.SetInitialFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ server_config.SetInitialStreamFlowControlWindowToSend(
+ 2 * kInitialStreamFlowControlWindowForTest);
+ server_config.SetInitialSessionFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
CryptoHandshakeMessage msg;
server_config.ToHandshakeMessage(&msg);
string error_details;
- const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
EXPECT_EQ(QUIC_NO_ERROR, error);
EXPECT_TRUE(config_.negotiated());
- EXPECT_EQ(kQBIC, config_.congestion_control());
+ EXPECT_EQ(kQBIC, config_.congestion_feedback());
EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2),
config_.idle_connection_state_lifetime());
EXPECT_EQ(kDefaultMaxStreamsPerConnection / 2,
config_.max_streams_per_connection());
EXPECT_EQ(kDefaultInitialWindow / 2,
- config_.server_initial_congestion_window());
+ config_.ReceivedInitialCongestionWindow());
EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout());
EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond,
- config_.initial_round_trip_time_us());
+ config_.ReceivedInitialRoundTripTimeUs());
+ EXPECT_FALSE(config_.HasReceivedLossDetection());
+ EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(),
+ 2 * kInitialStreamFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+}
+
+TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) {
+ CryptoHandshakeMessage msg;
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+
+ // Set all REQUIRED tags.
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+ msg.SetValue(kMSPC, 1);
+
+ // No error, as rest are optional.
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+
+ EXPECT_FALSE(config_.HasReceivedInitialFlowControlWindowBytes());
+}
+
+TEST_F(QuicConfigTest, MissingOptionalValuesInSHLO) {
+ CryptoHandshakeMessage msg;
+
+ // Set all REQUIRED tags.
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+ msg.SetValue(kMSPC, 1);
+
+ // No error, as rest are optional.
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+
+ EXPECT_FALSE(config_.HasReceivedInitialFlowControlWindowBytes());
}
TEST_F(QuicConfigTest, MissingValueInCHLO) {
@@ -140,7 +225,8 @@ TEST_F(QuicConfigTest, MissingValueInCHLO) {
msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
// Missing kMSPC. KATO is optional.
string error_details;
- const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
}
@@ -150,7 +236,8 @@ TEST_F(QuicConfigTest, MissingValueInSHLO) {
msg.SetValue(kMSPC, 3);
// Missing CGST. KATO is optional.
string error_details;
- const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
}
@@ -163,7 +250,8 @@ TEST_F(QuicConfigTest, OutOfBoundSHLO) {
CryptoHandshakeMessage msg;
server_config.ToHandshakeMessage(&msg);
string error_details;
- const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error);
}
@@ -172,12 +260,13 @@ TEST_F(QuicConfigTest, MultipleNegotiatedValuesInVectorTag) {
QuicTagVector cgst;
cgst.push_back(kQBIC);
cgst.push_back(kINAR);
- server_config.set_congestion_control(cgst, kQBIC);
+ server_config.set_congestion_feedback(cgst, kQBIC);
CryptoHandshakeMessage msg;
server_config.ToHandshakeMessage(&msg);
string error_details;
- const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error);
}
@@ -186,16 +275,29 @@ TEST_F(QuicConfigTest, NoOverLapInCGST) {
server_config.SetDefaults();
QuicTagVector cgst;
cgst.push_back(kINAR);
- server_config.set_congestion_control(cgst, kINAR);
+ server_config.set_congestion_feedback(cgst, kINAR);
CryptoHandshakeMessage msg;
string error_details;
server_config.ToHandshakeMessage(&msg);
- const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details);
- LOG(INFO) << QuicUtils::ErrorToString(error);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ DVLOG(1) << QuicUtils::ErrorToString(error);
EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP, error);
}
+TEST_F(QuicConfigTest, InvalidFlowControlWindow) {
+ // QuicConfig should not accept an invalid flow control window to send to the
+ // peer: the receive window must be at least the default of 16 Kb.
+ QuicConfig config;
+ const uint64 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ EXPECT_DFATAL(config.SetInitialFlowControlWindowToSend(kInvalidWindow),
+ "Initial flow control receive window");
+
+ EXPECT_EQ(kDefaultFlowControlSendWindow,
+ config.GetInitialFlowControlWindowToSend());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_connection.cc b/chromium/net/quic/quic_connection.cc
index 51a83cc41a0..a977b3fdcdb 100644
--- a/chromium/net/quic/quic_connection.cc
+++ b/chromium/net/quic/quic_connection.cc
@@ -13,6 +13,7 @@
#include <set>
#include <utility>
+#include "base/debug/stack_trace.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "net/base/net_errors.h"
@@ -21,26 +22,20 @@
#include "net/quic/iovector.h"
#include "net/quic/quic_bandwidth.h"
#include "net/quic/quic_config.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
+using base::StringPiece;
using base::hash_map;
using base::hash_set;
-using base::StringPiece;
using std::list;
using std::make_pair;
-using std::min;
using std::max;
+using std::min;
using std::numeric_limits;
-using std::vector;
using std::set;
using std::string;
-
-int FLAGS_fake_packet_loss_percentage = 0;
-
-// If true, then QUIC connections will bundle acks with any outgoing packet when
-// an ack is being delayed. This is an optimization to reduce ack latency and
-// packet count of pure ack packets.
-bool FLAGS_bundle_ack_with_outgoing_packet = false;
+using std::vector;
namespace net {
@@ -66,7 +61,6 @@ bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) {
return delta <= kMaxPacketGap;
}
-
// An alarm that is scheduled to send an ack if a timeout occurs.
class AckAlarm : public QuicAlarm::Delegate {
public:
@@ -81,6 +75,8 @@ class AckAlarm : public QuicAlarm::Delegate {
private:
QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(AckAlarm);
};
// This alarm will be scheduled any time a data-bearing packet is sent out.
@@ -99,6 +95,8 @@ class RetransmissionAlarm : public QuicAlarm::Delegate {
private:
QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(RetransmissionAlarm);
};
// An alarm that is scheduled when the sent scheduler requires a
@@ -111,12 +109,14 @@ class SendAlarm : public QuicAlarm::Delegate {
virtual QuicTime OnAlarm() OVERRIDE {
connection_->WriteIfNotBlocked();
- // Never reschedule the alarm, since OnCanWrite does that.
+ // Never reschedule the alarm, since CanWrite does that.
return QuicTime::Zero();
}
private:
QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(SendAlarm);
};
class TimeoutAlarm : public QuicAlarm::Delegate {
@@ -133,90 +133,119 @@ class TimeoutAlarm : public QuicAlarm::Delegate {
private:
QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeoutAlarm);
};
-// Indicates if any of the frames are intended to be sent with FORCE.
-// Returns FORCE when one of the frames is a CONNECTION_CLOSE_FRAME.
-net::QuicConnection::Force HasForcedFrames(
- const RetransmittableFrames* retransmittable_frames) {
- if (!retransmittable_frames) {
- return net::QuicConnection::NO_FORCE;
+class PingAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit PingAlarm(QuicConnection* connection)
+ : connection_(connection) {
}
- for (size_t i = 0; i < retransmittable_frames->frames().size(); ++i) {
- if (retransmittable_frames->frames()[i].type == CONNECTION_CLOSE_FRAME) {
- return net::QuicConnection::FORCE;
- }
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ connection_->SendPing();
+ return QuicTime::Zero();
}
- return net::QuicConnection::NO_FORCE;
-}
-net::IsHandshake HasCryptoHandshake(
+ private:
+ QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(PingAlarm);
+};
+
+QuicConnection::PacketType GetPacketType(
const RetransmittableFrames* retransmittable_frames) {
if (!retransmittable_frames) {
- return net::NOT_HANDSHAKE;
+ return QuicConnection::NORMAL;
}
for (size_t i = 0; i < retransmittable_frames->frames().size(); ++i) {
- if (retransmittable_frames->frames()[i].type == STREAM_FRAME &&
- retransmittable_frames->frames()[i].stream_frame->stream_id ==
- kCryptoStreamId) {
- return net::IS_HANDSHAKE;
+ if (retransmittable_frames->frames()[i].type == CONNECTION_CLOSE_FRAME) {
+ return QuicConnection::CONNECTION_CLOSE;
}
}
- return net::NOT_HANDSHAKE;
+ return QuicConnection::NORMAL;
}
} // namespace
+QuicConnection::QueuedPacket::QueuedPacket(SerializedPacket packet,
+ EncryptionLevel level,
+ TransmissionType transmission_type)
+ : sequence_number(packet.sequence_number),
+ packet(packet.packet),
+ encryption_level(level),
+ transmission_type(transmission_type),
+ retransmittable((transmission_type != NOT_RETRANSMISSION ||
+ packet.retransmittable_frames != NULL) ?
+ HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA),
+ handshake(packet.retransmittable_frames == NULL ?
+ NOT_HANDSHAKE : packet.retransmittable_frames->HasCryptoHandshake()),
+ type(GetPacketType(packet.retransmittable_frames)),
+ length(packet.packet->length()) {
+}
+
#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
-QuicConnection::QuicConnection(QuicGuid guid,
+QuicConnection::QuicConnection(QuicConnectionId connection_id,
IPEndPoint address,
QuicConnectionHelperInterface* helper,
QuicPacketWriter* writer,
bool is_server,
const QuicVersionVector& supported_versions)
- : framer_(supported_versions,
- helper->GetClock()->ApproximateNow(),
+ : framer_(supported_versions, helper->GetClock()->ApproximateNow(),
is_server),
helper_(helper),
writer_(writer),
encryption_level_(ENCRYPTION_NONE),
clock_(helper->GetClock()),
random_generator_(helper->GetRandomGenerator()),
- guid_(guid),
+ connection_id_(connection_id),
peer_address_(address),
+ migrating_peer_port_(0),
+ last_packet_revived_(false),
+ last_size_(0),
+ last_decrypted_packet_level_(ENCRYPTION_NONE),
largest_seen_packet_with_ack_(0),
+ largest_seen_packet_with_stop_waiting_(0),
pending_version_negotiation_packet_(false),
- write_blocked_(false),
- received_packet_manager_(kTCP),
+ received_packet_manager_(kTCP, &stats_),
+ ack_queued_(false),
+ stop_waiting_count_(0),
ack_alarm_(helper->CreateAlarm(new AckAlarm(this))),
retransmission_alarm_(helper->CreateAlarm(new RetransmissionAlarm(this))),
send_alarm_(helper->CreateAlarm(new SendAlarm(this))),
resume_writes_alarm_(helper->CreateAlarm(new SendAlarm(this))),
timeout_alarm_(helper->CreateAlarm(new TimeoutAlarm(this))),
+ ping_alarm_(helper->CreateAlarm(new PingAlarm(this))),
debug_visitor_(NULL),
- packet_creator_(guid_, &framer_, random_generator_, is_server),
- packet_generator_(this, NULL, &packet_creator_),
+ packet_generator_(connection_id_, &framer_, random_generator_, this),
idle_network_timeout_(
QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)),
overall_connection_timeout_(QuicTime::Delta::Infinite()),
- creation_time_(clock_->ApproximateNow()),
time_of_last_received_packet_(clock_->ApproximateNow()),
- time_of_last_sent_packet_(clock_->ApproximateNow()),
- sequence_number_of_last_inorder_packet_(0),
- sent_packet_manager_(is_server, this, clock_, kTCP),
+ time_of_last_sent_new_packet_(clock_->ApproximateNow()),
+ sequence_number_of_last_sent_packet_(0),
+ sent_packet_manager_(
+ is_server, clock_, &stats_, kTCP,
+ FLAGS_quic_use_time_loss_detection ? kTime : kNack),
version_negotiation_state_(START_NEGOTIATION),
is_server_(is_server),
connected_(true),
- address_migrating_(false) {
+ peer_ip_changed_(false),
+ peer_port_changed_(false),
+ self_ip_changed_(false),
+ self_port_changed_(false) {
if (!is_server_) {
// Pacing will be enabled if the client negotiates it.
sent_packet_manager_.MaybeEnablePacing();
}
- DVLOG(1) << ENDPOINT << "Created connection with guid: " << guid;
+ DVLOG(1) << ENDPOINT << "Created connection with connection_id: "
+ << connection_id;
timeout_alarm_->Set(clock_->ApproximateNow().Add(idle_network_timeout_));
framer_.set_visitor(this);
framer_.set_received_entropy_calculator(&received_packet_manager_);
+ stats_.connection_creation_time = clock_->ApproximateNow();
}
QuicConnection::~QuicConnection() {
@@ -229,7 +258,6 @@ QuicConnection::~QuicConnection() {
}
void QuicConnection::SetFromConfig(const QuicConfig& config) {
- DCHECK_LT(0u, config.server_initial_congestion_window());
SetIdleNetworkTimeout(config.idle_connection_state_lifetime());
sent_packet_manager_.SetFromConfig(config);
// TODO(satyamshekhar): Set congestion control and ICSL also.
@@ -265,9 +293,12 @@ void QuicConnection::OnError(QuicFramer* framer) {
void QuicConnection::OnPacket() {
DCHECK(last_stream_frames_.empty() &&
last_goaway_frames_.empty() &&
+ last_window_update_frames_.empty() &&
+ last_blocked_frames_.empty() &&
last_rst_frames_.empty() &&
last_ack_frames_.empty() &&
- last_congestion_frames_.empty());
+ last_congestion_frames_.empty() &&
+ last_stop_waiting_frames_.empty());
}
void QuicConnection::OnPublicResetPacket(
@@ -280,7 +311,7 @@ void QuicConnection::OnPublicResetPacket(
bool QuicConnection::OnProtocolVersionMismatch(QuicVersion received_version) {
DVLOG(1) << ENDPOINT << "Received packet with mismatched version "
- << received_version;
+ << received_version;
// TODO(satyamshekhar): Implement no server state in this mode.
if (!is_server_) {
LOG(DFATAL) << ENDPOINT << "Framer called OnProtocolVersionMismatch. "
@@ -375,10 +406,19 @@ void QuicConnection::OnVersionNegotiationPacket(
void QuicConnection::OnRevivedPacket() {
}
+bool QuicConnection::OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) {
+ return true;
+}
+
bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) {
return true;
}
+void QuicConnection::OnDecryptedPacket(EncryptionLevel level) {
+ last_decrypted_packet_level_ = level;
+}
+
bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
if (debug_visitor_) {
debug_visitor_->OnPacketHeader(header);
@@ -391,16 +431,17 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
// Will be decrement below if we fall through to return true;
++stats_.packets_dropped;
- if (header.public_header.guid != guid_) {
- DVLOG(1) << ENDPOINT << "Ignoring packet from unexpected GUID: "
- << header.public_header.guid << " instead of " << guid_;
+ if (header.public_header.connection_id != connection_id_) {
+ DVLOG(1) << ENDPOINT << "Ignoring packet from unexpected ConnectionId: "
+ << header.public_header.connection_id << " instead of "
+ << connection_id_;
return false;
}
if (!Near(header.packet_sequence_number,
last_header_.packet_sequence_number)) {
DVLOG(1) << ENDPOINT << "Packet " << header.packet_sequence_number
- << " out of bounds. Discarding";
+ << " out of bounds. Discarding";
SendConnectionCloseWithDetails(QUIC_INVALID_PACKET_HEADER,
"Packet sequence number out of bounds");
return false;
@@ -410,14 +451,18 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
// has told us will not be retransmitted, then stop processing the packet.
if (!received_packet_manager_.IsAwaitingPacket(
header.packet_sequence_number)) {
+ DVLOG(1) << ENDPOINT << "Packet " << header.packet_sequence_number
+ << " no longer being waited for. Discarding.";
+ // TODO(jri): Log reception of duplicate packets or packets the peer has
+ // told us to stop waiting for.
return false;
}
if (version_negotiation_state_ != NEGOTIATED_VERSION) {
if (is_server_) {
if (!header.public_header.version_flag) {
- DLOG(WARNING) << ENDPOINT << "Got packet without version flag before "
- << "version negotiated.";
+ DLOG(WARNING) << ENDPOINT << "Packet " << header.packet_sequence_number
+ << " without version flag before version negotiated.";
// Packets should have the version flag till version negotiation is
// done.
CloseConnection(QUIC_INVALID_VERSION, false);
@@ -432,7 +477,7 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
DCHECK(!header.public_header.version_flag);
// If the client gets a packet without the version flag from the server
// it should stop sending version since the version negotiation is done.
- packet_creator_.StopSendingVersion();
+ packet_generator_.StopSendingVersion();
version_negotiation_state_ = NEGOTIATED_VERSION;
visitor_->OnSuccessfulVersionNegotiation(version());
}
@@ -452,7 +497,7 @@ void QuicConnection::OnFecProtectedPayload(StringPiece payload) {
DCHECK_NE(0u, last_header_.fec_group);
QuicFecGroup* group = GetFecGroup();
if (group != NULL) {
- group->Update(last_header_, payload);
+ group->Update(last_decrypted_packet_level_, last_header_, payload);
}
}
@@ -461,6 +506,13 @@ bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) {
if (debug_visitor_) {
debug_visitor_->OnStreamFrame(frame);
}
+ if (frame.stream_id != kCryptoStreamId &&
+ last_decrypted_packet_level_ == ENCRYPTION_NONE) {
+ DLOG(WARNING) << ENDPOINT
+ << "Received an unencrypted data frame: closing connection";
+ SendConnectionClose(QUIC_UNENCRYPTED_STREAM_DATA);
+ return false;
+ }
last_stream_frames_.push_back(frame);
return true;
}
@@ -488,34 +540,39 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) {
void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) {
largest_seen_packet_with_ack_ = last_header_.packet_sequence_number;
-
- received_packet_manager_.UpdatePacketInformationReceivedByPeer(incoming_ack);
- received_packet_manager_.UpdatePacketInformationSentByPeer(incoming_ack);
- // Possibly close any FecGroups which are now irrelevant.
- CloseFecGroupsBefore(incoming_ack.sent_info.least_unacked + 1);
+ received_packet_manager_.UpdatePacketInformationReceivedByPeer(
+ incoming_ack.received_info);
+ if (version() <= QUIC_VERSION_15) {
+ ProcessStopWaitingFrame(incoming_ack.sent_info);
+ }
sent_entropy_manager_.ClearEntropyBefore(
received_packet_manager_.least_packet_awaited_by_peer() - 1);
- bool reset_retransmission_alarm =
- sent_packet_manager_.OnIncomingAck(incoming_ack.received_info,
- time_of_last_received_packet_);
+ sent_packet_manager_.OnIncomingAck(incoming_ack.received_info,
+ time_of_last_received_packet_);
if (sent_packet_manager_.HasPendingRetransmissions()) {
WriteIfNotBlocked();
}
- if (reset_retransmission_alarm) {
- retransmission_alarm_->Cancel();
- // Reset the RTO and FEC alarms if the are unacked packets.
- if (sent_packet_manager_.HasUnackedPackets()) {
- QuicTime::Delta retransmission_delay =
- sent_packet_manager_.GetRetransmissionDelay();
- retransmission_alarm_->Set(
- clock_->ApproximateNow().Add(retransmission_delay));
- }
+ // Always reset the retransmission alarm when an ack comes in, since we now
+ // have a better estimate of the current rtt than when it was set.
+ retransmission_alarm_->Cancel();
+ QuicTime retransmission_time =
+ sent_packet_manager_.GetRetransmissionTime();
+ if (retransmission_time != QuicTime::Zero()) {
+ retransmission_alarm_->Set(retransmission_time);
}
}
+void QuicConnection::ProcessStopWaitingFrame(
+ const QuicStopWaitingFrame& stop_waiting) {
+ largest_seen_packet_with_stop_waiting_ = last_header_.packet_sequence_number;
+ received_packet_manager_.UpdatePacketInformationSentByPeer(stop_waiting);
+ // Possibly close any FecGroups which are now irrelevant.
+ CloseFecGroupsBefore(stop_waiting.least_unacked + 1);
+}
+
bool QuicConnection::OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& feedback) {
DCHECK(connected_);
@@ -526,12 +583,42 @@ bool QuicConnection::OnCongestionFeedbackFrame(
return connected_;
}
+bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {
+ DCHECK(connected_);
+
+ if (last_header_.packet_sequence_number <=
+ largest_seen_packet_with_stop_waiting_) {
+ DVLOG(1) << ENDPOINT << "Received an old stop waiting frame: ignoring";
+ return true;
+ }
+
+ if (!ValidateStopWaitingFrame(frame)) {
+ SendConnectionClose(QUIC_INVALID_STOP_WAITING_DATA);
+ return false;
+ }
+
+ if (debug_visitor_) {
+ debug_visitor_->OnStopWaitingFrame(frame);
+ }
+
+ last_stop_waiting_frames_.push_back(frame);
+ return connected_;
+}
+
+bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_) {
+ debug_visitor_->OnPingFrame(frame);
+ }
+ return true;
+}
+
bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
if (incoming_ack.received_info.largest_observed >
- packet_creator_.sequence_number()) {
+ packet_generator_.sequence_number()) {
DLOG(ERROR) << ENDPOINT << "Peer's observed unsent packet:"
<< incoming_ack.received_info.largest_observed << " vs "
- << packet_creator_.sequence_number();
+ << packet_generator_.sequence_number();
// We got an error for data we have not sent. Error out.
return false;
}
@@ -546,22 +633,10 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
return false;
}
- if (incoming_ack.sent_info.least_unacked <
- received_packet_manager_.peer_least_packet_awaiting_ack()) {
- DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: "
- << incoming_ack.sent_info.least_unacked << " vs "
- << received_packet_manager_.peer_least_packet_awaiting_ack();
- // We never process old ack frames, so this number should only increase.
- return false;
- }
-
- if (incoming_ack.sent_info.least_unacked >
- last_header_.packet_sequence_number) {
- DLOG(ERROR) << ENDPOINT << "Peer sent least_unacked:"
- << incoming_ack.sent_info.least_unacked
- << " greater than the enclosing packet sequence number:"
- << last_header_.packet_sequence_number;
- return false;
+ if (version() <= QUIC_VERSION_15) {
+ if (!ValidateStopWaitingFrame(incoming_ack.sent_info)) {
+ return false;
+ }
}
if (!incoming_ack.received_info.missing_packets.empty() &&
@@ -592,6 +667,38 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
return false;
}
+ for (SequenceNumberSet::const_iterator iter =
+ incoming_ack.received_info.revived_packets.begin();
+ iter != incoming_ack.received_info.revived_packets.end(); ++iter) {
+ if (!ContainsKey(incoming_ack.received_info.missing_packets, *iter)) {
+ DLOG(ERROR) << ENDPOINT
+ << "Peer specified revived packet which was not missing.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QuicConnection::ValidateStopWaitingFrame(
+ const QuicStopWaitingFrame& stop_waiting) {
+ if (stop_waiting.least_unacked <
+ received_packet_manager_.peer_least_packet_awaiting_ack()) {
+ DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: "
+ << stop_waiting.least_unacked << " vs "
+ << received_packet_manager_.peer_least_packet_awaiting_ack();
+ // We never process old ack frames, so this number should only increase.
+ return false;
+ }
+
+ if (stop_waiting.least_unacked >
+ last_header_.packet_sequence_number) {
+ DLOG(ERROR) << ENDPOINT << "Peer sent least_unacked:"
+ << stop_waiting.least_unacked
+ << " greater than the enclosing packet sequence number:"
+ << last_header_.packet_sequence_number;
+ return false;
+ }
+
return true;
}
@@ -600,8 +707,8 @@ void QuicConnection::OnFecData(const QuicFecData& fec) {
DCHECK_NE(0u, last_header_.fec_group);
QuicFecGroup* group = GetFecGroup();
if (group != NULL) {
- group->UpdateFec(last_header_.packet_sequence_number,
- last_header_.entropy_flag, fec);
+ group->UpdateFec(last_decrypted_packet_level_,
+ last_header_.packet_sequence_number, fec);
}
}
@@ -611,7 +718,7 @@ bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
debug_visitor_->OnRstStreamFrame(frame);
}
DVLOG(1) << ENDPOINT << "Stream reset with error "
- << QuicUtils::StreamErrorToString(frame.error_code);
+ << QuicUtils::StreamErrorToString(frame.error_code);
last_rst_frames_.push_back(frame);
return connected_;
}
@@ -622,22 +729,48 @@ bool QuicConnection::OnConnectionCloseFrame(
if (debug_visitor_) {
debug_visitor_->OnConnectionCloseFrame(frame);
}
- DVLOG(1) << ENDPOINT << "Connection " << guid() << " closed with error "
- << QuicUtils::ErrorToString(frame.error_code)
- << " " << frame.error_details;
+ DVLOG(1) << ENDPOINT << "Connection " << connection_id()
+ << " closed with error "
+ << QuicUtils::ErrorToString(frame.error_code)
+ << " " << frame.error_details;
last_close_frames_.push_back(frame);
return connected_;
}
bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
DCHECK(connected_);
+ if (debug_visitor_) {
+ debug_visitor_->OnGoAwayFrame(frame);
+ }
DVLOG(1) << ENDPOINT << "Go away received with error "
- << QuicUtils::ErrorToString(frame.error_code)
- << " and reason:" << frame.reason_phrase;
+ << QuicUtils::ErrorToString(frame.error_code)
+ << " and reason:" << frame.reason_phrase;
last_goaway_frames_.push_back(frame);
return connected_;
}
+bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_) {
+ debug_visitor_->OnWindowUpdateFrame(frame);
+ }
+ DVLOG(1) << ENDPOINT << "WindowUpdate received for stream: "
+ << frame.stream_id << " with byte offset: " << frame.byte_offset;
+ last_window_update_frames_.push_back(frame);
+ return connected_;
+}
+
+bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_) {
+ debug_visitor_->OnBlockedFrame(frame);
+ }
+ DVLOG(1) << ENDPOINT << "Blocked frame received for stream: "
+ << frame.stream_id;
+ last_blocked_frames_.push_back(frame);
+ return connected_;
+}
+
void QuicConnection::OnPacketComplete() {
// Don't do anything if this packet closed the connection.
if (!connected_) {
@@ -646,38 +779,51 @@ void QuicConnection::OnPacketComplete() {
}
DVLOG(1) << ENDPOINT << (last_packet_revived_ ? "Revived" : "Got")
- << " packet " << last_header_.packet_sequence_number
- << " with " << last_ack_frames_.size() << " acks, "
- << last_congestion_frames_.size() << " congestions, "
- << last_goaway_frames_.size() << " goaways, "
- << last_rst_frames_.size() << " rsts, "
- << last_close_frames_.size() << " closes, "
- << last_stream_frames_.size()
- << " stream frames for " << last_header_.public_header.guid;
-
- // Must called before ack processing, because processing acks removes entries
- // from unacket_packets_, increasing the least_unacked.
- const bool last_packet_should_instigate_ack = ShouldLastPacketInstigateAck();
+ << " packet " << last_header_.packet_sequence_number
+ << " with " << last_ack_frames_.size() << " acks, "
+ << last_congestion_frames_.size() << " congestions, "
+ << last_stop_waiting_frames_.size() << " stop_waiting, "
+ << last_goaway_frames_.size() << " goaways, "
+ << last_window_update_frames_.size() << " window updates, "
+ << last_blocked_frames_.size() << " blocked, "
+ << last_rst_frames_.size() << " rsts, "
+ << last_close_frames_.size() << " closes, "
+ << last_stream_frames_.size()
+ << " stream frames for "
+ << last_header_.public_header.connection_id;
+
+ // Call MaybeQueueAck() before recording the received packet, since we want
+ // to trigger an ack if the newly received packet was previously missing.
+ MaybeQueueAck();
+
+ // Record received or revived packet to populate ack info correctly before
+ // processing stream frames, since the processing may result in a response
+ // packet with a bundled ack.
+ if (last_packet_revived_) {
+ received_packet_manager_.RecordPacketRevived(
+ last_header_.packet_sequence_number);
+ } else {
+ received_packet_manager_.RecordPacketReceived(
+ last_size_, last_header_, time_of_last_received_packet_);
+ }
- // If the incoming packet was missing, send an ack immediately.
- bool send_ack_immediately = received_packet_manager_.IsMissing(
- last_header_.packet_sequence_number);
+ if (!last_stream_frames_.empty()) {
+ visitor_->OnStreamFrames(last_stream_frames_);
+ }
- // Ensure the visitor can process the stream frames before recording and
- // processing the rest of the packet.
- if (last_stream_frames_.empty() ||
- visitor_->OnStreamFrames(last_stream_frames_)) {
- received_packet_manager_.RecordPacketReceived(last_size_,
- last_header_,
- time_of_last_received_packet_,
- last_packet_revived_);
- for (size_t i = 0; i < last_stream_frames_.size(); ++i) {
- stats_.stream_bytes_received +=
- last_stream_frames_[i].data.TotalBufferSize();
- }
+ for (size_t i = 0; i < last_stream_frames_.size(); ++i) {
+ stats_.stream_bytes_received +=
+ last_stream_frames_[i].data.TotalBufferSize();
}
- // Process stream resets, then acks, then congestion feedback.
+ // Process window updates, blocked, stream resets, acks, then congestion
+ // feedback.
+ if (!last_window_update_frames_.empty()) {
+ visitor_->OnWindowUpdateFrames(last_window_update_frames_);
+ }
+ if (!last_blocked_frames_.empty()) {
+ visitor_->OnBlockedFrames(last_blocked_frames_);
+ }
for (size_t i = 0; i < last_goaway_frames_.size(); ++i) {
visitor_->OnGoAway(last_goaway_frames_[i]);
}
@@ -691,6 +837,9 @@ void QuicConnection::OnPacketComplete() {
sent_packet_manager_.OnIncomingQuicCongestionFeedbackFrame(
last_congestion_frames_[i], time_of_last_received_packet_);
}
+ for (size_t i = 0; i < last_stop_waiting_frames_.size(); ++i) {
+ ProcessStopWaitingFrame(last_stop_waiting_frames_[i]);
+ }
if (!last_close_frames_.empty()) {
CloseConnection(last_close_frames_[0].error_code, true);
DCHECK(!connected_);
@@ -698,20 +847,48 @@ void QuicConnection::OnPacketComplete() {
// If there are new missing packets to report, send an ack immediately.
if (received_packet_manager_.HasNewMissingPackets()) {
- send_ack_immediately = true;
+ ack_queued_ = true;
+ ack_alarm_->Cancel();
}
- MaybeSendInResponseToPacket(send_ack_immediately,
- last_packet_should_instigate_ack);
+ UpdateStopWaitingCount();
ClearLastFrames();
}
+void QuicConnection::MaybeQueueAck() {
+ // If the incoming packet was missing, send an ack immediately.
+ ack_queued_ = received_packet_manager_.IsMissing(
+ last_header_.packet_sequence_number);
+
+ if (!ack_queued_ && ShouldLastPacketInstigateAck()) {
+ if (ack_alarm_->IsSet()) {
+ ack_queued_ = true;
+ } else {
+ // Send an ack much more quickly for crypto handshake packets.
+ QuicTime::Delta delayed_ack_time = sent_packet_manager_.DelayedAckTime();
+ if (last_stream_frames_.size() == 1 &&
+ last_stream_frames_[0].stream_id == kCryptoStreamId) {
+ delayed_ack_time = QuicTime::Delta::Zero();
+ }
+ ack_alarm_->Set(clock_->ApproximateNow().Add(delayed_ack_time));
+ DVLOG(1) << "Ack timer set; next packet or timer will trigger ACK.";
+ }
+ }
+
+ if (ack_queued_) {
+ ack_alarm_->Cancel();
+ }
+}
+
void QuicConnection::ClearLastFrames() {
last_stream_frames_.clear();
last_goaway_frames_.clear();
+ last_window_update_frames_.clear();
+ last_blocked_frames_.clear();
last_rst_frames_.clear();
last_ack_frames_.clear();
+ last_stop_waiting_frames_.clear();
last_congestion_frames_.clear();
}
@@ -719,7 +896,7 @@ QuicAckFrame* QuicConnection::CreateAckFrame() {
QuicAckFrame* outgoing_ack = new QuicAckFrame();
received_packet_manager_.UpdateReceivedPacketInfo(
&(outgoing_ack->received_info), clock_->ApproximateNow());
- UpdateSentPacketInfo(&(outgoing_ack->sent_info));
+ UpdateStopWaiting(&(outgoing_ack->sent_info));
DVLOG(1) << ENDPOINT << "Creating ack frame: " << *outgoing_ack;
return outgoing_ack;
}
@@ -728,82 +905,91 @@ QuicCongestionFeedbackFrame* QuicConnection::CreateFeedbackFrame() {
return new QuicCongestionFeedbackFrame(outgoing_congestion_feedback_);
}
-bool QuicConnection::ShouldLastPacketInstigateAck() {
+QuicStopWaitingFrame* QuicConnection::CreateStopWaitingFrame() {
+ QuicStopWaitingFrame stop_waiting;
+ UpdateStopWaiting(&stop_waiting);
+ return new QuicStopWaitingFrame(stop_waiting);
+}
+
+bool QuicConnection::ShouldLastPacketInstigateAck() const {
if (!last_stream_frames_.empty() ||
!last_goaway_frames_.empty() ||
- !last_rst_frames_.empty()) {
+ !last_rst_frames_.empty() ||
+ !last_window_update_frames_.empty() ||
+ !last_blocked_frames_.empty()) {
return true;
}
- // If the peer is still waiting for a packet that we are no
- // longer planning to send, we should send an ack to raise
- // the high water mark.
if (!last_ack_frames_.empty() &&
- !last_ack_frames_.back().received_info.missing_packets.empty()) {
- return sent_packet_manager_.GetLeastUnackedSentPacket() >
- *last_ack_frames_.back().received_info.missing_packets.begin();
+ last_ack_frames_.back().received_info.is_truncated) {
+ return true;
}
return false;
}
-void QuicConnection::MaybeSendInResponseToPacket(
- bool send_ack_immediately,
- bool last_packet_should_instigate_ack) {
- // |include_ack| is false since we decide about ack bundling below.
- ScopedPacketBundler bundler(this, false);
+void QuicConnection::UpdateStopWaitingCount() {
+ if (last_ack_frames_.empty()) {
+ return;
+ }
- if (last_packet_should_instigate_ack) {
- // In general, we ack every second packet. When we don't ack the first
- // packet, we set the delayed ack alarm. Thus, if the ack alarm is set
- // then we know this is the second packet, and we should send an ack.
- if (send_ack_immediately || ack_alarm_->IsSet()) {
- SendAck();
- DCHECK(!ack_alarm_->IsSet());
- } else {
- ack_alarm_->Set(clock_->ApproximateNow().Add(
- sent_packet_manager_.DelayedAckTime()));
- DVLOG(1) << "Ack timer set; next packet or timer will trigger ACK.";
- }
+ // If the peer is still waiting for a packet that we are no longer planning to
+ // send, send an ack to raise the high water mark.
+ if (!last_ack_frames_.back().received_info.missing_packets.empty() &&
+ GetLeastUnacked() >
+ *last_ack_frames_.back().received_info.missing_packets.begin()) {
+ ++stop_waiting_count_;
+ } else {
+ stop_waiting_count_ = 0;
}
+}
- if (!last_ack_frames_.empty()) {
- // Now the we have received an ack, we might be able to send packets which
- // are queued locally, or drain streams which are blocked.
- QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend(
- time_of_last_received_packet_, NOT_RETRANSMISSION,
- HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
- if (delay.IsZero()) {
- send_alarm_->Cancel();
- WriteIfNotBlocked();
- } else if (!delay.IsInfinite()) {
- send_alarm_->Cancel();
- send_alarm_->Set(time_of_last_received_packet_.Add(delay));
- }
+QuicPacketSequenceNumber QuicConnection::GetLeastUnacked() const {
+ return sent_packet_manager_.HasUnackedPackets() ?
+ sent_packet_manager_.GetLeastUnackedSentPacket() :
+ packet_generator_.sequence_number() + 1;
+}
+
+void QuicConnection::MaybeSendInResponseToPacket() {
+ if (!connected_) {
+ return;
+ }
+ ScopedPacketBundler bundler(this, ack_queued_ ? SEND_ACK : NO_ACK);
+
+ // Now that we have received an ack, we might be able to send packets which
+ // are queued locally, or drain streams which are blocked.
+ if (CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ OnCanWrite();
}
}
void QuicConnection::SendVersionNegotiationPacket() {
- scoped_ptr<QuicEncryptedPacket> version_packet(
- packet_creator_.SerializeVersionNegotiationPacket(
- framer_.supported_versions()));
- // TODO(satyamshekhar): implement zero server state negotiation.
- WriteResult result =
- writer_->WritePacket(version_packet->data(), version_packet->length(),
- self_address().address(), peer_address(), this);
- if (result.status == WRITE_STATUS_BLOCKED) {
- write_blocked_ = true;
- }
- if (result.status == WRITE_STATUS_OK ||
- (result.status == WRITE_STATUS_BLOCKED &&
- writer_->IsWriteBlockedDataBuffered())) {
- pending_version_negotiation_packet_ = false;
+ // TODO(alyssar): implement zero server state negotiation.
+ pending_version_negotiation_packet_ = true;
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked();
return;
}
+ scoped_ptr<QuicEncryptedPacket> version_packet(
+ packet_generator_.SerializeVersionNegotiationPacket(
+ framer_.supported_versions()));
+ WriteResult result = writer_->WritePacket(
+ version_packet->data(), version_packet->length(),
+ self_address().address(), peer_address());
+
if (result.status == WRITE_STATUS_ERROR) {
// We can't send an error as the socket is presumably borked.
CloseConnection(QUIC_PACKET_WRITE_ERROR, false);
+ return;
}
- pending_version_negotiation_packet_ = true;
+ if (result.status == WRITE_STATUS_BLOCKED) {
+ visitor_->OnWriteBlocked();
+ if (writer_->IsWriteBlockedDataBuffered()) {
+ pending_version_negotiation_packet_ = false;
+ }
+ return;
+ }
+
+ pending_version_negotiation_packet_ = false;
}
QuicConsumedData QuicConnection::SendStreamData(
@@ -811,6 +997,7 @@ QuicConsumedData QuicConnection::SendStreamData(
const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier::DelegateInterface* delegate) {
if (!fin && data.Empty()) {
LOG(DFATAL) << "Attempt to send empty stream frame";
@@ -823,11 +1010,23 @@ QuicConsumedData QuicConnection::SendStreamData(
notifier = new QuicAckNotifier(delegate);
}
- // Opportunistically bundle an ack with this outgoing packet, unless it's the
- // crypto stream.
- ScopedPacketBundler ack_bundler(this, id != kCryptoStreamId);
+ // Opportunistically bundle an ack with every outgoing packet.
+ // Particularly, we want to bundle with handshake packets since we don't know
+ // which decrypter will be used on an ack packet following a handshake
+ // packet (a handshake packet from client to server could result in a REJ or a
+ // SHLO from the server, leading to two different decrypters at the server.)
+ //
+ // TODO(jri): Note that ConsumeData may cause a response packet to be sent.
+ // We may end up sending stale ack information if there are undecryptable
+ // packets hanging around and/or there are revivable packets which may get
+ // handled after this packet is sent. Change ScopedPacketBundler to do the
+ // right thing: check ack_queued_, and then check undecryptable packets and
+ // also if there is possibility of revival. Only bundle an ack if there's no
+ // processing left that may cause received_info_ to change.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
QuicConsumedData consumed_data =
- packet_generator_.ConsumeData(id, data, offset, fin, notifier);
+ packet_generator_.ConsumeData(id, data, offset, fin, fec_protection,
+ notifier);
if (notifier &&
(consumed_data.bytes_consumed == 0 && !consumed_data.fin_consumed)) {
@@ -839,19 +1038,38 @@ QuicConsumedData QuicConnection::SendStreamData(
}
void QuicConnection::SendRstStream(QuicStreamId id,
- QuicRstStreamErrorCode error) {
- DVLOG(1) << "Sending RST_STREAM: " << id << " code: " << error;
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
// Opportunistically bundle an ack with this outgoing packet.
- ScopedPacketBundler ack_bundler(this, true);
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
+ packet_generator_.AddControlFrame(QuicFrame(new QuicRstStreamFrame(
+ id, AdjustErrorForVersion(error, version()), bytes_written)));
+}
+
+void QuicConnection::SendWindowUpdate(QuicStreamId id,
+ QuicStreamOffset byte_offset) {
+ // Opportunistically bundle an ack with this outgoing packet.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
packet_generator_.AddControlFrame(
- QuicFrame(new QuicRstStreamFrame(id, error)));
+ QuicFrame(new QuicWindowUpdateFrame(id, byte_offset)));
+}
+
+void QuicConnection::SendBlocked(QuicStreamId id) {
+ // Opportunistically bundle an ack with this outgoing packet.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
+ packet_generator_.AddControlFrame(QuicFrame(new QuicBlockedFrame(id)));
}
const QuicConnectionStats& QuicConnection::GetStats() {
// Update rtt and estimated bandwidth.
- stats_.rtt = sent_packet_manager_.SmoothedRtt().ToMicroseconds();
+ stats_.min_rtt_us =
+ sent_packet_manager_.GetRttStats()->min_rtt().ToMicroseconds();
+ stats_.srtt_us =
+ sent_packet_manager_.GetRttStats()->SmoothedRtt().ToMicroseconds();
stats_.estimated_bandwidth =
sent_packet_manager_.BandwidthEstimate().ToBytesPerSecond();
+ stats_.congestion_window = sent_packet_manager_.GetCongestionWindow();
+ stats_.max_packet_size = packet_generator_.max_packet_length();
return stats_;
}
@@ -867,18 +1085,7 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address,
last_packet_revived_ = false;
last_size_ = packet.length();
- address_migrating_ = false;
-
- if (peer_address_.address().empty()) {
- peer_address_ = peer_address;
- }
- if (self_address_.address().empty()) {
- self_address_ = self_address;
- }
-
- if (!(peer_address == peer_address_ && self_address == self_address_)) {
- address_migrating_ = true;
- }
+ CheckForAddressMigration(self_address, peer_address);
stats_.bytes_received += packet.length();
++stats_.packets_received;
@@ -895,101 +1102,128 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address,
<< last_header_.packet_sequence_number;
return;
}
+
+ ++stats_.packets_processed;
MaybeProcessUndecryptablePackets();
MaybeProcessRevivedPacket();
+ MaybeSendInResponseToPacket();
+ SetPingAlarm();
}
-bool QuicConnection::OnCanWrite() {
- write_blocked_ = false;
- return DoWrite();
-}
+void QuicConnection::CheckForAddressMigration(
+ const IPEndPoint& self_address, const IPEndPoint& peer_address) {
+ peer_ip_changed_ = false;
+ peer_port_changed_ = false;
+ self_ip_changed_ = false;
+ self_port_changed_ = false;
-bool QuicConnection::WriteIfNotBlocked() {
- if (write_blocked_) {
- return false;
+ if (peer_address_.address().empty()) {
+ peer_address_ = peer_address;
+ }
+ if (self_address_.address().empty()) {
+ self_address_ = self_address;
+ }
+
+ if (!peer_address.address().empty() && !peer_address_.address().empty()) {
+ peer_ip_changed_ = (peer_address.address() != peer_address_.address());
+ peer_port_changed_ = (peer_address.port() != peer_address_.port());
+
+ // Store in case we want to migrate connection in ProcessValidatedPacket.
+ migrating_peer_port_ = peer_address.port();
+ }
+
+ if (!self_address.address().empty() && !self_address_.address().empty()) {
+ self_ip_changed_ = (self_address.address() != self_address_.address());
+ self_port_changed_ = (self_address.port() != self_address_.port());
}
- return DoWrite();
}
-bool QuicConnection::DoWrite() {
- DCHECK(!write_blocked_);
- WriteQueuedPackets();
+void QuicConnection::OnCanWrite() {
+ DCHECK(!writer_->IsWriteBlocked());
+ WriteQueuedPackets();
WritePendingRetransmissions();
- IsHandshake pending_handshake = visitor_->HasPendingHandshake() ?
- IS_HANDSHAKE : NOT_HANDSHAKE;
// Sending queued packets may have caused the socket to become write blocked,
// or the congestion manager to prohibit sending. If we've sent everything
// we had queued and we're still not blocked, let the visitor know it can
// write more.
- if (CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- pending_handshake)) {
+ if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ return;
+ }
+
+ { // Limit the scope of the bundler.
// Set |include_ack| to false in bundler; ack inclusion happens elsewhere.
- scoped_ptr<ScopedPacketBundler> bundler(
- new ScopedPacketBundler(this, false));
- bool all_bytes_written = visitor_->OnCanWrite();
- bundler.reset();
- // After the visitor writes, it may have caused the socket to become write
- // blocked or the congestion manager to prohibit sending, so check again.
- pending_handshake = visitor_->HasPendingHandshake() ? IS_HANDSHAKE
- : NOT_HANDSHAKE;
- if (!all_bytes_written && !resume_writes_alarm_->IsSet() &&
- CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- pending_handshake)) {
- // We're not write blocked, but some stream didn't write out all of its
- // bytes. Register for 'immediate' resumption so we'll keep writing after
- // other quic connections have had a chance to use the socket.
- resume_writes_alarm_->Set(clock_->ApproximateNow());
- }
+ ScopedPacketBundler bundler(this, NO_ACK);
+ visitor_->OnCanWrite();
+ }
+
+ // After the visitor writes, it may have caused the socket to become write
+ // blocked or the congestion manager to prohibit sending, so check again.
+ if (visitor_->WillingAndAbleToWrite() &&
+ !resume_writes_alarm_->IsSet() &&
+ CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ // We're not write blocked, but some stream didn't write out all of its
+ // bytes. Register for 'immediate' resumption so we'll keep writing after
+ // other connections and events have had a chance to use the thread.
+ resume_writes_alarm_->Set(clock_->ApproximateNow());
}
+}
- return !write_blocked_;
+void QuicConnection::WriteIfNotBlocked() {
+ if (!writer_->IsWriteBlocked()) {
+ OnCanWrite();
+ }
}
bool QuicConnection::ProcessValidatedPacket() {
- if (address_migrating_) {
+ if ((!FLAGS_quic_allow_port_migration && peer_port_changed_) ||
+ peer_ip_changed_ || self_ip_changed_ || self_port_changed_) {
SendConnectionCloseWithDetails(
QUIC_ERROR_MIGRATING_ADDRESS,
- "Address migration is not yet a supported feature");
+ "Neither IP address migration, nor self port migration are supported.");
return false;
}
+
+ // Port migration is supported, do it now if port has changed.
+ if (FLAGS_quic_allow_port_migration &&
+ peer_port_changed_) {
+ DVLOG(1) << ENDPOINT << "Peer's port changed from "
+ << peer_address_.port() << " to " << migrating_peer_port_
+ << ", migrating connection.";
+ peer_address_ = IPEndPoint(peer_address_.address(), migrating_peer_port_);
+ }
+
time_of_last_received_packet_ = clock_->Now();
DVLOG(1) << ENDPOINT << "time of last received packet: "
<< time_of_last_received_packet_.ToDebuggingValue();
if (is_server_ && encryption_level_ == ENCRYPTION_NONE &&
- last_size_ > options()->max_packet_length) {
- options()->max_packet_length = last_size_;
+ last_size_ > packet_generator_.max_packet_length()) {
+ packet_generator_.set_max_packet_length(last_size_);
}
return true;
}
-bool QuicConnection::WriteQueuedPackets() {
- DCHECK(!write_blocked_);
+void QuicConnection::WriteQueuedPackets() {
+ DCHECK(!writer_->IsWriteBlocked());
if (pending_version_negotiation_packet_) {
SendVersionNegotiationPacket();
}
QueuedPacketList::iterator packet_iterator = queued_packets_.begin();
- while (!write_blocked_ && packet_iterator != queued_packets_.end()) {
- if (WritePacket(packet_iterator->encryption_level,
- packet_iterator->sequence_number,
- packet_iterator->packet,
- packet_iterator->transmission_type,
- packet_iterator->retransmittable,
- packet_iterator->handshake,
- packet_iterator->forced)) {
+ while (!writer_->IsWriteBlocked() &&
+ packet_iterator != queued_packets_.end()) {
+ if (WritePacket(*packet_iterator)) {
+ delete packet_iterator->packet;
packet_iterator = queued_packets_.erase(packet_iterator);
} else {
// Continue, because some queued packets may still be writable.
- // This can happen if a retransmit send fail.
+ // This can happen if a retransmit send fails.
++packet_iterator;
}
}
-
- return !write_blocked_;
}
void QuicConnection::WritePendingRetransmissions() {
@@ -998,9 +1232,8 @@ void QuicConnection::WritePendingRetransmissions() {
while (sent_packet_manager_.HasPendingRetransmissions()) {
const QuicSentPacketManager::PendingRetransmission pending =
sent_packet_manager_.NextPendingRetransmission();
- if (HasForcedFrames(&pending.retransmittable_frames) == NO_FORCE &&
- !CanWrite(pending.transmission_type, HAS_RETRANSMITTABLE_DATA,
- HasCryptoHandshake(&pending.retransmittable_frames))) {
+ if (GetPacketType(&pending.retransmittable_frames) == NORMAL &&
+ !CanWrite(HAS_RETRANSMITTABLE_DATA)) {
break;
}
@@ -1008,22 +1241,23 @@ void QuicConnection::WritePendingRetransmissions() {
// Retransmitted data packets do not use FEC, even when it's enabled.
// Retransmitted packets use the same sequence number length as the
// original.
- // Flush the packet creator before making a new packet.
+ // Flush the packet generator before making a new packet.
// TODO(ianswett): Implement ReserializeAllFrames as a separate path that
// does not require the creator to be flushed.
- Flush();
- SerializedPacket serialized_packet = packet_creator_.ReserializeAllFrames(
+ packet_generator_.FlushAllQueuedFrames();
+ SerializedPacket serialized_packet = packet_generator_.ReserializeAllFrames(
pending.retransmittable_frames.frames(),
pending.sequence_number_length);
DVLOG(1) << ENDPOINT << "Retransmitting " << pending.sequence_number
- << " as " << serialized_packet.sequence_number;
+ << " as " << serialized_packet.sequence_number;
if (debug_visitor_) {
debug_visitor_->OnPacketRetransmitted(
pending.sequence_number, serialized_packet.sequence_number);
}
sent_packet_manager_.OnRetransmittedPacket(
- pending.sequence_number, serialized_packet.sequence_number);
+ pending.sequence_number,
+ serialized_packet.sequence_number);
SendOrQueuePacket(pending.retransmittable_frames.encryption_level(),
serialized_packet,
@@ -1038,6 +1272,16 @@ void QuicConnection::RetransmitUnackedPackets(
WriteIfNotBlocked();
}
+void QuicConnection::NeuterUnencryptedPackets() {
+ sent_packet_manager_.NeuterUnencryptedPackets();
+ // This may have changed the retransmission timer, so re-arm it.
+ retransmission_alarm_->Cancel();
+ QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime();
+ if (retransmission_time != QuicTime::Zero()) {
+ retransmission_alarm_->Set(retransmission_time);
+ }
+}
+
bool QuicConnection::ShouldGeneratePacket(
TransmissionType transmission_type,
HasRetransmittableData retransmittable,
@@ -1048,35 +1292,25 @@ bool QuicConnection::ShouldGeneratePacket(
return true;
}
- return CanWrite(transmission_type, retransmittable, handshake);
+ return CanWrite(retransmittable);
}
-bool QuicConnection::CanWrite(TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake) {
- if (write_blocked_) {
- return false;
- }
-
- // TODO(rch): consider removing this check so that if an ACK comes in
- // before the alarm goes it, we might be able send out a packet.
- // This check assumes that if the send alarm is set, it applies equally to all
- // types of transmissions.
- if (send_alarm_->IsSet()) {
- DVLOG(1) << "Send alarm set. Not sending.";
+bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) {
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked();
return false;
}
+ send_alarm_->Cancel();
QuicTime now = clock_->Now();
QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend(
- now, transmission_type, retransmittable, handshake);
+ now, retransmittable);
if (delay.IsInfinite()) {
return false;
}
// If the scheduler requires a delay, then we can not send this packet now.
if (!delay.IsZero()) {
- send_alarm_->Cancel();
send_alarm_->Set(now.Add(delay));
DVLOG(1) << "Delaying sending.";
return false;
@@ -1084,122 +1318,103 @@ bool QuicConnection::CanWrite(TransmissionType transmission_type,
return true;
}
-void QuicConnection::SetupRetransmissionAlarm(
- QuicPacketSequenceNumber sequence_number) {
- if (!sent_packet_manager_.HasRetransmittableFrames(sequence_number)) {
- DVLOG(1) << ENDPOINT << "Will not retransmit packet " << sequence_number;
- return;
- }
-
- // Do not set the retransmission alarm if we're already handling one, since
- // it will be reset when OnRetransmissionTimeout completes.
- if (retransmission_alarm_->IsSet()) {
- return;
- }
-
- QuicTime::Delta retransmission_delay =
- sent_packet_manager_.GetRetransmissionDelay();
- retransmission_alarm_->Set(
- clock_->ApproximateNow().Add(retransmission_delay));
-}
-
-bool QuicConnection::WritePacket(EncryptionLevel level,
- QuicPacketSequenceNumber sequence_number,
- QuicPacket* packet,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake,
- Force forced) {
- if (ShouldDiscardPacket(level, sequence_number, retransmittable)) {
- delete packet;
+bool QuicConnection::WritePacket(QueuedPacket packet) {
+ QuicPacketSequenceNumber sequence_number = packet.sequence_number;
+ if (ShouldDiscardPacket(packet.encryption_level,
+ sequence_number,
+ packet.retransmittable)) {
+ ++stats_.packets_discarded;
return true;
}
- // If we're write blocked, we know we can't write.
- if (write_blocked_) {
- return false;
- }
-
- // If we are not forced and we can't write, then simply return false;
- if (forced == NO_FORCE &&
- !CanWrite(transmission_type, retransmittable, handshake)) {
+ // If the packet is CONNECTION_CLOSE, we need to try to send it immediately
+ // and encrypt it to hand it off to TimeWaitListManager.
+ // If the packet is QUEUED, we don't re-consult the congestion control.
+ // This ensures packets are sent in sequence number order.
+ // TODO(ianswett): The congestion control should have been consulted before
+ // serializing the packet, so this could be turned into a LOG_IF(DFATAL).
+ if (packet.type == NORMAL && !CanWrite(packet.retransmittable)) {
return false;
}
// Some encryption algorithms require the packet sequence numbers not be
// repeated.
- DCHECK_LE(sequence_number_of_last_inorder_packet_, sequence_number);
- // Only increase this when packets have not been queued. Once they're queued
- // due to a write block, there is the chance of sending forced and other
- // higher priority packets out of order.
- if (queued_packets_.empty()) {
- sequence_number_of_last_inorder_packet_ = sequence_number;
- }
+ DCHECK_LE(sequence_number_of_last_sent_packet_, sequence_number);
+ sequence_number_of_last_sent_packet_ = sequence_number;
- scoped_ptr<QuicEncryptedPacket> encrypted(
- framer_.EncryptPacket(level, sequence_number, *packet));
- if (encrypted.get() == NULL) {
+ QuicEncryptedPacket* encrypted = framer_.EncryptPacket(
+ packet.encryption_level, sequence_number, *packet.packet);
+ if (encrypted == NULL) {
LOG(DFATAL) << ENDPOINT << "Failed to encrypt packet number "
<< sequence_number;
+ // CloseConnection does not send close packet, so no infinite loop here.
CloseConnection(QUIC_ENCRYPTION_FAILURE, false);
return false;
}
- // If it's the ConnectionClose packet, the only FORCED frame type,
- // clone a copy for resending later by the TimeWaitListManager.
- if (forced == FORCE) {
+ // Connection close packets are eventually owned by TimeWaitListManager.
+ // Others are deleted at the end of this call.
+ scoped_ptr<QuicEncryptedPacket> encrypted_deleter;
+ if (packet.type == CONNECTION_CLOSE) {
DCHECK(connection_close_packet_.get() == NULL);
- connection_close_packet_.reset(encrypted->Clone());
- }
-
- if (encrypted->length() > options()->max_packet_length) {
- LOG(DFATAL) << "Writing an encrypted packet larger than max_packet_length:"
- << options()->max_packet_length << " encrypted length: "
- << encrypted->length();
- }
- DVLOG(1) << ENDPOINT << "Sending packet number " << sequence_number
- << " : " << (packet->is_fec_packet() ? "FEC " :
- (retransmittable == HAS_RETRANSMITTABLE_DATA
- ? "data bearing " : " ack only "))
- << ", encryption level: "
- << QuicUtils::EncryptionLevelToString(level)
- << ", length:" << packet->length() << ", encrypted length:"
- << encrypted->length();
+ connection_close_packet_.reset(encrypted);
+ // This assures we won't try to write *forced* packets when blocked.
+ // Return true to stop processing.
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked();
+ return true;
+ }
+ } else {
+ encrypted_deleter.reset(encrypted);
+ }
+
+ LOG_IF(DFATAL, encrypted->length() >
+ packet_generator_.max_packet_length())
+ << "Writing an encrypted packet larger than max_packet_length:"
+ << packet_generator_.max_packet_length() << " encrypted length: "
+ << encrypted->length();
+ DVLOG(1) << ENDPOINT << "Sending packet " << sequence_number
+ << " : " << (packet.packet->is_fec_packet() ? "FEC " :
+ (packet.retransmittable == HAS_RETRANSMITTABLE_DATA
+ ? "data bearing " : " ack only "))
+ << ", encryption level: "
+ << QuicUtils::EncryptionLevelToString(packet.encryption_level)
+ << ", length:" << packet.packet->length() << ", encrypted length:"
+ << encrypted->length();
DVLOG(2) << ENDPOINT << "packet(" << sequence_number << "): " << std::endl
- << QuicUtils::StringToHexASCIIDump(packet->AsStringPiece());
+ << QuicUtils::StringToHexASCIIDump(packet.packet->AsStringPiece());
- DCHECK(encrypted->length() <= kMaxPacketSize)
+ DCHECK(encrypted->length() <= kMaxPacketSize ||
+ FLAGS_quic_allow_oversized_packets_for_test)
<< "Packet " << sequence_number << " will not be read; too large: "
- << packet->length() << " " << encrypted->length() << " "
- << " forced: " << (forced == FORCE ? "yes" : "no");
+ << packet.packet->length() << " " << encrypted->length() << " "
+ << " close: " << (packet.type == CONNECTION_CLOSE ? "yes" : "no");
DCHECK(pending_write_.get() == NULL);
- pending_write_.reset(new PendingWrite(sequence_number, transmission_type,
- retransmittable, level,
- packet->is_fec_packet(),
- packet->length()));
-
- WriteResult result =
- writer_->WritePacket(encrypted->data(), encrypted->length(),
- self_address().address(), peer_address(), this);
+ pending_write_.reset(new QueuedPacket(packet));
+
+ WriteResult result = writer_->WritePacket(encrypted->data(),
+ encrypted->length(),
+ self_address().address(),
+ peer_address());
if (result.error_code == ERR_IO_PENDING) {
DCHECK_EQ(WRITE_STATUS_BLOCKED, result.status);
}
if (debug_visitor_) {
// Pass the write result to the visitor.
- debug_visitor_->OnPacketSent(sequence_number, level, *encrypted, result);
+ debug_visitor_->OnPacketSent(sequence_number,
+ packet.encryption_level,
+ packet.transmission_type,
+ *encrypted,
+ result);
}
if (result.status == WRITE_STATUS_BLOCKED) {
- // TODO(satyashekhar): It might be more efficient (fewer system calls), if
- // all connections share this variable i.e this becomes a part of
- // PacketWriterInterface.
- write_blocked_ = true;
+ visitor_->OnWriteBlocked();
// If the socket buffers the the data, then the packet should not
// be queued and sent again, which would result in an unnecessary
// duplicate packet being sent. The helper must call OnPacketSent
// when the packet is actually sent.
if (writer_->IsWriteBlockedDataBuffered()) {
- delete packet;
return true;
}
pending_write_.reset();
@@ -1207,7 +1422,6 @@ bool QuicConnection::WritePacket(EncryptionLevel level,
}
if (OnPacketSent(result)) {
- delete packet;
return true;
}
return false;
@@ -1219,7 +1433,15 @@ bool QuicConnection::ShouldDiscardPacket(
HasRetransmittableData retransmittable) {
if (!connected_) {
DVLOG(1) << ENDPOINT
- << "Not sending packet as connection is disconnected.";
+ << "Not sending packet as connection is disconnected.";
+ return true;
+ }
+
+ // If the packet has been discarded before sending, don't send it.
+ // This occurs if a packet gets serialized, queued, then discarded.
+ if (!sent_packet_manager_.IsUnacked(sequence_number)) {
+ DVLOG(1) << ENDPOINT << "Dropping packet before sending: "
+ << sequence_number << " since it has already been discarded.";
return true;
}
@@ -1227,43 +1449,20 @@ bool QuicConnection::ShouldDiscardPacket(
level == ENCRYPTION_NONE) {
// Drop packets that are NULL encrypted since the peer won't accept them
// anymore.
- DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since the packet is NULL encrypted.";
- sent_packet_manager_.DiscardUnackedPacket(sequence_number);
+ DVLOG(1) << ENDPOINT << "Dropping NULL encrypted packet: "
+ << sequence_number << " since the connection is forward secure.";
+ LOG_IF(DFATAL,
+ sent_packet_manager_.HasRetransmittableFrames(sequence_number))
+ << "Once forward secure, all NULL encrypted packets should be "
+ << "neutered.";
return true;
}
- if (retransmittable == HAS_RETRANSMITTABLE_DATA) {
- if (!sent_packet_manager_.IsUnacked(sequence_number)) {
- // This is a crazy edge case, but if we retransmit a packet,
- // (but have to queue it for some reason) then receive an ack
- // for the previous transmission (but not the retransmission)
- // then receive a truncated ACK which causes us to raise the
- // high water mark, all before we're able to send the packet
- // then we can simply drop it.
- DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since it has already been acked.";
- return true;
- }
-
- if (sent_packet_manager_.IsPreviousTransmission(sequence_number)) {
- // If somehow we have already retransmitted this packet *before*
- // we actually send it for the first time (I think this is probably
- // impossible in the real world), then don't bother sending it.
- // We don't want to call DiscardUnackedPacket because in this case
- // the peer has not yet ACK'd the data. We need the subsequent
- // retransmission to be sent.
- DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since it has already been retransmitted.";
- return true;
- }
-
- if (!sent_packet_manager_.HasRetransmittableFrames(sequence_number)) {
- DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number
- << " since a previous transmission has been acked.";
- sent_packet_manager_.DiscardUnackedPacket(sequence_number);
- return true;
- }
+ if (retransmittable == HAS_RETRANSMITTABLE_DATA &&
+ !sent_packet_manager_.HasRetransmittableFrames(sequence_number)) {
+ DVLOG(1) << ENDPOINT << "Dropping unacked packet: " << sequence_number
+ << " A previous transmission was acked while write blocked.";
+ return true;
}
return false;
@@ -1279,12 +1478,12 @@ bool QuicConnection::OnPacketSent(WriteResult result) {
QuicPacketSequenceNumber sequence_number = pending_write_->sequence_number;
TransmissionType transmission_type = pending_write_->transmission_type;
HasRetransmittableData retransmittable = pending_write_->retransmittable;
- bool is_fec_packet = pending_write_->is_fec_packet;
size_t length = pending_write_->length;
pending_write_.reset();
if (result.status == WRITE_STATUS_ERROR) {
- DVLOG(1) << "Write failed with error code: " << result.error_code;
+ DVLOG(1) << "Write failed with error: " << result.error_code << " ("
+ << ErrorToString(result.error_code) << ")";
// We can't send an error as the socket is presumably borked.
CloseConnection(QUIC_PACKET_WRITE_ERROR, false);
return false;
@@ -1292,33 +1491,34 @@ bool QuicConnection::OnPacketSent(WriteResult result) {
QuicTime now = clock_->Now();
if (transmission_type == NOT_RETRANSMISSION) {
- time_of_last_sent_packet_ = now;
+ time_of_last_sent_new_packet_ = now;
}
+ SetPingAlarm();
DVLOG(1) << ENDPOINT << "time of last sent packet: "
<< now.ToDebuggingValue();
- // Set the retransmit alarm only when we have sent the packet to the client
- // and not when it goes to the pending queue, otherwise we will end up adding
- // an entry to retransmission_timeout_ every time we attempt a write.
- if (retransmittable == HAS_RETRANSMITTABLE_DATA || is_fec_packet) {
- SetupRetransmissionAlarm(sequence_number);
- }
-
// TODO(ianswett): Change the sequence number length and other packet creator
// options by a more explicit API than setting a struct value directly.
- packet_creator_.UpdateSequenceNumberLength(
+ packet_generator_.UpdateSequenceNumberLength(
received_packet_manager_.least_packet_awaited_by_peer(),
- sent_packet_manager_.BandwidthEstimate().ToBytesPerPeriod(
- sent_packet_manager_.SmoothedRtt()));
+ sent_packet_manager_.GetCongestionWindow());
+
+ bool reset_retransmission_alarm =
+ sent_packet_manager_.OnPacketSent(sequence_number, now, length,
+ transmission_type, retransmittable);
- sent_packet_manager_.OnPacketSent(sequence_number, now, length,
- transmission_type, retransmittable);
+ if (reset_retransmission_alarm || !retransmission_alarm_->IsSet()) {
+ retransmission_alarm_->Cancel();
+ QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime();
+ if (retransmission_time != QuicTime::Zero()) {
+ retransmission_alarm_->Set(retransmission_time);
+ }
+ }
stats_.bytes_sent += result.bytes_written;
++stats_.packets_sent;
- if (transmission_type == NACK_RETRANSMISSION ||
- transmission_type == RTO_RETRANSMISSION) {
+ if (transmission_type != NOT_RETRANSMISSION) {
stats_.bytes_retransmitted += result.bytes_written;
++stats_.packets_retransmitted;
}
@@ -1340,39 +1540,59 @@ bool QuicConnection::OnSerializedPacket(
NOT_RETRANSMISSION);
}
-QuicPacketSequenceNumber QuicConnection::GetNextPacketSequenceNumber() {
- return packet_creator_.sequence_number() + 1;
-}
-
bool QuicConnection::SendOrQueuePacket(EncryptionLevel level,
const SerializedPacket& packet,
TransmissionType transmission_type) {
- IsHandshake handshake = HasCryptoHandshake(packet.retransmittable_frames);
- Force forced = HasForcedFrames(packet.retransmittable_frames);
- HasRetransmittableData retransmittable =
- (transmission_type != NOT_RETRANSMISSION ||
- packet.retransmittable_frames != NULL) ?
- HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA;
+ if (packet.packet == NULL) {
+ LOG(DFATAL) << "NULL packet passed in to SendOrQueuePacket";
+ return true;
+ }
+
sent_entropy_manager_.RecordPacketEntropyHash(packet.sequence_number,
packet.entropy_hash);
- if (WritePacket(level, packet.sequence_number, packet.packet,
- transmission_type, retransmittable, handshake, forced)) {
+ QueuedPacket queued_packet(packet, level, transmission_type);
+ // If there are already queued packets, put this at the end,
+ // unless it's ConnectionClose, in which case it is written immediately.
+ if ((queued_packet.type == CONNECTION_CLOSE || queued_packets_.empty()) &&
+ WritePacket(queued_packet)) {
+ delete packet.packet;
return true;
}
- queued_packets_.push_back(QueuedPacket(packet.sequence_number, packet.packet,
- level, transmission_type,
- retransmittable, handshake, forced));
+ queued_packet.type = QUEUED;
+ queued_packets_.push_back(queued_packet);
return false;
}
-void QuicConnection::UpdateSentPacketInfo(SentPacketInfo* sent_info) {
- sent_info->least_unacked = sent_packet_manager_.GetLeastUnackedSentPacket();
- sent_info->entropy_hash = sent_entropy_manager_.EntropyHash(
- sent_info->least_unacked - 1);
+void QuicConnection::UpdateStopWaiting(QuicStopWaitingFrame* stop_waiting) {
+ stop_waiting->least_unacked = GetLeastUnacked();
+ stop_waiting->entropy_hash = sent_entropy_manager_.EntropyHash(
+ stop_waiting->least_unacked - 1);
+}
+
+void QuicConnection::SendPing() {
+ if (retransmission_alarm_->IsSet()) {
+ return;
+ }
+ if (version() <= QUIC_VERSION_17) {
+ // TODO(rch): remove this when we remove version 17.
+ // This is a horrible hideous hack which we should not support.
+ IOVector data;
+ char c_data[] = "C";
+ data.Append(c_data, 1);
+ QuicConsumedData consumed_data =
+ packet_generator_.ConsumeData(kCryptoStreamId, data, 0, false,
+ MAY_FEC_PROTECT, NULL);
+ if (consumed_data.bytes_consumed == 0) {
+ DLOG(ERROR) << "Unable to send ping!?";
+ }
+ } else {
+ packet_generator_.AddControlFrame(QuicFrame(new QuicPingFrame));
+ }
}
void QuicConnection::SendAck() {
ack_alarm_->Cancel();
+ stop_waiting_count_ = 0;
// TODO(rch): delay this until the CreateFeedbackFrame
// method is invoked. This requires changes SetShouldSendAck
// to be a no-arg method, and re-jiggering its implementation.
@@ -1384,7 +1604,8 @@ void QuicConnection::SendAck() {
send_feedback = true;
}
- packet_generator_.SetShouldSendAck(send_feedback);
+ packet_generator_.SetShouldSendAck(send_feedback,
+ version() > QUIC_VERSION_15);
}
void QuicConnection::OnRetransmissionTimeout() {
@@ -1392,18 +1613,22 @@ void QuicConnection::OnRetransmissionTimeout() {
return;
}
- ++stats_.rto_count;
-
sent_packet_manager_.OnRetransmissionTimeout();
-
WriteIfNotBlocked();
+ // In the TLP case, the SentPacketManager gives the connection the opportunity
+ // to send new data before retransmitting.
+ if (sent_packet_manager_.MaybeRetransmitTailLossProbe()) {
+ // Send the pending retransmission now that it's been queued.
+ WriteIfNotBlocked();
+ }
- // Ensure the retransmission alarm is always set if there are unacked packets.
- if (sent_packet_manager_.HasUnackedPackets() && !HasQueuedData() &&
- !retransmission_alarm_->IsSet()) {
- QuicTime rto_timeout = clock_->ApproximateNow().Add(
- sent_packet_manager_.GetRetransmissionDelay());
- retransmission_alarm_->Set(rto_timeout);
+ // Ensure the retransmission alarm is always set if there are unacked packets
+ // and nothing waiting to be sent.
+ if (!HasQueuedData() && !retransmission_alarm_->IsSet()) {
+ QuicTime rto_timeout = sent_packet_manager_.GetRetransmissionTime();
+ if (rto_timeout != QuicTime::Zero()) {
+ retransmission_alarm_->Set(rto_timeout);
+ }
}
}
@@ -1416,18 +1641,20 @@ const QuicEncrypter* QuicConnection::encrypter(EncryptionLevel level) const {
return framer_.encrypter(level);
}
-void QuicConnection::SetDefaultEncryptionLevel(
- EncryptionLevel level) {
+void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) {
encryption_level_ = level;
+ packet_generator_.set_encryption_level(level);
}
-void QuicConnection::SetDecrypter(QuicDecrypter* decrypter) {
- framer_.SetDecrypter(decrypter);
+void QuicConnection::SetDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level) {
+ framer_.SetDecrypter(decrypter, level);
}
void QuicConnection::SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
bool latch_once_used) {
- framer_.SetAlternativeDecrypter(decrypter, latch_once_used);
+ framer_.SetAlternativeDecrypter(decrypter, level, latch_once_used);
}
const QuicDecrypter* QuicConnection::decrypter() const {
@@ -1445,8 +1672,7 @@ void QuicConnection::QueueUndecryptablePacket(
}
void QuicConnection::MaybeProcessUndecryptablePackets() {
- if (undecryptable_packets_.empty() ||
- encryption_level_ == ENCRYPTION_NONE) {
+ if (undecryptable_packets_.empty() || encryption_level_ == ENCRYPTION_NONE) {
return;
}
@@ -1459,6 +1685,7 @@ void QuicConnection::MaybeProcessUndecryptablePackets() {
break;
}
DVLOG(1) << ENDPOINT << "Processed undecryptable packet!";
+ ++stats_.packets_processed;
delete packet;
undecryptable_packets_.pop_front();
}
@@ -1479,13 +1706,19 @@ void QuicConnection::MaybeProcessRevivedPacket() {
QuicPacketHeader revived_header;
char revived_payload[kMaxPacketSize];
size_t len = group->Revive(&revived_header, revived_payload, kMaxPacketSize);
- revived_header.public_header.guid = guid_;
+ revived_header.public_header.connection_id = connection_id_;
+ revived_header.public_header.connection_id_length =
+ last_header_.public_header.connection_id_length;
revived_header.public_header.version_flag = false;
revived_header.public_header.reset_flag = false;
+ revived_header.public_header.sequence_number_length =
+ last_header_.public_header.sequence_number_length;
revived_header.fec_flag = false;
revived_header.is_in_fec_group = NOT_IN_FEC_GROUP;
revived_header.fec_group = 0;
group_map_.erase(last_header_.fec_group);
+ last_decrypted_packet_level_ = group->effective_encryption_level();
+ DCHECK_LT(last_decrypted_packet_level_, NUM_ENCRYPTION_LEVELS);
delete group;
last_packet_revived_ = true;
@@ -1526,27 +1759,36 @@ void QuicConnection::SendConnectionClose(QuicErrorCode error) {
void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error,
const string& details) {
- if (!write_blocked_) {
- SendConnectionClosePacket(error, details);
+ // If we're write blocked, WritePacket() will not send, but will capture the
+ // serialized packet.
+ SendConnectionClosePacket(error, details);
+ if (connected_) {
+ // It's possible that while sending the connection close packet, we get a
+ // socket error and disconnect right then and there. Avoid a double
+ // disconnect in that case.
+ CloseConnection(error, false);
}
- CloseConnection(error, false);
}
void QuicConnection::SendConnectionClosePacket(QuicErrorCode error,
const string& details) {
- DVLOG(1) << ENDPOINT << "Force closing " << guid() << " with error "
- << QuicUtils::ErrorToString(error) << " (" << error << ") "
- << details;
- ScopedPacketBundler ack_bundler(this, true);
+ DVLOG(1) << ENDPOINT << "Force closing " << connection_id()
+ << " with error " << QuicUtils::ErrorToString(error)
+ << " (" << error << ") " << details;
+ ScopedPacketBundler ack_bundler(this, SEND_ACK);
QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame();
frame->error_code = error;
frame->error_details = details;
packet_generator_.AddControlFrame(QuicFrame(frame));
- Flush();
+ packet_generator_.FlushAllQueuedFrames();
}
void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) {
- DCHECK(connected_);
+ if (!connected_) {
+ DLOG(DFATAL) << "Error: attempt to close an already closed connection"
+ << base::debug::StackTrace().ToString();
+ return;
+ }
connected_ = false;
visitor_->OnConnectionClosed(error, from_peer);
// Cancel the alarms so they don't trigger any action now that the
@@ -1562,11 +1804,11 @@ void QuicConnection::SendGoAway(QuicErrorCode error,
QuicStreamId last_good_stream_id,
const string& reason) {
DVLOG(1) << ENDPOINT << "Going away with error "
- << QuicUtils::ErrorToString(error)
- << " (" << error << ")";
+ << QuicUtils::ErrorToString(error)
+ << " (" << error << ")";
// Opportunistically bundle an ack with this outgoing packet.
- ScopedPacketBundler ack_bundler(this, true);
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
packet_generator_.AddControlFrame(
QuicFrame(new QuicGoAwayFrame(error, last_good_stream_id, reason)));
}
@@ -1592,8 +1834,12 @@ void QuicConnection::CloseFecGroupsBefore(
}
}
-void QuicConnection::Flush() {
- packet_generator_.FlushAllQueuedFrames();
+size_t QuicConnection::max_packet_length() const {
+ return packet_generator_.max_packet_length();
+}
+
+void QuicConnection::set_max_packet_length(size_t length) {
+ return packet_generator_.set_max_packet_length(length);
}
bool QuicConnection::HasQueuedData() const {
@@ -1601,6 +1847,23 @@ bool QuicConnection::HasQueuedData() const {
!queued_packets_.empty() || packet_generator_.HasQueuedFrames();
}
+bool QuicConnection::CanWriteStreamData() {
+ // Don't write stream data if there are negotiation or queued data packets
+ // to send. Otherwise, continue and bundle as many frames as possible.
+ if (pending_version_negotiation_packet_ || !queued_packets_.empty()) {
+ return false;
+ }
+
+ IsHandshake pending_handshake = visitor_->HasPendingHandshake() ?
+ IS_HANDSHAKE : NOT_HANDSHAKE;
+ // Sending queued packets may have caused the socket to become write blocked,
+ // or the congestion manager to prohibit sending. If we've sent everything
+ // we had queued and we're still not blocked, let the visitor know it can
+ // write more.
+ return ShouldGeneratePacket(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
+ pending_handshake);
+}
+
void QuicConnection::SetIdleNetworkTimeout(QuicTime::Delta timeout) {
if (timeout < idle_network_timeout_) {
idle_network_timeout_ = timeout;
@@ -1621,8 +1884,8 @@ void QuicConnection::SetOverallConnectionTimeout(QuicTime::Delta timeout) {
bool QuicConnection::CheckForTimeout() {
QuicTime now = clock_->ApproximateNow();
- QuicTime time_of_last_packet = std::max(time_of_last_received_packet_,
- time_of_last_sent_packet_);
+ QuicTime time_of_last_packet = max(time_of_last_received_packet_,
+ time_of_last_sent_new_packet_);
// |delta| can be < 0 as |now| is approximate time but |time_of_last_packet|
// is accurate time. However, this should not change the behavior of
@@ -1643,7 +1906,8 @@ bool QuicConnection::CheckForTimeout() {
QuicTime::Delta timeout = idle_network_timeout_.Subtract(delta);
if (!overall_connection_timeout_.IsInfinite()) {
- QuicTime::Delta connected_time = now.Subtract(creation_time_);
+ QuicTime::Delta connected_time =
+ now.Subtract(stats_.connection_creation_time);
DVLOG(1) << ENDPOINT << "connection time: "
<< connected_time.ToMilliseconds() << " overall timeout: "
<< overall_connection_timeout_.ToMilliseconds();
@@ -1667,24 +1931,49 @@ bool QuicConnection::CheckForTimeout() {
return false;
}
+void QuicConnection::SetPingAlarm() {
+ if (is_server_) {
+ // Only clients send pings.
+ return;
+ }
+ ping_alarm_->Cancel();
+ if (!visitor_->HasOpenDataStreams()) {
+ // Don't send a ping unless there are open streams.
+ return;
+ }
+ QuicTime::Delta ping_timeout = QuicTime::Delta::FromSeconds(kPingTimeoutSecs);
+ ping_alarm_->Set(clock_->ApproximateNow().Add(ping_timeout));
+}
+
QuicConnection::ScopedPacketBundler::ScopedPacketBundler(
QuicConnection* connection,
- bool include_ack)
+ AckBundling send_ack)
: connection_(connection),
- already_in_batch_mode_(connection->packet_generator_.InBatchMode()) {
+ already_in_batch_mode_(connection != NULL &&
+ connection->packet_generator_.InBatchMode()) {
+ if (connection_ == NULL) {
+ return;
+ }
// Move generator into batch mode. If caller wants us to include an ack,
// check the delayed-ack timer to see if there's ack info to be sent.
if (!already_in_batch_mode_) {
DVLOG(1) << "Entering Batch Mode.";
connection_->packet_generator_.StartBatchOperations();
}
- if (include_ack && connection_->ack_alarm_->IsSet()) {
+ // Bundle an ack if the alarm is set or with every second packet if we need to
+ // raise the peer's least unacked.
+ bool ack_pending =
+ connection_->ack_alarm_->IsSet() || connection_->stop_waiting_count_ > 1;
+ if (send_ack == SEND_ACK || (send_ack == BUNDLE_PENDING_ACK && ack_pending)) {
DVLOG(1) << "Bundling ack with outgoing packet.";
connection_->SendAck();
}
}
QuicConnection::ScopedPacketBundler::~ScopedPacketBundler() {
+ if (connection_ == NULL) {
+ return;
+ }
// If we changed the generator's batch state, restore original batch state.
if (!already_in_batch_mode_) {
DVLOG(1) << "Leaving Batch Mode.";
diff --git a/chromium/net/quic/quic_connection.h b/chromium/net/quic/quic_connection.h
index beb8985d4d1..fec6c6882e5 100644
--- a/chromium/net/quic/quic_connection.h
+++ b/chromium/net/quic/quic_connection.h
@@ -40,9 +40,7 @@
#include "net/quic/quic_received_packet_manager.h"
#include "net/quic/quic_sent_entropy_manager.h"
#include "net/quic/quic_sent_packet_manager.h"
-
-NET_EXPORT_PRIVATE extern int FLAGS_fake_packet_loss_percentage;
-NET_EXPORT_PRIVATE extern bool FLAGS_bundle_ack_with_outgoing_packet;
+#include "net/quic/quic_types.h"
namespace net {
@@ -64,11 +62,18 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
public:
virtual ~QuicConnectionVisitorInterface() {}
- // A simple visitor interface for dealing with data frames. The session
- // should determine if all frames will be accepted, and return true if so.
- // If any frames can't be processed or buffered, none of the data should
- // be used, and the callee should return false.
- virtual bool OnStreamFrames(const std::vector<QuicStreamFrame>& frames) = 0;
+ // A simple visitor interface for dealing with data frames.
+ virtual void OnStreamFrames(const std::vector<QuicStreamFrame>& frames) = 0;
+
+ // The session should process all WINDOW_UPDATE frames, adjusting both stream
+ // and connection level flow control windows.
+ virtual void OnWindowUpdateFrames(
+ const std::vector<QuicWindowUpdateFrame>& frames) = 0;
+
+ // BLOCKED frames tell us that the peer believes it is flow control blocked on
+ // a specified stream. If the session at this end disagrees, something has
+ // gone wrong with our flow control accounting.
+ virtual void OnBlockedFrames(const std::vector<QuicBlockedFrame>& frames) = 0;
// Called when the stream is reset by the peer.
virtual void OnRstStream(const QuicRstStreamFrame& frame) = 0;
@@ -78,85 +83,110 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
// Called when the connection is closed either locally by the framer, or
// remotely by the peer.
- virtual void OnConnectionClosed(QuicErrorCode error,
- bool from_peer) = 0;
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) = 0;
+
+ // Called when the connection failed to write because the socket was blocked.
+ virtual void OnWriteBlocked() = 0;
// Called once a specific QUIC version is agreed by both endpoints.
virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) = 0;
- // Indicates a new QuicConfig has been negotiated.
- virtual void OnConfigNegotiated() = 0;
+ // Called when a blocked socket becomes writable.
+ virtual void OnCanWrite() = 0;
- // Called when a blocked socket becomes writable. If all pending bytes for
- // this visitor are consumed by the connection successfully this should
- // return true, otherwise it should return false.
- virtual bool OnCanWrite() = 0;
+ // Called to ask if the visitor wants to schedule write resumption as it both
+ // has pending data to write, and is able to write (e.g. based on flow control
+ // limits).
+ // Writes may be pending because they were write-blocked, congestion-throttled
+ // or yielded to other connections.
+ virtual bool WillingAndAbleToWrite() const = 0;
// Called to ask if any handshake messages are pending in this visitor.
virtual bool HasPendingHandshake() const = 0;
+
+ // Called to ask if any streams are open in this visitor, excluding the
+ // reserved crypto and headers stream.
+ virtual bool HasOpenDataStreams() const = 0;
};
// Interface which gets callbacks from the QuicConnection at interesting
// points. Implementations must not mutate the state of the connection
// as a result of these callbacks.
-class NET_EXPORT_PRIVATE QuicConnectionDebugVisitorInterface
- : public QuicPacketGenerator::DebugDelegateInterface {
+class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor
+ : public QuicPacketGenerator::DebugDelegate,
+ public QuicSentPacketManager::DebugDelegate {
public:
- virtual ~QuicConnectionDebugVisitorInterface() {}
+ virtual ~QuicConnectionDebugVisitor() {}
// Called when a packet has been sent.
virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number,
EncryptionLevel level,
+ TransmissionType transmission_type,
const QuicEncryptedPacket& packet,
- WriteResult result) = 0;
+ WriteResult result) {}
// Called when the contents of a packet have been retransmitted as
// a new packet.
virtual void OnPacketRetransmitted(
QuicPacketSequenceNumber old_sequence_number,
- QuicPacketSequenceNumber new_sequence_number) = 0;
+ QuicPacketSequenceNumber new_sequence_number) {}
// Called when a packet has been received, but before it is
// validated or parsed.
virtual void OnPacketReceived(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
- const QuicEncryptedPacket& packet) = 0;
+ const QuicEncryptedPacket& packet) {}
// Called when the protocol version on the received packet doensn't match
// current protocol version of the connection.
- virtual void OnProtocolVersionMismatch(QuicVersion version) = 0;
+ virtual void OnProtocolVersionMismatch(QuicVersion version) {}
// Called when the complete header of a packet has been parsed.
- virtual void OnPacketHeader(const QuicPacketHeader& header) = 0;
+ virtual void OnPacketHeader(const QuicPacketHeader& header) {}
// Called when a StreamFrame has been parsed.
- virtual void OnStreamFrame(const QuicStreamFrame& frame) = 0;
+ virtual void OnStreamFrame(const QuicStreamFrame& frame) {}
// Called when a AckFrame has been parsed.
- virtual void OnAckFrame(const QuicAckFrame& frame) = 0;
+ virtual void OnAckFrame(const QuicAckFrame& frame) {}
// Called when a CongestionFeedbackFrame has been parsed.
virtual void OnCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& frame) = 0;
+ const QuicCongestionFeedbackFrame& frame) {}
+
+ // Called when a StopWaitingFrame has been parsed.
+ virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {}
+
+ // Called when a Ping has been parsed.
+ virtual void OnPingFrame(const QuicPingFrame& frame) {}
+
+ // Called when a GoAway has been parsed.
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) {}
// Called when a RstStreamFrame has been parsed.
- virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) = 0;
+ virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) {}
// Called when a ConnectionCloseFrame has been parsed.
virtual void OnConnectionCloseFrame(
- const QuicConnectionCloseFrame& frame) = 0;
+ const QuicConnectionCloseFrame& frame) {}
+
+ // Called when a WindowUpdate has been parsed.
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {}
+
+ // Called when a BlockedFrame has been parsed.
+ virtual void OnBlockedFrame(const QuicBlockedFrame& frame) {}
// Called when a public reset packet has been received.
- virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) = 0;
+ virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) {}
// Called when a version negotiation packet has been received.
virtual void OnVersionNegotiationPacket(
- const QuicVersionNegotiationPacket& packet) = 0;
+ const QuicVersionNegotiationPacket& packet) {}
// Called after a packet has been successfully parsed which results
// in the revival of a packet via FEC.
virtual void OnRevivedPacket(const QuicPacketHeader& revived_header,
- base::StringPiece payload) = 0;
+ base::StringPiece payload) {}
};
class NET_EXPORT_PRIVATE QuicConnectionHelperInterface {
@@ -178,17 +208,23 @@ class NET_EXPORT_PRIVATE QuicConnectionHelperInterface {
class NET_EXPORT_PRIVATE QuicConnection
: public QuicFramerVisitorInterface,
public QuicBlockedWriterInterface,
- public QuicPacketGenerator::DelegateInterface,
- public QuicSentPacketManager::HelperInterface {
+ public QuicPacketGenerator::DelegateInterface {
public:
- enum Force {
- NO_FORCE,
- FORCE
+ enum PacketType {
+ NORMAL,
+ QUEUED,
+ CONNECTION_CLOSE
};
- // Constructs a new QuicConnection for the specified |guid| and |address|.
+ enum AckBundling {
+ NO_ACK = 0,
+ SEND_ACK = 1,
+ BUNDLE_PENDING_ACK = 2,
+ };
+
+ // Constructs a new QuicConnection for |connection_id| and |address|.
// |helper| and |writer| must outlive this connection.
- QuicConnection(QuicGuid guid,
+ QuicConnection(QuicConnectionId connection_id,
IPEndPoint address,
QuicConnectionHelperInterface* helper,
QuicPacketWriter* writer,
@@ -203,7 +239,10 @@ class NET_EXPORT_PRIVATE QuicConnection
// Returns a pair with the number of bytes consumed from data, and a boolean
// indicating if the fin bit was consumed. This does not indicate the data
// has been sent on the wire: it may have been turned into a packet and queued
- // if the socket was unexpectedly blocked.
+ // if the socket was unexpectedly blocked. |fec_protection| indicates if
+ // data is to be FEC protected. Note that data that is sent immediately
+ // following MUST_FEC_PROTECT data may get protected by falling within the
+ // same FEC group.
// If |delegate| is provided, then it will be informed once ACKs have been
// received for all the packets written in this call.
// The |delegate| is not owned by the QuicConnection and must outlive it.
@@ -211,11 +250,20 @@ class NET_EXPORT_PRIVATE QuicConnection
const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier::DelegateInterface* delegate);
- // Send a stream reset frame to the peer.
+ // Send a RST_STREAM frame to the peer.
virtual void SendRstStream(QuicStreamId id,
- QuicRstStreamErrorCode error);
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written);
+
+ // Send a BLOCKED frame to the peer.
+ virtual void SendBlocked(QuicStreamId id);
+
+ // Send a WINDOW_UPDATE frame to the peer.
+ virtual void SendWindowUpdate(QuicStreamId id,
+ QuicStreamOffset byte_offset);
// Sends the connection close packet without affecting the state of the
// connection. This should only be called if the session is actively being
@@ -246,15 +294,14 @@ class NET_EXPORT_PRIVATE QuicConnection
// QuicBlockedWriterInterface
// Called when the underlying connection becomes writable to allow queued
- // writes to happen. Returns false if the socket has become blocked.
- virtual bool OnCanWrite() OVERRIDE;
+ // writes to happen.
+ virtual void OnCanWrite() OVERRIDE;
// Called when a packet has been finally sent to the network.
bool OnPacketSent(WriteResult result);
- // If the socket is not blocked, this allows queued writes to happen. Returns
- // false if the socket has become blocked.
- bool WriteIfNotBlocked();
+ // If the socket is not blocked, writes queued packets.
+ void WriteIfNotBlocked();
// Do any work which logically would be done in OnPacket but can not be
// safely done until the packet is validated. Returns true if the packet
@@ -278,17 +325,24 @@ class NET_EXPORT_PRIVATE QuicConnection
virtual void OnVersionNegotiationPacket(
const QuicVersionNegotiationPacket& packet) OVERRIDE;
virtual void OnRevivedPacket() OVERRIDE;
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE;
virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE;
virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE;
virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
virtual bool OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE;
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE;
virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
virtual bool OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) OVERRIDE;
virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE;
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE;
virtual void OnFecData(const QuicFecData& fec) OVERRIDE;
virtual void OnPacketComplete() OVERRIDE;
@@ -298,26 +352,25 @@ class NET_EXPORT_PRIVATE QuicConnection
IsHandshake handshake) OVERRIDE;
virtual QuicAckFrame* CreateAckFrame() OVERRIDE;
virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() OVERRIDE;
+ virtual QuicStopWaitingFrame* CreateStopWaitingFrame() OVERRIDE;
virtual bool OnSerializedPacket(const SerializedPacket& packet) OVERRIDE;
- // QuicSentPacketManager::HelperInterface
- virtual QuicPacketSequenceNumber GetNextPacketSequenceNumber() OVERRIDE;
-
// Accessors
void set_visitor(QuicConnectionVisitorInterface* visitor) {
visitor_ = visitor;
}
- void set_debug_visitor(QuicConnectionDebugVisitorInterface* debug_visitor) {
+ void set_debug_visitor(QuicConnectionDebugVisitor* debug_visitor) {
debug_visitor_ = debug_visitor;
packet_generator_.set_debug_delegate(debug_visitor);
+ sent_packet_manager_.set_debug_delegate(debug_visitor);
}
const IPEndPoint& self_address() const { return self_address_; }
const IPEndPoint& peer_address() const { return peer_address_; }
- QuicGuid guid() const { return guid_; }
+ QuicConnectionId connection_id() const { return connection_id_; }
const QuicClock* clock() const { return clock_; }
QuicRandom* random_generator() const { return random_generator_; }
-
- QuicPacketCreator::Options* options() { return packet_creator_.options(); }
+ size_t max_packet_length() const;
+ void set_max_packet_length(size_t length);
bool connected() const { return connected_; }
@@ -336,9 +389,10 @@ class NET_EXPORT_PRIVATE QuicConnection
return connection_close_packet_.release();
}
- // Flush any queued frames immediately. Preserves the batch write mode and
- // does nothing if there are no pending frames.
- void Flush();
+ // Returns true if the underlying UDP socket is writable, there is
+ // no queued data and the connection is not congestion-control
+ // blocked.
+ bool CanWriteStreamData();
// Returns true if the connection has queued packets or frames.
bool HasQueuedData() const;
@@ -356,6 +410,9 @@ class NET_EXPORT_PRIVATE QuicConnection
// true. Otherwise, it will return false and will reset the timeout alarm.
bool CheckForTimeout();
+ // Sends a ping, and resets the ping alarm.
+ void SendPing();
+
// Sets up a packet with an QuicAckFrame and sends it out.
void SendAck();
@@ -370,6 +427,10 @@ class NET_EXPORT_PRIVATE QuicConnection
// initially encrypted packets when the initial encrypter changes.
void RetransmitUnackedPackets(RetransmissionType retransmission_type);
+ // Calls |sent_packet_manager_|'s NeuterUnencryptedPackets. Used when the
+ // connection becomes forward secure and hasn't received acks for all packets.
+ void NeuterUnencryptedPackets();
+
// Changes the encrypter used for level |level| to |encrypter|. The function
// takes ownership of |encrypter|.
void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter);
@@ -383,15 +444,17 @@ class NET_EXPORT_PRIVATE QuicConnection
// and takes ownership. If an alternative decrypter is in place then the
// function DCHECKs. This is intended for cases where one knows that future
// packets will be using the new decrypter and the previous decrypter is now
- // obsolete.
- void SetDecrypter(QuicDecrypter* decrypter);
+ // obsolete. |level| indicates the encryption level of the new decrypter.
+ void SetDecrypter(QuicDecrypter* decrypter, EncryptionLevel level);
// SetAlternativeDecrypter sets a decrypter that may be used to decrypt
- // future packets and takes ownership of it. If |latch_once_used| is true,
- // then the first time that the decrypter is successful it will replace the
- // primary decrypter. Otherwise both decrypters will remain active and the
- // primary decrypter will be the one last used.
+ // future packets and takes ownership of it. |level| indicates the encryption
+ // level of the decrypter. If |latch_once_used| is true, then the first time
+ // that the decrypter is successful it will replace the primary decrypter.
+ // Otherwise both decrypters will remain active and the primary decrypter
+ // will be the one last used.
void SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
bool latch_once_used);
const QuicDecrypter* decrypter() const;
@@ -404,16 +467,34 @@ class NET_EXPORT_PRIVATE QuicConnection
return sent_packet_manager_;
}
- bool CanWrite(TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake);
+ bool CanWrite(HasRetransmittableData retransmittable);
+
+ // Stores current batch state for connection, puts the connection
+ // into batch mode, and destruction restores the stored batch state.
+ // While the bundler is in scope, any generated frames are bundled
+ // as densely as possible into packets. In addition, this bundler
+ // can be configured to ensure that an ACK frame is included in the
+ // first packet created, if there's new ack information to be sent.
+ class ScopedPacketBundler {
+ public:
+ // In addition to all outgoing frames being bundled when the
+ // bundler is in scope, setting |include_ack| to true ensures that
+ // an ACK frame is opportunistically bundled with the first
+ // outgoing packet.
+ ScopedPacketBundler(QuicConnection* connection, AckBundling send_ack);
+ ~ScopedPacketBundler();
+
+ private:
+ QuicConnection* connection_;
+ bool already_in_batch_mode_;
+ };
protected:
// Send a packet to the peer using encryption |level|. If |sequence_number|
// is present in the |retransmission_map_|, then contents of this packet will
// be retransmitted with a new sequence number if it's not acked by the peer.
- // Deletes |packet| via WritePacket call or transfers ownership to
- // QueuedPacket, ultimately deleted via WritePacket. Updates the
+ // Deletes |packet| if WritePacket call succeeds, or transfers ownership to
+ // QueuedPacket, ultimately deleted in WriteQueuedPackets. Updates the
// entropy map corresponding to |sequence_number| using |entropy_hash|.
// |transmission_type| and |retransmittable| are supplied to the congestion
// manager, and when |forced| is true, it bypasses the congestion manager.
@@ -422,25 +503,6 @@ class NET_EXPORT_PRIVATE QuicConnection
const SerializedPacket& packet,
TransmissionType transmission_type);
- // Writes the given packet to socket, encrypted with |level|, with the help
- // of helper. Returns true on successful write, false otherwise. However,
- // behavior is undefined if connection is not established or broken. In any
- // circumstances, a return value of true implies that |packet| has been
- // deleted and should not be accessed. If |sequence_number| is present in
- // |retransmission_map_| it also sets up retransmission of the given packet
- // in case of successful write. If |force| is FORCE, then the packet will be
- // sent immediately and the send scheduler will not be consulted.
- bool WritePacket(EncryptionLevel level,
- QuicPacketSequenceNumber sequence_number,
- QuicPacket* packet,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake,
- Force force);
-
- // Make sure an ack we got from our peer is sane.
- bool ValidateAckFrame(const QuicAckFrame& incoming_ack);
-
QuicConnectionHelperInterface* helper() { return helper_; }
// Selects and updates the version of the protocol being used by selecting a
@@ -448,50 +510,17 @@ class NET_EXPORT_PRIVATE QuicConnection
// such a version exists, false otherwise.
bool SelectMutualVersion(const QuicVersionVector& available_versions);
- QuicFramer framer_;
+ QuicPacketWriter* writer() { return writer_; }
private:
- // Stores current batch state for connection, puts the connection
- // into batch mode, and destruction restores the stored batch state.
- // While the bundler is in scope, any generated frames are bundled
- // as densely as possible into packets. In addition, this bundler
- // can be configured to ensure that an ACK frame is included in the
- // first packet created, if there's new ack information to be sent.
- class ScopedPacketBundler {
- public:
- // In addition to all outgoing frames being bundled when the
- // bundler is in scope, setting |include_ack| to true ensures that
- // an ACK frame is opportunistically bundled with the first
- // outgoing packet.
- ScopedPacketBundler(QuicConnection* connection, bool include_ack);
- ~ScopedPacketBundler();
-
- private:
- QuicConnection* connection_;
- bool already_in_batch_mode_;
- };
-
- friend class ScopedPacketBundler;
friend class test::QuicConnectionPeer;
// Packets which have not been written to the wire.
// Owns the QuicPacket* packet.
struct QueuedPacket {
- QueuedPacket(QuicPacketSequenceNumber sequence_number,
- QuicPacket* packet,
+ QueuedPacket(SerializedPacket packet,
EncryptionLevel level,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake,
- Force forced)
- : sequence_number(sequence_number),
- packet(packet),
- encryption_level(level),
- transmission_type(transmission_type),
- retransmittable(retransmittable),
- handshake(handshake),
- forced(forced) {
- }
+ TransmissionType transmission_type);
QuicPacketSequenceNumber sequence_number;
QuicPacket* packet;
@@ -499,109 +528,37 @@ class NET_EXPORT_PRIVATE QuicConnection
TransmissionType transmission_type;
HasRetransmittableData retransmittable;
IsHandshake handshake;
- Force forced;
+ PacketType type;
+ QuicByteCount length;
};
- struct RetransmissionInfo {
- RetransmissionInfo(QuicPacketSequenceNumber sequence_number,
- QuicSequenceNumberLength sequence_number_length,
- QuicTime sent_time)
- : sequence_number(sequence_number),
- sequence_number_length(sequence_number_length),
- sent_time(sent_time),
- number_nacks(0),
- number_retransmissions(0) {
- }
-
- QuicPacketSequenceNumber sequence_number;
- QuicSequenceNumberLength sequence_number_length;
- QuicTime sent_time;
- size_t number_nacks;
- size_t number_retransmissions;
- };
-
- struct RetransmissionTime {
- RetransmissionTime(QuicPacketSequenceNumber sequence_number,
- const QuicTime& scheduled_time,
- bool for_fec)
- : sequence_number(sequence_number),
- scheduled_time(scheduled_time),
- for_fec(for_fec) { }
-
- QuicPacketSequenceNumber sequence_number;
- QuicTime scheduled_time;
- bool for_fec;
- };
+ typedef std::list<QueuedPacket> QueuedPacketList;
+ typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap;
- struct PendingWrite {
- PendingWrite(QuicPacketSequenceNumber sequence_number,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- EncryptionLevel level,
- bool is_fec_packet,
- size_t length)
- : sequence_number(sequence_number),
- transmission_type(transmission_type),
- retransmittable(retransmittable),
- level(level),
- is_fec_packet(is_fec_packet),
- length(length) { }
+ // Writes the given packet to socket, encrypted with packet's
+ // encryption_level. Returns true on successful write. Behavior is undefined
+ // if connection is not established or broken. A return value of true means
+ // the packet was transmitted and may be destroyed. If the packet is
+ // retransmittable, WritePacket sets up retransmission for a successful write.
+ // If packet is FORCE, then it will be sent immediately and the send scheduler
+ // will not be consulted.
+ bool WritePacket(QueuedPacket packet);
- QuicPacketSequenceNumber sequence_number;
- TransmissionType transmission_type;
- HasRetransmittableData retransmittable;
- EncryptionLevel level;
- bool is_fec_packet;
- size_t length;
- };
-
- class RetransmissionTimeComparator {
- public:
- bool operator()(const RetransmissionTime& lhs,
- const RetransmissionTime& rhs) const {
- DCHECK(lhs.scheduled_time.IsInitialized() &&
- rhs.scheduled_time.IsInitialized());
- return lhs.scheduled_time > rhs.scheduled_time;
- }
- };
+ // Make sure an ack we got from our peer is sane.
+ bool ValidateAckFrame(const QuicAckFrame& incoming_ack);
- typedef std::list<QueuedPacket> QueuedPacketList;
- typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap;
- typedef std::priority_queue<RetransmissionTime,
- std::vector<RetransmissionTime>,
- RetransmissionTimeComparator>
- RetransmissionTimeouts;
+ // Make sure a stop waiting we got from our peer is sane.
+ bool ValidateStopWaitingFrame(const QuicStopWaitingFrame& stop_waiting);
// Sends a version negotiation packet to the peer.
void SendVersionNegotiationPacket();
- void SetupRetransmissionAlarm(QuicPacketSequenceNumber sequence_number);
- bool IsRetransmission(QuicPacketSequenceNumber sequence_number);
-
- void SetupAbandonFecTimer(QuicPacketSequenceNumber sequence_number);
-
// Clears any accumulated frames from the last received packet.
void ClearLastFrames();
- // Called from OnCanWrite and WriteIfNotBlocked to write queued packets.
- // Returns false if the socket has become blocked.
- bool DoWrite();
-
- // Calculates the smallest sequence number length that can also represent four
- // times the maximum of the congestion window and the difference between the
- // least_packet_awaited_by_peer_ and |sequence_number|.
- QuicSequenceNumberLength CalculateSequenceNumberLength(
- QuicPacketSequenceNumber sequence_number);
-
- // Drop packet corresponding to |sequence_number| by deleting entries from
- // |unacked_packets_| and |retransmission_map_|, if present. We need to drop
- // all packets with encryption level NONE after the default level has been set
- // to FORWARD_SECURE.
- void DropPacket(QuicPacketSequenceNumber sequence_number);
-
// Writes as many queued packets as possible. The connection must not be
// blocked when this is called.
- bool WriteQueuedPackets();
+ void WriteQueuedPackets();
// Writes as many pending retransmissions as possible.
void WritePendingRetransmissions();
@@ -624,16 +581,29 @@ class NET_EXPORT_PRIVATE QuicConnection
void ProcessAckFrame(const QuicAckFrame& incoming_ack);
- // Update the |sent_info| for an outgoing ack.
- void UpdateSentPacketInfo(SentPacketInfo* sent_info);
+ void ProcessStopWaitingFrame(const QuicStopWaitingFrame& stop_waiting);
+
+ // Update |stop_waiting| for an outgoing ack.
+ void UpdateStopWaiting(QuicStopWaitingFrame* stop_waiting);
+
+ // Queues an ack or sets the ack alarm when an incoming packet arrives that
+ // should be acked.
+ void MaybeQueueAck();
// Checks if the last packet should instigate an ack.
- bool ShouldLastPacketInstigateAck();
+ bool ShouldLastPacketInstigateAck() const;
+
+ // Checks if the peer is waiting for packets that have been given up on, and
+ // therefore an ack frame should be sent with a larger least_unacked.
+ void UpdateStopWaitingCount();
// Sends any packets which are a response to the last packet, including both
// acks and pending writes if an ack opened the congestion window.
- void MaybeSendInResponseToPacket(bool send_ack_immediately,
- bool last_packet_should_instigate_ack);
+ void MaybeSendInResponseToPacket();
+
+ // Gets the least unacked sequence number, which is the next sequence number
+ // to be sent if there are no outstanding packets.
+ QuicPacketSequenceNumber GetLeastUnacked() const;
// Get the FEC group associate with the last processed packet or NULL, if the
// group has already been deleted.
@@ -642,26 +612,41 @@ class NET_EXPORT_PRIVATE QuicConnection
// Closes any FEC groups protecting packets before |sequence_number|.
void CloseFecGroupsBefore(QuicPacketSequenceNumber sequence_number);
+ // Sets the ping alarm to the appropriate value, if any.
+ void SetPingAlarm();
+
+ // On arrival of a new packet, checks to see if the socket addresses have
+ // changed since the last packet we saw on this connection.
+ void CheckForAddressMigration(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address);
+
+ QuicFramer framer_;
QuicConnectionHelperInterface* helper_; // Not owned.
QuicPacketWriter* writer_; // Not owned.
EncryptionLevel encryption_level_;
const QuicClock* clock_;
QuicRandom* random_generator_;
- const QuicGuid guid_;
+ const QuicConnectionId connection_id_;
// Address on the last successfully processed packet received from the
// client.
IPEndPoint self_address_;
IPEndPoint peer_address_;
+ // Used to store latest peer port to possibly migrate to later.
+ int migrating_peer_port_;
bool last_packet_revived_; // True if the last packet was revived from FEC.
size_t last_size_; // Size of the last received packet.
+ EncryptionLevel last_decrypted_packet_level_;
QuicPacketHeader last_header_;
std::vector<QuicStreamFrame> last_stream_frames_;
std::vector<QuicAckFrame> last_ack_frames_;
std::vector<QuicCongestionFeedbackFrame> last_congestion_frames_;
+ std::vector<QuicStopWaitingFrame> last_stop_waiting_frames_;
std::vector<QuicRstStreamFrame> last_rst_frames_;
std::vector<QuicGoAwayFrame> last_goaway_frames_;
+ std::vector<QuicWindowUpdateFrame> last_window_update_frames_;
+ std::vector<QuicBlockedFrame> last_blocked_frames_;
std::vector<QuicConnectionCloseFrame> last_close_frames_;
QuicCongestionFeedbackFrame outgoing_congestion_feedback_;
@@ -670,6 +655,9 @@ class NET_EXPORT_PRIVATE QuicConnection
// Largest sequence sent by the peer which had an ack frame (latest ack info).
QuicPacketSequenceNumber largest_seen_packet_with_ack_;
+ // Largest sequence number sent by the peer which had a stop waiting frame.
+ QuicPacketSequenceNumber largest_seen_packet_with_stop_waiting_;
+
// Collection of packets which were received before encryption was
// established, but which could not be decrypted. We buffer these on
// the assumption that they could not be processed because they were
@@ -686,19 +674,22 @@ class NET_EXPORT_PRIVATE QuicConnection
QueuedPacketList queued_packets_;
// Contains information about the current write in progress, if any.
- scoped_ptr<PendingWrite> pending_write_;
+ scoped_ptr<QueuedPacket> pending_write_;
// Contains the connection close packet if the connection has been closed.
scoped_ptr<QuicEncryptedPacket> connection_close_packet_;
- // True when the socket becomes unwritable.
- bool write_blocked_;
-
FecGroupMap group_map_;
QuicReceivedPacketManager received_packet_manager_;
QuicSentEntropyManager sent_entropy_manager_;
+ // Indicates whether an ack should be sent the next time we try to write.
+ bool ack_queued_;
+ // Indicates how many consecutive times an ack has arrived which indicates
+ // the peer needs to stop waiting for some packets.
+ int stop_waiting_count_;
+
// An alarm that fires when an ACK should be sent to the peer.
scoped_ptr<QuicAlarm> ack_alarm_;
// An alarm that fires when a packet needs to be retransmitted.
@@ -711,18 +702,17 @@ class NET_EXPORT_PRIVATE QuicConnection
scoped_ptr<QuicAlarm> resume_writes_alarm_;
// An alarm that fires when the connection may have timed out.
scoped_ptr<QuicAlarm> timeout_alarm_;
+ // An alarm that fires when a ping should be sent.
+ scoped_ptr<QuicAlarm> ping_alarm_;
QuicConnectionVisitorInterface* visitor_;
- QuicConnectionDebugVisitorInterface* debug_visitor_;
- QuicPacketCreator packet_creator_;
+ QuicConnectionDebugVisitor* debug_visitor_;
QuicPacketGenerator packet_generator_;
// Network idle time before we kill of this connection.
QuicTime::Delta idle_network_timeout_;
// Overall connection timeout.
QuicTime::Delta overall_connection_timeout_;
- // Connection creation time.
- QuicTime creation_time_;
// Statistics for this session.
QuicConnectionStats stats_;
@@ -731,13 +721,13 @@ class NET_EXPORT_PRIVATE QuicConnection
// This is used for timeouts, and does not indicate the packet was processed.
QuicTime time_of_last_received_packet_;
- // The time that we last sent a packet for this connection.
- QuicTime time_of_last_sent_packet_;
+ // The last time a new (non-retransmitted) packet was sent for this
+ // connection.
+ QuicTime time_of_last_sent_new_packet_;
- // Sequence number of the last packet guaranteed to be sent in packet sequence
- // number order. Not set when packets are queued, since that may cause
- // re-ordering.
- QuicPacketSequenceNumber sequence_number_of_last_inorder_packet_;
+ // Sequence number of the last sent packet. Packets are guaranteed to be sent
+ // in sequence number order.
+ QuicPacketSequenceNumber sequence_number_of_last_sent_packet_;
// Sent packet manager which tracks the status of packets sent by this
// connection and contains the send and receive algorithms to determine when
@@ -754,14 +744,21 @@ class NET_EXPORT_PRIVATE QuicConnection
// close.
bool connected_;
- // Set to true if the udp packet headers have a new self or peer address.
- // This is checked later on validating a data or version negotiation packet.
- bool address_migrating_;
+ // Set to true if the UDP packet headers have a new IP address for the peer.
+ // If true, do not perform connection migration.
+ bool peer_ip_changed_;
+
+ // Set to true if the UDP packet headers have a new port for the peer.
+ // If true, and the IP has not changed, then we can migrate the connection.
+ bool peer_port_changed_;
+
+ // Set to true if the UDP packet headers are addressed to a different IP.
+ // We do not support connection migration when the self IP changed.
+ bool self_ip_changed_;
- // An AckNotifier can register to be informed when ACKs have been received for
- // all packets that a given block of data was sent in. The AckNotifierManager
- // maintains the currently active notifiers.
- AckNotifierManager ack_notifier_manager_;
+ // Set to true if the UDP packet headers are addressed to a different port.
+ // We do not support connection migration when the self port changed.
+ bool self_port_changed_;
// If non-empty this contains the set of versions received in a
// version negotiation packet.
diff --git a/chromium/net/quic/quic_connection_helper.h b/chromium/net/quic/quic_connection_helper.h
index 7098039c9eb..fa866f296af 100644
--- a/chromium/net/quic/quic_connection_helper.h
+++ b/chromium/net/quic/quic_connection_helper.h
@@ -12,6 +12,7 @@
#include <set>
+#include "base/basictypes.h"
#include "base/memory/weak_ptr.h"
#include "net/base/ip_endpoint.h"
#include "net/quic/quic_protocol.h"
@@ -33,7 +34,6 @@ class NET_EXPORT_PRIVATE QuicConnectionHelper
QuicConnectionHelper(base::TaskRunner* task_runner,
const QuicClock* clock,
QuicRandom* random_generator);
-
virtual ~QuicConnectionHelper();
// QuicConnectionHelperInterface
diff --git a/chromium/net/quic/quic_connection_logger.cc b/chromium/net/quic/quic_connection_logger.cc
index 174d99a58c6..6ac4f5563d9 100644
--- a/chromium/net/quic/quic_connection_logger.cc
+++ b/chromium/net/quic/quic_connection_logger.cc
@@ -4,6 +4,9 @@
#include "net/quic/quic_connection_logger.h"
+#include <algorithm>
+#include <string>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/metrics/histogram.h"
@@ -11,14 +14,24 @@
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "net/base/net_log.h"
-#include "net/quic/crypto/crypto_handshake.h"
+#include "net/base/net_util.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_address_mismatch.h"
+#include "net/quic/quic_socket_address_coder.h"
+using base::StringPiece;
using std::string;
namespace net {
namespace {
+// We have ranges-of-buckets in the cumulative histogram (covering 21 packet
+// sequences) of length 2, 3, 4, ... 22.
+// Hence the largest sample is bounded by the sum of those numbers.
+const int kBoundingSampleInCumulativeHistogram = ((2 + 22) * 21) / 2;
+
base::Value* NetLogQuicPacketCallback(const IPEndPoint* self_address,
const IPEndPoint* peer_address,
size_t packet_size,
@@ -33,11 +46,13 @@ base::Value* NetLogQuicPacketCallback(const IPEndPoint* self_address,
base::Value* NetLogQuicPacketSentCallback(
QuicPacketSequenceNumber sequence_number,
EncryptionLevel level,
+ TransmissionType transmission_type,
size_t packet_size,
WriteResult result,
NetLog::LogLevel /* log_level */) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetInteger("encryption_level", level);
+ dict->SetInteger("transmission_type", transmission_type);
dict->SetString("packet_sequence_number",
base::Uint64ToString(sequence_number));
dict->SetInteger("size", packet_size);
@@ -51,19 +66,19 @@ base::Value* NetLogQuicPacketRetransmittedCallback(
QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number,
NetLog::LogLevel /* log_level */) {
- base::DictionaryValue* dict = new base::DictionaryValue();
- dict->SetString("old_packet_sequence_number",
- base::Uint64ToString(old_sequence_number));
- dict->SetString("new_packet_sequence_number",
- base::Uint64ToString(new_sequence_number));
- return dict;
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("old_packet_sequence_number",
+ base::Uint64ToString(old_sequence_number));
+ dict->SetString("new_packet_sequence_number",
+ base::Uint64ToString(new_sequence_number));
+ return dict;
}
base::Value* NetLogQuicPacketHeaderCallback(const QuicPacketHeader* header,
NetLog::LogLevel /* log_level */) {
base::DictionaryValue* dict = new base::DictionaryValue();
- dict->SetString("guid",
- base::Uint64ToString(header->public_header.guid));
+ dict->SetString("connection_id",
+ base::Uint64ToString(header->public_header.connection_id));
dict->SetInteger("reset_flag", header->public_header.reset_flag);
dict->SetInteger("version_flag", header->public_header.version_flag);
dict->SetString("packet_sequence_number",
@@ -87,15 +102,12 @@ base::Value* NetLogQuicStreamFrameCallback(const QuicStreamFrame* frame,
base::Value* NetLogQuicAckFrameCallback(const QuicAckFrame* frame,
NetLog::LogLevel /* log_level */) {
base::DictionaryValue* dict = new base::DictionaryValue();
- base::DictionaryValue* sent_info = new base::DictionaryValue();
- dict->Set("sent_info", sent_info);
- sent_info->SetString("least_unacked",
- base::Uint64ToString(frame->sent_info.least_unacked));
base::DictionaryValue* received_info = new base::DictionaryValue();
dict->Set("received_info", received_info);
received_info->SetString(
"largest_observed",
base::Uint64ToString(frame->received_info.largest_observed));
+ received_info->SetBoolean("truncated", frame->received_info.is_truncated);
base::ListValue* missing = new base::ListValue();
received_info->Set("missing_packets", missing);
const SequenceNumberSet& missing_packets =
@@ -114,14 +126,12 @@ base::Value* NetLogQuicCongestionFeedbackFrameCallback(
switch (frame->type) {
case kInterArrival: {
dict->SetString("type", "InterArrival");
- dict->SetInteger("accumulated_number_of_lost_packets",
- frame->inter_arrival.accumulated_number_of_lost_packets);
base::ListValue* received = new base::ListValue();
dict->Set("received_packets", received);
for (TimeMap::const_iterator it =
frame->inter_arrival.received_packet_times.begin();
it != frame->inter_arrival.received_packet_times.end(); ++it) {
- std::string value = base::Uint64ToString(it->first) + "@" +
+ string value = base::Uint64ToString(it->first) + "@" +
base::Uint64ToString(it->second.ToDebuggingValue());
received->AppendString(value);
}
@@ -134,10 +144,12 @@ base::Value* NetLogQuicCongestionFeedbackFrameCallback(
break;
case kTCP:
dict->SetString("type", "TCP");
- dict->SetInteger("accumulated_number_of_lost_packets",
- frame->tcp.accumulated_number_of_lost_packets);
dict->SetInteger("receive_window", frame->tcp.receive_window);
break;
+ case kTCPBBR:
+ dict->SetString("type", "TCPBBR");
+ // TODO(rtenneti): Add support for BBR.
+ break;
}
return dict;
@@ -162,6 +174,44 @@ base::Value* NetLogQuicConnectionCloseFrameCallback(
return dict;
}
+base::Value* NetLogQuicWindowUpdateFrameCallback(
+ const QuicWindowUpdateFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("stream_id", frame->stream_id);
+ dict->SetString("byte_offset", base::Uint64ToString(frame->byte_offset));
+ return dict;
+}
+
+base::Value* NetLogQuicBlockedFrameCallback(
+ const QuicBlockedFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("stream_id", frame->stream_id);
+ return dict;
+}
+
+base::Value* NetLogQuicGoAwayFrameCallback(
+ const QuicGoAwayFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("quic_error", frame->error_code);
+ dict->SetInteger("last_good_stream_id", frame->last_good_stream_id);
+ dict->SetString("reason_phrase", frame->reason_phrase);
+ return dict;
+}
+
+base::Value* NetLogQuicStopWaitingFrameCallback(
+ const QuicStopWaitingFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ base::DictionaryValue* sent_info = new base::DictionaryValue();
+ dict->Set("sent_info", sent_info);
+ sent_info->SetString("least_unacked",
+ base::Uint64ToString(frame->least_unacked));
+ return dict;
+}
+
base::Value* NetLogQuicVersionNegotiationPacketCallback(
const QuicVersionNegotiationPacket* packet,
NetLog::LogLevel /* log_level */) {
@@ -198,19 +248,111 @@ void UpdatePacketGapSentHistogram(size_t num_consecutive_missing_packets) {
num_consecutive_missing_packets);
}
+void UpdatePublicResetAddressMismatchHistogram(
+ const IPEndPoint& server_hello_address,
+ const IPEndPoint& public_reset_address) {
+ int sample = GetAddressMismatch(server_hello_address, public_reset_address);
+ // We are seemingly talking to an older server that does not support the
+ // feature, so we can't report the results in the histogram.
+ if (sample < 0) {
+ return;
+ }
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.PublicResetAddressMismatch2",
+ sample, QUIC_ADDRESS_MISMATCH_MAX);
+}
+
+const char* GetConnectionDescriptionString() {
+ NetworkChangeNotifier::ConnectionType type =
+ NetworkChangeNotifier::GetConnectionType();
+ const char* description = NetworkChangeNotifier::ConnectionTypeToString(type);
+ // Most platforms don't distingish Wifi vs Etherenet, and call everything
+ // CONNECTION_UNKNOWN :-(. We'll tease out some details when we are on WiFi,
+ // and hopefully leave only ethernet (with no WiFi available) in the
+ // CONNECTION_UNKNOWN category. This *might* err if there is both ethernet,
+ // as well as WiFi, where WiFi was not being used that much.
+ // This function only seems usefully defined on Windows currently.
+ if (type == NetworkChangeNotifier::CONNECTION_UNKNOWN ||
+ type == NetworkChangeNotifier::CONNECTION_WIFI) {
+ WifiPHYLayerProtocol wifi_type = GetWifiPHYLayerProtocol();
+ switch (wifi_type) {
+ case WIFI_PHY_LAYER_PROTOCOL_NONE:
+ // No wifi support or no associated AP.
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_ANCIENT:
+ // An obsolete modes introduced by the original 802.11, e.g. IR, FHSS.
+ description = "CONNECTION_WIFI_ANCIENT";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_A:
+ // 802.11a, OFDM-based rates.
+ description = "CONNECTION_WIFI_802.11a";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_B:
+ // 802.11b, DSSS or HR DSSS.
+ description = "CONNECTION_WIFI_802.11b";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_G:
+ // 802.11g, same rates as 802.11a but compatible with 802.11b.
+ description = "CONNECTION_WIFI_802.11g";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_N:
+ // 802.11n, HT rates.
+ description = "CONNECTION_WIFI_802.11n";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_UNKNOWN:
+ // Unclassified mode or failure to identify.
+ break;
+ }
+ }
+ return description;
+}
+
+// If |address| is an IPv4-mapped IPv6 address, returns ADDRESS_FAMILY_IPV4
+// instead of ADDRESS_FAMILY_IPV6. Othewise, behaves like GetAddressFamily().
+AddressFamily GetRealAddressFamily(const IPAddressNumber& address) {
+ return IsIPv4Mapped(address) ? ADDRESS_FAMILY_IPV4 :
+ GetAddressFamily(address);
+}
+
} // namespace
QuicConnectionLogger::QuicConnectionLogger(const BoundNetLog& net_log)
: net_log_(net_log),
last_received_packet_sequence_number_(0),
+ last_received_packet_size_(0),
largest_received_packet_sequence_number_(0),
largest_received_missing_packet_sequence_number_(0),
- out_of_order_recieved_packet_count_(0) {
+ num_out_of_order_received_packets_(0),
+ num_packets_received_(0),
+ num_truncated_acks_sent_(0),
+ num_truncated_acks_received_(0),
+ num_frames_received_(0),
+ num_duplicate_frames_received_(0),
+ connection_description_(GetConnectionDescriptionString()) {
}
QuicConnectionLogger::~QuicConnectionLogger() {
UMA_HISTOGRAM_COUNTS("Net.QuicSession.OutOfOrderPacketsReceived",
- out_of_order_recieved_packet_count_);
+ num_out_of_order_received_packets_);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.TruncatedAcksSent",
+ num_truncated_acks_sent_);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.TruncatedAcksReceived",
+ num_truncated_acks_received_);
+ if (num_frames_received_ > 0) {
+ int duplicate_stream_frame_per_thousand =
+ num_duplicate_frames_received_ * 1000 / num_frames_received_;
+ if (num_packets_received_ < 100) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.QuicSession.StreamFrameDuplicatedShortConnection",
+ duplicate_stream_frame_per_thousand, 1, 1000, 75);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.QuicSession.StreamFrameDuplicatedLongConnection",
+ duplicate_stream_frame_per_thousand, 1, 1000, 75);
+
+ }
+ }
+
+ RecordLossHistograms();
}
void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) {
@@ -226,6 +368,8 @@ void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) {
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_ACK_FRAME_SENT,
base::Bind(&NetLogQuicAckFrameCallback, frame.ack_frame));
+ if (frame.ack_frame->received_info.is_truncated)
+ ++num_truncated_acks_sent_;
break;
case CONGESTION_FEEDBACK_FRAME:
net_log_.AddEvent(
@@ -248,6 +392,32 @@ void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) {
frame.connection_close_frame));
break;
case GOAWAY_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_GOAWAY_FRAME_SENT,
+ base::Bind(&NetLogQuicGoAwayFrameCallback,
+ frame.goaway_frame));
+ break;
+ case WINDOW_UPDATE_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_WINDOW_UPDATE_FRAME_SENT,
+ base::Bind(&NetLogQuicWindowUpdateFrameCallback,
+ frame.window_update_frame));
+ break;
+ case BLOCKED_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_BLOCKED_FRAME_SENT,
+ base::Bind(&NetLogQuicBlockedFrameCallback,
+ frame.blocked_frame));
+ break;
+ case STOP_WAITING_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_STOP_WAITING_FRAME_SENT,
+ base::Bind(&NetLogQuicStopWaitingFrameCallback,
+ frame.stop_waiting_frame));
+ break;
+ case PING_FRAME:
+ // PingFrame has no contents to log, so just record that it was sent.
+ net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PING_FRAME_SENT);
break;
default:
DCHECK(false) << "Illegal frame type: " << frame.type;
@@ -257,12 +427,13 @@ void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) {
void QuicConnectionLogger::OnPacketSent(
QuicPacketSequenceNumber sequence_number,
EncryptionLevel level,
+ TransmissionType transmission_type,
const QuicEncryptedPacket& packet,
WriteResult result) {
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_PACKET_SENT,
base::Bind(&NetLogQuicPacketSentCallback, sequence_number, level,
- packet.length(), result));
+ transmission_type, packet.length(), result));
}
void QuicConnectionLogger:: OnPacketRetransmitted(
@@ -277,6 +448,14 @@ void QuicConnectionLogger:: OnPacketRetransmitted(
void QuicConnectionLogger::OnPacketReceived(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
const QuicEncryptedPacket& packet) {
+ if (local_address_from_self_.GetFamily() == ADDRESS_FAMILY_UNSPECIFIED) {
+ local_address_from_self_ = self_address;
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ConnectionTypeFromSelf",
+ GetRealAddressFamily(self_address.address()),
+ ADDRESS_FAMILY_LAST);
+ }
+
+ last_received_packet_size_ = packet.length();
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_PACKET_RECEIVED,
base::Bind(&NetLogQuicPacketCallback, &self_address, &peer_address,
@@ -292,6 +471,7 @@ void QuicConnectionLogger::OnPacketHeader(const QuicPacketHeader& header) {
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_PACKET_HEADER_RECEIVED,
base::Bind(&NetLogQuicPacketHeaderCallback, &header));
+ ++num_packets_received_;
if (largest_received_packet_sequence_number_ <
header.packet_sequence_number) {
QuicPacketSequenceNumber delta = header.packet_sequence_number -
@@ -304,8 +484,10 @@ void QuicConnectionLogger::OnPacketHeader(const QuicPacketHeader& header) {
}
largest_received_packet_sequence_number_ = header.packet_sequence_number;
}
+ if (header.packet_sequence_number < received_packets_.size())
+ received_packets_[header.packet_sequence_number] = true;
if (header.packet_sequence_number < last_received_packet_sequence_number_) {
- ++out_of_order_recieved_packet_count_;
+ ++num_out_of_order_received_packets_;
UMA_HISTOGRAM_COUNTS("Net.QuicSession.OutOfOrderGapReceived",
last_received_packet_sequence_number_ -
header.packet_sequence_number);
@@ -324,6 +506,14 @@ void QuicConnectionLogger::OnAckFrame(const QuicAckFrame& frame) {
NetLog::TYPE_QUIC_SESSION_ACK_FRAME_RECEIVED,
base::Bind(&NetLogQuicAckFrameCallback, &frame));
+ const size_t kApproximateLargestSoloAckBytes = 100;
+ if (last_received_packet_sequence_number_ < received_acks_.size() &&
+ last_received_packet_size_ < kApproximateLargestSoloAckBytes)
+ received_acks_[last_received_packet_sequence_number_] = true;
+
+ if (frame.received_info.is_truncated)
+ ++num_truncated_acks_received_;
+
if (frame.received_info.missing_packets.empty())
return;
@@ -367,6 +557,13 @@ void QuicConnectionLogger::OnCongestionFeedbackFrame(
base::Bind(&NetLogQuicCongestionFeedbackFrameCallback, &frame));
}
+void QuicConnectionLogger::OnStopWaitingFrame(
+ const QuicStopWaitingFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_STOP_WAITING_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicStopWaitingFrameCallback, &frame));
+}
+
void QuicConnectionLogger::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.RstStreamErrorCodeServer",
frame.error_code);
@@ -382,9 +579,35 @@ void QuicConnectionLogger::OnConnectionCloseFrame(
base::Bind(&NetLogQuicConnectionCloseFrameCallback, &frame));
}
+void QuicConnectionLogger::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_WINDOW_UPDATE_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicWindowUpdateFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_BLOCKED_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicBlockedFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_GOAWAY_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicGoAwayFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnPingFrame(const QuicPingFrame& frame) {
+ // PingFrame has no contents to log, so just record that it was received.
+ net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PING_FRAME_RECEIVED);
+}
+
void QuicConnectionLogger::OnPublicResetPacket(
const QuicPublicResetPacket& packet) {
net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PUBLIC_RESET_PACKET_RECEIVED);
+ UpdatePublicResetAddressMismatchHistogram(local_address_from_shlo_,
+ packet.client_address);
}
void QuicConnectionLogger::OnVersionNegotiationPacket(
@@ -407,6 +630,19 @@ void QuicConnectionLogger::OnCryptoHandshakeMessageReceived(
net_log_.AddEvent(
NetLog::TYPE_QUIC_SESSION_CRYPTO_HANDSHAKE_MESSAGE_RECEIVED,
base::Bind(&NetLogQuicCryptoHandshakeMessageCallback, &message));
+
+ if (message.tag() == kSHLO) {
+ StringPiece address;
+ QuicSocketAddressCoder decoder;
+ if (message.GetStringPiece(kCADR, &address) &&
+ decoder.Decode(address.data(), address.size())) {
+ local_address_from_shlo_ = IPEndPoint(decoder.ip(), decoder.port());
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ConnectionTypeFromPeer",
+ GetRealAddressFamily(
+ local_address_from_shlo_.address()),
+ ADDRESS_FAMILY_LAST);
+ }
+ }
}
void QuicConnectionLogger::OnCryptoHandshakeMessageSent(
@@ -430,4 +666,172 @@ void QuicConnectionLogger::OnSuccessfulVersionNegotiation(
NetLog::StringCallback("version", &quic_version));
}
+void QuicConnectionLogger::UpdateReceivedFrameCounts(
+ QuicStreamId stream_id,
+ int num_frames_received,
+ int num_duplicate_frames_received) {
+ if (stream_id != kCryptoStreamId) {
+ num_frames_received_ += num_frames_received;
+ num_duplicate_frames_received_ += num_duplicate_frames_received;
+ }
+}
+
+base::HistogramBase* QuicConnectionLogger::GetPacketSequenceNumberHistogram(
+ const char* statistic_name) const {
+ string prefix("Net.QuicSession.PacketReceived_");
+ return base::LinearHistogram::FactoryGet(
+ prefix + statistic_name + connection_description_,
+ 1, received_packets_.size(), received_packets_.size() + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+base::HistogramBase* QuicConnectionLogger::Get6PacketHistogram(
+ const char* which_6) const {
+ // This histogram takes a binary encoding of the 6 consecutive packets
+ // received. As a result, there are 64 possible sample-patterns.
+ string prefix("Net.QuicSession.6PacketsPatternsReceived_");
+ return base::LinearHistogram::FactoryGet(
+ prefix + which_6 + connection_description_, 1, 64, 65,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+base::HistogramBase* QuicConnectionLogger::Get21CumulativeHistogram(
+ const char* which_21) const {
+ // This histogram contains, for each sequence of 21 packets, the results from
+ // 21 distinct questions about that sequence. Conceptually the histogtram is
+ // broken into 21 distinct ranges, and one sample is added into each of those
+ // ranges whenever we process a set of 21 packets.
+ // There is a little rendundancy, as each "range" must have the same number
+ // of samples, all told, but the histogram is a tad easier to read this way.
+ // The questions are:
+ // Was the first packet present (bucket 0==>no; bucket 1==>yes)
+ // Of the first two packets, how many were present? (bucket 2==> none;
+ // bucket 3==> 1 of 2; bucket 4==> 2 of 2)
+ // Of the first three packets, how many were present? (bucket 5==>none;
+ // bucket 6==> 1 of 3; bucket 7==> 2 of 3; bucket 8==> 3 of 3).
+ // etc.
+ string prefix("Net.QuicSession.21CumulativePacketsReceived_");
+ return base::LinearHistogram::FactoryGet(
+ prefix + which_21 + connection_description_,
+ 1, kBoundingSampleInCumulativeHistogram,
+ kBoundingSampleInCumulativeHistogram + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+// static
+void QuicConnectionLogger::AddTo21CumulativeHistogram(
+ base::HistogramBase* histogram,
+ int bit_mask_of_packets,
+ int valid_bits_in_mask) {
+ DCHECK_LE(valid_bits_in_mask, 21);
+ DCHECK_LT(bit_mask_of_packets, 1 << 21);
+ const int blank_bits_in_mask = 21 - valid_bits_in_mask;
+ DCHECK_EQ(bit_mask_of_packets & ((1 << blank_bits_in_mask) - 1), 0);
+ bit_mask_of_packets >>= blank_bits_in_mask;
+ int bits_so_far = 0;
+ int range_start = 0;
+ for (int i = 1; i <= valid_bits_in_mask; ++i) {
+ bits_so_far += bit_mask_of_packets & 1;
+ bit_mask_of_packets >>= 1;
+ DCHECK_LT(range_start + bits_so_far, kBoundingSampleInCumulativeHistogram);
+ histogram->Add(range_start + bits_so_far);
+ range_start += i + 1;
+ }
+}
+
+void QuicConnectionLogger::RecordAggregatePacketLossRate() const {
+ // For short connections under 22 packets in length, we'll rely on the
+ // Net.QuicSession.21CumulativePacketsReceived_* histogram to indicate packet
+ // loss rates. This way we avoid tremendously anomalous contributions to our
+ // histogram. (e.g., if we only got 5 packets, but lost 1, we'd otherwise
+ // record a 20% loss in this histogram!). We may still get some strange data
+ // (1 loss in 22 is still high :-/).
+ if (largest_received_packet_sequence_number_ <= 21)
+ return;
+
+ QuicPacketSequenceNumber divisor = largest_received_packet_sequence_number_;
+ QuicPacketSequenceNumber numerator = divisor - num_packets_received_;
+ if (divisor < 100000)
+ numerator *= 1000;
+ else
+ divisor /= 1000;
+ string prefix("Net.QuicSession.PacketLossRate_");
+ base::HistogramBase* histogram = base::Histogram::FactoryGet(
+ prefix + connection_description_, 1, 1000, 75,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ histogram->Add(numerator / divisor);
+}
+
+void QuicConnectionLogger::RecordLossHistograms() const {
+ if (largest_received_packet_sequence_number_ == 0)
+ return; // Connection was never used.
+ RecordAggregatePacketLossRate();
+
+ base::HistogramBase* is_not_ack_histogram =
+ GetPacketSequenceNumberHistogram("IsNotAck_");
+ base::HistogramBase* is_an_ack_histogram =
+ GetPacketSequenceNumberHistogram("IsAnAck_");
+ base::HistogramBase* packet_arrived_histogram =
+ GetPacketSequenceNumberHistogram("Ack_");
+ base::HistogramBase* packet_missing_histogram =
+ GetPacketSequenceNumberHistogram("Nack_");
+ base::HistogramBase* ongoing_cumulative_packet_histogram =
+ Get21CumulativeHistogram("Some21s_");
+ base::HistogramBase* first_cumulative_packet_histogram =
+ Get21CumulativeHistogram("First21_");
+ base::HistogramBase* six_packet_histogram = Get6PacketHistogram("Some6s_");
+
+ DCHECK_EQ(received_packets_.size(), received_acks_.size());
+ const QuicPacketSequenceNumber last_index =
+ std::min<QuicPacketSequenceNumber>(received_packets_.size() - 1,
+ largest_received_packet_sequence_number_);
+ const QuicPacketSequenceNumber index_of_first_21_contribution =
+ std::min<QuicPacketSequenceNumber>(21, last_index);
+ // Bit pattern of consecutively received packets that is maintained as we scan
+ // through the received_packets_ vector. Less significant bits correspond to
+ // less recent packets, and only the low order 21 bits are ever defined.
+ // Bit is 1 iff corresponding packet was received.
+ int packet_pattern_21 = 0;
+ // Zero is an invalid packet sequence number.
+ DCHECK(!received_packets_[0]);
+ for (size_t i = 1; i <= last_index; ++i) {
+ if (received_acks_[i])
+ is_an_ack_histogram->Add(i);
+ else
+ is_not_ack_histogram->Add(i);
+
+ packet_pattern_21 >>= 1;
+ if (received_packets_[i]) {
+ packet_arrived_histogram->Add(i);
+ packet_pattern_21 |= (1 << 20); // Turn on the 21st bit.
+ } else {
+ packet_missing_histogram->Add(i);
+ }
+
+ if (i == index_of_first_21_contribution) {
+ AddTo21CumulativeHistogram(first_cumulative_packet_histogram,
+ packet_pattern_21, i);
+ }
+ // We'll just record for non-overlapping ranges, to reduce histogramming
+ // cost for now. Each call does 21 separate histogram additions.
+ if (i > 21 || i % 21 == 0) {
+ AddTo21CumulativeHistogram(ongoing_cumulative_packet_histogram,
+ packet_pattern_21, 21);
+ }
+
+ if (i < 6)
+ continue; // Not enough packets to do any pattern recording.
+ int recent_6_mask = packet_pattern_21 >> 15;
+ DCHECK_LT(recent_6_mask, 64);
+ if (i == 6) {
+ Get6PacketHistogram("First6_")->Add(recent_6_mask);
+ continue;
+ }
+ // Record some overlapping patterns, to get a better picture, since this is
+ // not very expensive.
+ if (i % 3 == 0)
+ six_packet_histogram->Add(recent_6_mask);
+ }
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_connection_logger.h b/chromium/net/quic/quic_connection_logger.h
index d162282204a..2110f9ef6d3 100644
--- a/chromium/net/quic/quic_connection_logger.h
+++ b/chromium/net/quic/quic_connection_logger.h
@@ -5,18 +5,22 @@
#ifndef NET_QUIC_QUIC_CONNECTION_LOGGER_H_
#define NET_QUIC_QUIC_CONNECTION_LOGGER_H_
+#include <bitset>
+
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_log.h"
+#include "net/base/network_change_notifier.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_protocol.h"
namespace net {
-class BoundNetLog;
class CryptoHandshakeMessage;
// This class is a debug visitor of a QuicConnection which logs
// events to |net_log|.
class NET_EXPORT_PRIVATE QuicConnectionLogger
- : public QuicConnectionDebugVisitorInterface {
+ : public QuicConnectionDebugVisitor {
public:
explicit QuicConnectionLogger(const BoundNetLog& net_log);
@@ -28,6 +32,7 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger
// QuicConnectionDebugVisitorInterface
virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number,
EncryptionLevel level,
+ TransmissionType transmission_type,
const QuicEncryptedPacket& packet,
WriteResult result) OVERRIDE;
virtual void OnPacketRetransmitted(
@@ -42,9 +47,14 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger
virtual void OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
virtual void OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE;
virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
virtual void OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) OVERRIDE;
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE;
+ virtual void OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE;
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual void OnPingFrame(const QuicPingFrame& frame) OVERRIDE;
virtual void OnPublicResetPacket(
const QuicPublicResetPacket& packet) OVERRIDE;
virtual void OnVersionNegotiationPacket(
@@ -58,20 +68,87 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger
const CryptoHandshakeMessage& message);
void OnConnectionClosed(QuicErrorCode error, bool from_peer);
void OnSuccessfulVersionNegotiation(const QuicVersion& version);
+ void UpdateReceivedFrameCounts(QuicStreamId stream_id,
+ int num_frames_received,
+ int num_duplicate_frames_received);
private:
+ // Do a factory get for a histogram for recording data, about individual
+ // packet sequence numbers, that was gathered in the vectors
+ // received_packets_ and received_acks_. |statistic_name| identifies which
+ // element of data is recorded, and is used to form the histogram name.
+ base::HistogramBase* GetPacketSequenceNumberHistogram(
+ const char* statistic_name) const;
+ // Do a factory get for a histogram to record a 6-packet loss-sequence as a
+ // sample. The histogram will record the 64 distinct possible combinations.
+ // |which_6| is used to adjust the name of the histogram to distinguish the
+ // first 6 packets in a connection, vs. some later 6 packets.
+ base::HistogramBase* Get6PacketHistogram(const char* which_6) const;
+ // Do a factory get for a histogram to record cumulative stats across a 21
+ // packet sequence. |which_21| is used to adjust the name of the histogram
+ // to distinguish the first 21 packets' loss data, vs. some later 21 packet
+ // sequences' loss data.
+ base::HistogramBase* Get21CumulativeHistogram(const char* which_21) const;
+ // Add samples associated with a |bit_mask_of_packets| to the given histogram
+ // that was provided by Get21CumulativeHistogram(). The LSB of that mask
+ // corresponds to the oldest packet sequence number in the series of packets,
+ // and the bit in the 2^20 position corresponds to the most recently received
+ // packet. Of the maximum of 21 bits that are valid (correspond to packets),
+ // only the most significant |valid_bits_in_mask| are processed.
+ // A bit value of 0 indicates that a packet was never received, and a 1
+ // indicates the packet was received.
+ static void AddTo21CumulativeHistogram(base::HistogramBase* histogram,
+ int bit_mask_of_packets,
+ int valid_bits_in_mask);
+ // For connections longer than 21 received packets, this call will calculate
+ // the overall packet loss rate, and record it into a histogram.
+ void RecordAggregatePacketLossRate() const;
+ // At destruction time, this records results of |pacaket_received_| into
+ // histograms for specific connection types.
+ void RecordLossHistograms() const;
+
BoundNetLog net_log_;
// The last packet sequence number received.
QuicPacketSequenceNumber last_received_packet_sequence_number_;
- // The largest packet sequence number received. In case of that a packet is
- // received late, this value will not be updated.
+ // The size of the most recently received packet.
+ size_t last_received_packet_size_;
+ // The largest packet sequence number received. In the case where a packet is
+ // received late (out of order), this value will not be updated.
QuicPacketSequenceNumber largest_received_packet_sequence_number_;
// The largest packet sequence number which the peer has failed to
// receive, according to the missing packet set in their ack frames.
QuicPacketSequenceNumber largest_received_missing_packet_sequence_number_;
// Number of times that the current received packet sequence number is
// smaller than the last received packet sequence number.
- size_t out_of_order_recieved_packet_count_;
+ size_t num_out_of_order_received_packets_;
+ // The number of times that OnPacketHeader was called.
+ // If the network replicates packets, then this number may be slightly
+ // different from the real number of distinct packets received.
+ QuicPacketSequenceNumber num_packets_received_;
+ // Number of times a truncated ACK frame was sent.
+ size_t num_truncated_acks_sent_;
+ // Number of times a truncated ACK frame was received.
+ size_t num_truncated_acks_received_;
+ // The kCADR value provided by the server in ServerHello.
+ IPEndPoint local_address_from_shlo_;
+ // The first local address from which a packet was received.
+ IPEndPoint local_address_from_self_;
+ // Count of the number of frames received.
+ int num_frames_received_;
+ // Count of the number of duplicate frames received.
+ int num_duplicate_frames_received_;
+ // Vector of inital packets status' indexed by packet sequence numbers, where
+ // false means never received. Zero is not a valid packet sequence number, so
+ // that offset is never used, and we'll track 150 packets.
+ std::bitset<151> received_packets_;
+ // Vector to indicate which of the initial 150 received packets turned out to
+ // contain solo ACK frames. An element is true iff an ACK frame was in the
+ // corresponding packet, and there was very little else.
+ std::bitset<151> received_acks_;
+ // The available type of connection (WiFi, 3G, etc.) when connection was first
+ // used.
+ const char* const connection_description_;
+
DISALLOW_COPY_AND_ASSIGN(QuicConnectionLogger);
};
diff --git a/chromium/net/quic/quic_connection_stats.cc b/chromium/net/quic/quic_connection_stats.cc
index 500b5f39166..cf235cd751e 100644
--- a/chromium/net/quic/quic_connection_stats.cc
+++ b/chromium/net/quic/quic_connection_stats.cc
@@ -12,16 +12,33 @@ QuicConnectionStats::QuicConnectionStats()
: bytes_sent(0),
packets_sent(0),
stream_bytes_sent(0),
+ packets_discarded(0),
bytes_received(0),
packets_received(0),
+ packets_processed(0),
stream_bytes_received(0),
bytes_retransmitted(0),
packets_retransmitted(0),
+ bytes_spuriously_retransmitted(0),
+ packets_spuriously_retransmitted(0),
+ packets_lost(0),
+ slowstart_packets_lost(0),
packets_revived(0),
packets_dropped(0),
+ crypto_retransmit_count(0),
+ loss_timeout_count(0),
+ tlp_count(0),
rto_count(0),
- rtt(0),
- estimated_bandwidth(0) {
+ min_rtt_us(0),
+ srtt_us(0),
+ estimated_bandwidth(0),
+ packets_reordered(0),
+ max_sequence_reordering(0),
+ max_time_reordering_us(0),
+ tcp_loss_events(0),
+ cwnd_increase_congestion_avoidance(0),
+ cwnd_increase_cubic_mode(0),
+ connection_creation_time(QuicTime::Zero()) {
}
QuicConnectionStats::~QuicConnectionStats() {}
@@ -30,16 +47,36 @@ ostream& operator<<(ostream& os, const QuicConnectionStats& s) {
os << "{ bytes sent: " << s.bytes_sent
<< ", packets sent:" << s.packets_sent
<< ", stream bytes sent: " << s.stream_bytes_sent
+ << ", packets discarded: " << s.packets_discarded
<< ", bytes received: " << s.bytes_received
<< ", packets received: " << s.packets_received
+ << ", packets processed: " << s.packets_processed
<< ", stream bytes received: " << s.stream_bytes_received
<< ", bytes retransmitted: " << s.bytes_retransmitted
<< ", packets retransmitted: " << s.packets_retransmitted
+ << ", bytes spuriously retransmitted: " << s.bytes_spuriously_retransmitted
+ << ", packets spuriously retransmitted: "
+ << s.packets_spuriously_retransmitted
+ << ", packets lost: " << s.packets_lost
+ << ", slowstart packets lost: " << s.slowstart_packets_lost
<< ", packets revived: " << s.packets_revived
<< ", packets dropped:" << s.packets_dropped
+ << ", crypto retransmit count: " << s.crypto_retransmit_count
<< ", rto count: " << s.rto_count
- << ", rtt(us): " << s.rtt
- << ", estimated_bandwidth: " << s.estimated_bandwidth
+ << ", tlp count: " << s.tlp_count
+ << ", min_rtt(us): " << s.min_rtt_us
+ << ", srtt(us): " << s.srtt_us
+ << ", max packet size: " << s.max_packet_size
+ << ", estimated bandwidth: " << s.estimated_bandwidth
+ << ", congestion window: " << s.congestion_window
+ << ", tcp_loss_events: " << s.tcp_loss_events
+ << ", packets reordered: " << s.packets_reordered
+ << ", max sequence reordering: " << s.max_sequence_reordering
+ << ", max time reordering(us): " << s.max_time_reordering_us
+ << ", total amount of cwnd increase in TCPCubic, in congestion avoidance: "
+ << s.cwnd_increase_congestion_avoidance
+ << ", amount of cwnd increase in TCPCubic, in cubic mode: "
+ << s.cwnd_increase_cubic_mode
<< "}\n";
return os;
}
diff --git a/chromium/net/quic/quic_connection_stats.h b/chromium/net/quic/quic_connection_stats.h
index b0761d9ba8f..0bad8569029 100644
--- a/chromium/net/quic/quic_connection_stats.h
+++ b/chromium/net/quic/quic_connection_stats.h
@@ -9,19 +9,9 @@
#include "base/basictypes.h"
#include "net/base/net_export.h"
+#include "net/quic/quic_time.h"
namespace net {
-// TODO(satyamshekhar): Add more interesting stats:
-// 1. (C/S)HLO retransmission count.
-// 2. SHLO received to first stream packet processed time.
-// 3. CHLO sent to SHLO received time.
-// 4. Number of migrations.
-// 5. Number of out of order packets.
-// 6. Number of connections that require more that 1-RTT.
-// 7. Avg number of streams / session.
-// 8. Number of duplicates received.
-// 9. Fraction of data transferred that was padding.
-
// Structure to hold stats for a QuicConnection.
struct NET_EXPORT_PRIVATE QuicConnectionStats {
QuicConnectionStats();
@@ -30,24 +20,61 @@ struct NET_EXPORT_PRIVATE QuicConnectionStats {
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
std::ostream& os, const QuicConnectionStats& s);
- uint64 bytes_sent; // includes retransmissions, fec.
+ uint64 bytes_sent; // Includes retransmissions, fec.
uint32 packets_sent;
uint64 stream_bytes_sent; // non-retransmitted bytes sent in a stream frame.
+ uint32 packets_discarded; // Packets serialized and discarded before sending.
- uint64 bytes_received; // includes duplicate data for a stream, fec.
- uint32 packets_received; // includes dropped packets
- uint64 stream_bytes_received; // bytes received in a stream frame.
+ // These include version negotiation and public reset packets, which do not
+ // have sequence numbers or frame data.
+ uint64 bytes_received; // Includes duplicate data for a stream, fec.
+ uint32 packets_received; // Includes packets which were not processable.
+ uint32 packets_processed; // Excludes packets which were not processable.
+ uint64 stream_bytes_received; // Bytes received in a stream frame.
uint64 bytes_retransmitted;
uint32 packets_retransmitted;
+ uint64 bytes_spuriously_retransmitted;
+ uint32 packets_spuriously_retransmitted;
+ // Number of packets abandoned as lost by the loss detection algorithm.
+ uint32 packets_lost;
+ uint32 slowstart_packets_lost; // Number of packets lost exiting slow start.
+
uint32 packets_revived;
- uint32 packets_dropped; // duplicate or less than least unacked.
- uint32 rto_count;
+ uint32 packets_dropped; // Duplicate or less than least unacked.
+ uint32 crypto_retransmit_count;
+ // Count of times the loss detection alarm fired. At least one packet should
+ // be lost when the alarm fires.
+ uint32 loss_timeout_count;
+ uint32 tlp_count;
+ uint32 rto_count; // Count of times the rto timer fired.
+
+ uint32 min_rtt_us; // Minimum RTT in microseconds.
+ uint32 srtt_us; // Smoothed RTT in microseconds.
+ uint32 max_packet_size; // In bytes.
+ uint64 estimated_bandwidth; // In bytes per second.
+ uint32 congestion_window; // In bytes
+
+ // Reordering stats for received packets.
+ // Number of packets received out of sequence number order.
+ uint32 packets_reordered;
+ // Maximum reordering observed in sequence space.
+ uint32 max_sequence_reordering;
+ // Maximum reordering observed in microseconds
+ uint32 max_time_reordering_us;
+
+ // The following stats are used only in TcpCubicSender.
+ // The number of loss events from TCP's perspective. Each loss event includes
+ // one or more lost packets.
+ uint32 tcp_loss_events;
+ // Total amount of cwnd increase by TCPCubic in congestion avoidance.
+ uint32 cwnd_increase_congestion_avoidance;
+ // Total amount of cwnd increase by TCPCubic in cubic mode.
+ uint32 cwnd_increase_cubic_mode;
- uint32 rtt; // In microseconds
- uint64 estimated_bandwidth;
- // TODO(satyamshekhar): Add window_size, mss and mtu.
+ // Creation time, as reported by the QuicClock.
+ QuicTime connection_creation_time;
};
} // namespace net
diff --git a/chromium/net/quic/quic_connection_test.cc b/chromium/net/quic/quic_connection_test.cc
index 39655d0fe2a..38ead0ca1c0 100644
--- a/chromium/net/quic/quic_connection_test.cc
+++ b/chromium/net/quic/quic_connection_test.cc
@@ -8,29 +8,33 @@
#include "base/bind.h"
#include "base/stl_util.h"
#include "net/base/net_errors.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
#include "net/quic/congestion_control/receive_algorithm_interface.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/crypto/null_encrypter.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_sent_packet_manager.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_random.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_framer_peer.h"
#include "net/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simple_quic_framer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece;
using std::map;
using std::vector;
-using testing::_;
using testing::AnyNumber;
+using testing::AtLeast;
using testing::ContainerEq;
+using testing::Contains;
using testing::DoAll;
using testing::InSequence;
using testing::InvokeWithoutArgs;
@@ -38,6 +42,7 @@ using testing::Ref;
using testing::Return;
using testing::SaveArg;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace test {
@@ -54,11 +59,6 @@ const QuicPacketEntropyHash kTestEntropyHash = 76;
const int kDefaultRetransmissionTimeMs = 500;
const int kMinRetransmissionTimeMs = 200;
-// Used by TestConnection::SendStreamData3.
-const QuicStreamId kStreamId3 = 3;
-// Used by TestConnection::SendStreamData5.
-const QuicStreamId kStreamId5 = 5;
-
class TestReceiveAlgorithm : public ReceiveAlgorithmInterface {
public:
explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback)
@@ -74,8 +74,8 @@ class TestReceiveAlgorithm : public ReceiveAlgorithmInterface {
return true;
}
- MOCK_METHOD4(RecordIncomingPacket,
- void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool));
+ MOCK_METHOD3(RecordIncomingPacket,
+ void(QuicByteCount, QuicPacketSequenceNumber, QuicTime));
private:
QuicCongestionFeedbackFrame* feedback_;
@@ -142,6 +142,8 @@ class TaggingEncrypter : public QuicEncrypter {
};
const uint8 tag_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaggingEncrypter);
};
// TaggingDecrypter ensures that the final kTagSize bytes of the message all
@@ -269,23 +271,24 @@ class TestConnectionHelper : public QuicConnectionHelperInterface {
class TestPacketWriter : public QuicPacketWriter {
public:
- TestPacketWriter()
- : last_packet_size_(0),
- blocked_(false),
+ explicit TestPacketWriter(QuicVersion version)
+ : version_(version),
+ framer_(SupportedVersions(version_)),
+ last_packet_size_(0),
+ write_blocked_(false),
+ block_on_next_write_(false),
is_write_blocked_data_buffered_(false),
- is_server_(true),
final_bytes_of_last_packet_(0),
final_bytes_of_previous_packet_(0),
use_tagging_decrypter_(false),
packets_write_attempts_(0) {
}
- // QuicPacketWriter
+ // QuicPacketWriter interface
virtual WriteResult WritePacket(
const char* buffer, size_t buf_len,
const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) OVERRIDE {
+ const IPEndPoint& peer_address) OVERRIDE {
QuicEncryptedPacket packet(buffer, buf_len);
++packets_write_attempts_;
@@ -295,14 +298,15 @@ class TestPacketWriter : public QuicPacketWriter {
sizeof(final_bytes_of_last_packet_));
}
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), !is_server_);
if (use_tagging_decrypter_) {
- framer.SetDecrypter(new TaggingDecrypter);
+ framer_.framer()->SetDecrypter(new TaggingDecrypter, ENCRYPTION_NONE);
}
- visitor_.Reset();
- framer.set_visitor(&visitor_);
- EXPECT_TRUE(framer.ProcessPacket(packet));
- if (blocked_) {
+ EXPECT_TRUE(framer_.ProcessPacket(packet));
+ if (block_on_next_write_) {
+ write_blocked_ = true;
+ block_on_next_write_ = false;
+ }
+ if (IsWriteBlocked()) {
return WriteResult(WRITE_STATUS_BLOCKED, -1);
}
last_packet_size_ = packet.length();
@@ -313,40 +317,57 @@ class TestPacketWriter : public QuicPacketWriter {
return is_write_blocked_data_buffered_;
}
- // Resets the visitor's state by clearing out the headers and frames.
- void Reset() {
- visitor_.Reset();
- }
+ virtual bool IsWriteBlocked() const OVERRIDE { return write_blocked_; }
+
+ virtual void SetWritable() OVERRIDE { write_blocked_ = false; }
+
+ void BlockOnNextWrite() { block_on_next_write_ = true; }
- QuicPacketHeader* header() { return visitor_.header(); }
+ const QuicPacketHeader& header() { return framer_.header(); }
- size_t frame_count() const { return visitor_.frame_count(); }
+ size_t frame_count() const { return framer_.num_frames(); }
- QuicAckFrame* ack() { return visitor_.ack(); }
+ const vector<QuicAckFrame>& ack_frames() const {
+ return framer_.ack_frames();
+ }
+
+ const vector<QuicCongestionFeedbackFrame>& feedback_frames() const {
+ return framer_.feedback_frames();
+ }
+
+ const vector<QuicStopWaitingFrame>& stop_waiting_frames() const {
+ return framer_.stop_waiting_frames();
+ }
- QuicCongestionFeedbackFrame* feedback() { return visitor_.feedback(); }
+ const vector<QuicConnectionCloseFrame>& connection_close_frames() const {
+ return framer_.connection_close_frames();
+ }
- QuicConnectionCloseFrame* close() { return visitor_.close(); }
+ const vector<QuicStreamFrame>& stream_frames() const {
+ return framer_.stream_frames();
+ }
- const vector<QuicStreamFrame>* stream_frames() const {
- return visitor_.stream_frames();
+ const vector<QuicPingFrame>& ping_frames() const {
+ return framer_.ping_frames();
}
size_t last_packet_size() {
return last_packet_size_;
}
- QuicVersionNegotiationPacket* version_negotiation_packet() {
- return visitor_.version_negotiation_packet();
+ const QuicVersionNegotiationPacket* version_negotiation_packet() {
+ return framer_.version_negotiation_packet();
}
- void set_blocked(bool blocked) { blocked_ = blocked; }
-
void set_is_write_blocked_data_buffered(bool buffered) {
is_write_blocked_data_buffered_ = buffered;
}
- void set_is_server(bool is_server) { is_server_ = is_server; }
+ void set_is_server(bool is_server) {
+ // We invert is_server here, because the framer needs to parse packets
+ // we send.
+ QuicFramerPeer::SetIsServer(framer_.framer(), !is_server);
+ }
// final_bytes_of_last_packet_ returns the last four bytes of the previous
// packet as a little-endian, uint32. This is intended to be used with a
@@ -365,12 +386,19 @@ class TestPacketWriter : public QuicPacketWriter {
uint32 packets_write_attempts() { return packets_write_attempts_; }
+ void Reset() { framer_.Reset(); }
+
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ framer_.SetSupportedVersions(versions);
+ }
+
private:
- FramerVisitorCapturingFrames visitor_;
+ QuicVersion version_;
+ SimpleQuicFramer framer_;
size_t last_packet_size_;
- bool blocked_;
+ bool write_blocked_;
+ bool block_on_next_write_;
bool is_write_blocked_data_buffered_;
- bool is_server_;
uint32 final_bytes_of_last_packet_;
uint32 final_bytes_of_previous_packet_;
bool use_tagging_decrypter_;
@@ -381,15 +409,18 @@ class TestPacketWriter : public QuicPacketWriter {
class TestConnection : public QuicConnection {
public:
- TestConnection(QuicGuid guid,
+ TestConnection(QuicConnectionId connection_id,
IPEndPoint address,
TestConnectionHelper* helper,
TestPacketWriter* writer,
- bool is_server)
- : QuicConnection(guid, address, helper, writer, is_server,
- QuicSupportedVersions()),
- helper_(helper),
+ bool is_server,
+ QuicVersion version)
+ : QuicConnection(connection_id, address, helper, writer, is_server,
+ SupportedVersions(version)),
writer_(writer) {
+ // Disable tail loss probes for most tests.
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicConnectionPeer::GetSentPacketManager(this), 0);
writer_->set_is_server(is_server);
}
@@ -405,6 +436,11 @@ class TestConnection : public QuicConnection {
QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
}
+ void SetLossAlgorithm(LossDetectionInterface* loss_algorithm) {
+ QuicSentPacketManagerPeer::SetLossAlgorithm(
+ QuicConnectionPeer::GetSentPacketManager(this), loss_algorithm);
+ }
+
void SendPacket(EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
@@ -424,19 +460,58 @@ class TestConnection : public QuicConnection {
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate) {
+ return SendStreamDataWithStringHelper(id, data, offset, fin,
+ MAY_FEC_PROTECT, delegate);
+ }
+
+ QuicConsumedData SendStreamDataWithStringWithFec(
+ QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ return SendStreamDataWithStringHelper(id, data, offset, fin,
+ MUST_FEC_PROTECT, delegate);
+ }
+
+ QuicConsumedData SendStreamDataWithStringHelper(
+ QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* delegate) {
IOVector data_iov;
if (!data.empty()) {
data_iov.Append(const_cast<char*>(data.data()), data.size());
}
- return QuicConnection::SendStreamData(id, data_iov, offset, fin, delegate);
+ return QuicConnection::SendStreamData(id, data_iov, offset, fin,
+ fec_protection, delegate);
}
QuicConsumedData SendStreamData3() {
- return SendStreamDataWithString(kStreamId3, "food", 0, !kFin, NULL);
+ return SendStreamDataWithString(kClientDataStreamId1, "food", 0, !kFin,
+ NULL);
+ }
+
+ QuicConsumedData SendStreamData3WithFec() {
+ return SendStreamDataWithStringWithFec(kClientDataStreamId1, "food", 0,
+ !kFin, NULL);
}
QuicConsumedData SendStreamData5() {
- return SendStreamDataWithString(kStreamId5, "food2", 0, !kFin, NULL);
+ return SendStreamDataWithString(kClientDataStreamId2, "food2", 0,
+ !kFin, NULL);
+ }
+
+ QuicConsumedData SendStreamData5WithFec() {
+ return SendStreamDataWithStringWithFec(kClientDataStreamId2, "food2", 0,
+ !kFin, NULL);
+ }
+ // Ensures the connection can write stream data before writing.
+ QuicConsumedData EnsureWritableAndSendStreamData5() {
+ EXPECT_TRUE(CanWriteStreamData());
+ return SendStreamData5();
}
// The crypto stream has special semantics so that it is not blocked by a
@@ -445,11 +520,7 @@ class TestConnection : public QuicConnection {
// split needlessly across packet boundaries). As a result, we have separate
// tests for some cases for this stream.
QuicConsumedData SendCryptoStreamData() {
- this->Flush();
- QuicConsumedData consumed =
- SendStreamDataWithString(kCryptoStreamId, "chlo", 0, !kFin, NULL);
- this->Flush();
- return consumed;
+ return SendStreamDataWithString(kCryptoStreamId, "chlo", 0, !kFin, NULL);
}
bool is_server() {
@@ -457,13 +528,16 @@ class TestConnection : public QuicConnection {
}
void set_version(QuicVersion version) {
- framer_.set_version(version);
+ QuicConnectionPeer::GetFramer(this)->set_version(version);
+ }
+
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ QuicConnectionPeer::GetFramer(this)->SetSupportedVersions(versions);
+ writer_->SetSupportedVersions(versions);
}
void set_is_server(bool is_server) {
writer_->set_is_server(is_server);
- QuicPacketCreatorPeer::SetIsServer(
- QuicConnectionPeer::GetPacketCreator(this), is_server);
QuicConnectionPeer::SetIsServer(this, is_server);
}
@@ -472,6 +546,16 @@ class TestConnection : public QuicConnection {
QuicConnectionPeer::GetAckAlarm(this));
}
+ TestConnectionHelper::TestAlarm* GetPingAlarm() {
+ return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
+ QuicConnectionPeer::GetPingAlarm(this));
+ }
+
+ TestConnectionHelper::TestAlarm* GetResumeWritesAlarm() {
+ return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
+ QuicConnectionPeer::GetResumeWritesAlarm(this));
+ }
+
TestConnectionHelper::TestAlarm* GetRetransmissionAlarm() {
return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
QuicConnectionPeer::GetRetransmissionAlarm(this));
@@ -482,11 +566,6 @@ class TestConnection : public QuicConnection {
QuicConnectionPeer::GetSendAlarm(this));
}
- TestConnectionHelper::TestAlarm* GetResumeWritesAlarm() {
- return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
- QuicConnectionPeer::GetResumeWritesAlarm(this));
- }
-
TestConnectionHelper::TestAlarm* GetTimeoutAlarm() {
return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
QuicConnectionPeer::GetTimeoutAlarm(this));
@@ -495,81 +574,95 @@ class TestConnection : public QuicConnection {
using QuicConnection::SelectMutualVersion;
private:
- TestConnectionHelper* helper_;
TestPacketWriter* writer_;
DISALLOW_COPY_AND_ASSIGN(TestConnection);
};
-class QuicConnectionTest : public ::testing::TestWithParam<bool> {
+// Used for testing packets revived from FEC packets.
+class FecQuicConnectionDebugVisitor
+ : public QuicConnectionDebugVisitor {
+ public:
+ virtual void OnRevivedPacket(const QuicPacketHeader& header,
+ StringPiece data) OVERRIDE {
+ revived_header_ = header;
+ }
+
+ // Public accessor method.
+ QuicPacketHeader revived_header() const {
+ return revived_header_;
+ }
+
+ private:
+ QuicPacketHeader revived_header_;
+};
+
+class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> {
protected:
QuicConnectionTest()
- : guid_(42),
- framer_(QuicSupportedVersions(), QuicTime::Zero(), false),
- creator_(guid_, &framer_, &random_generator_, false),
+ : connection_id_(42),
+ framer_(SupportedVersions(version()), QuicTime::Zero(), false),
+ peer_creator_(connection_id_, &framer_, &random_generator_),
send_algorithm_(new StrictMock<MockSendAlgorithm>),
+ loss_algorithm_(new MockLossAlgorithm()),
helper_(new TestConnectionHelper(&clock_, &random_generator_)),
- writer_(new TestPacketWriter()),
- connection_(guid_, IPEndPoint(), helper_.get(), writer_.get(), false),
+ writer_(new TestPacketWriter(version())),
+ connection_(connection_id_, IPEndPoint(), helper_.get(),
+ writer_.get(), false, version()),
frame1_(1, false, 0, MakeIOVector(data1)),
frame2_(1, false, 3, MakeIOVector(data2)),
- accept_packet_(true) {
+ sequence_number_length_(PACKET_6BYTE_SEQUENCE_NUMBER),
+ connection_id_length_(PACKET_8BYTE_CONNECTION_ID) {
connection_.set_visitor(&visitor_);
connection_.SetSendAlgorithm(send_algorithm_);
+ connection_.SetLossAlgorithm(loss_algorithm_);
framer_.set_received_entropy_calculator(&entropy_calculator_);
// Simplify tests by not sending feedback unless specifically configured.
SetFeedback(NULL);
EXPECT_CALL(
- *send_algorithm_, TimeUntilSend(_, _, _, _)).WillRepeatedly(Return(
+ *send_algorithm_, TimeUntilSend(_, _, _)).WillRepeatedly(Return(
QuicTime::Delta::Zero()));
EXPECT_CALL(*receive_algorithm_,
- RecordIncomingPacket(_, _, _, _)).Times(AnyNumber());
+ RecordIncomingPacket(_, _, _)).Times(AnyNumber());
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.Times(AnyNumber());
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly(Return(
- QuicBandwidth::FromKBitsPerSecond(100)));
- EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillRepeatedly(Return(
- QuicTime::Delta::FromMilliseconds(100)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize));
ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.WillByDefault(Return(true));
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).Times(AnyNumber());
EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
- EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber()).WillRepeatedly(
- Return(true));
- }
-
- QuicAckFrame* outgoing_ack() {
- outgoing_ack_.reset(QuicConnectionPeer::CreateAckFrame(&connection_));
- return outgoing_ack_.get();
- }
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber());
+ EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(false));
- QuicAckFrame* last_ack() {
- return writer_->ack();
+ EXPECT_CALL(*loss_algorithm_, GetLossTimeout())
+ .WillRepeatedly(Return(QuicTime::Zero()));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillRepeatedly(Return(SequenceNumberSet()));
}
- QuicCongestionFeedbackFrame* last_feedback() {
- return writer_->feedback();
+ QuicVersion version() {
+ return GetParam();
}
- QuicConnectionCloseFrame* last_close() {
- return writer_->close();
- }
-
- QuicPacketHeader* last_header() {
- return writer_->header();
- }
-
- size_t last_sent_packet_size() {
- return writer_->last_packet_size();
- }
-
- uint32 final_bytes_of_last_packet() {
- return writer_->final_bytes_of_last_packet();
+ QuicAckFrame* outgoing_ack() {
+ outgoing_ack_.reset(QuicConnectionPeer::CreateAckFrame(&connection_));
+ return outgoing_ack_.get();
}
- uint32 final_bytes_of_previous_packet() {
- return writer_->final_bytes_of_previous_packet();
+ QuicPacketSequenceNumber least_unacked() {
+ if (version() <= QUIC_VERSION_15) {
+ if (writer_->ack_frames().empty()) {
+ return 0;
+ }
+ return writer_->ack_frames()[0].sent_info.least_unacked;
+ }
+ if (writer_->stop_waiting_frames().empty()) {
+ return 0;
+ }
+ return writer_->stop_waiting_frames()[0].least_unacked;
}
void use_tagging_decrypter() {
@@ -577,16 +670,17 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
}
void ProcessPacket(QuicPacketSequenceNumber number) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
ProcessDataPacket(number, 0, !kEntropyFlag);
}
QuicPacketEntropyHash ProcessFramePacket(QuicFrame frame) {
QuicFrames frames;
frames.push_back(QuicFrame(frame));
- QuicPacketCreatorPeer::SetSendVersionInPacket(&creator_,
+ QuicPacketCreatorPeer::SetSendVersionInPacket(&peer_creator_,
connection_.is_server());
- SerializedPacket serialized_packet = creator_.SerializeAllFrames(frames);
+ SerializedPacket serialized_packet =
+ peer_creator_.SerializeAllFrames(frames);
scoped_ptr<QuicPacket> packet(serialized_packet.packet);
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE,
@@ -623,12 +717,12 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
}
size_t ProcessFecProtectedPacket(QuicPacketSequenceNumber number,
- bool expect_revival, bool entropy_flag) {
+ bool expect_revival, bool entropy_flag) {
if (expect_revival) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
}
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_))
- .RetiresOnSaturation();
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1).
+ RetiresOnSaturation();
return ProcessDataPacket(number, 1, entropy_flag);
}
@@ -640,7 +734,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
bool entropy_flag,
QuicPacket* packet) {
if (expect_revival) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
}
// Construct the decrypted data packet so we can compute the correct
@@ -653,12 +747,14 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
data_packet.reset(ConstructDataPacket(number, 1, !kEntropyFlag));
}
- header_.public_header.guid = guid_;
+ header_.public_header.connection_id = connection_id_;
header_.public_header.reset_flag = false;
header_.public_header.version_flag = false;
+ header_.public_header.sequence_number_length = sequence_number_length_;
+ header_.public_header.connection_id_length = connection_id_length_;
+ header_.packet_sequence_number = number;
header_.entropy_flag = entropy_flag;
header_.fec_flag = true;
- header_.packet_sequence_number = number;
header_.is_in_fec_group = IN_FEC_GROUP;
header_.fec_group = min_protected_packet;
QuicFecData fec_data;
@@ -669,7 +765,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
// with itself, depending on the number of packets.
if (((number - min_protected_packet) % 2) == 0) {
for (size_t i = GetStartOfFecProtectedData(
- header_.public_header.guid_length,
+ header_.public_header.connection_id_length,
header_.public_header.version_flag,
header_.public_header.sequence_number_length);
i < data_packet->length(); ++i) {
@@ -694,7 +790,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicPacketSequenceNumber* last_packet) {
QuicByteCount packet_size;
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .WillOnce(DoAll(SaveArg<2>(&packet_size), Return(true)));
+ .WillOnce(DoAll(SaveArg<3>(&packet_size), Return(true)));
connection_.SendStreamDataWithString(id, data, offset, fin, NULL);
if (last_packet != NULL) {
*last_packet =
@@ -716,6 +812,10 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
return ProcessFramePacket(QuicFrame(frame));
}
+ QuicPacketEntropyHash ProcessStopWaitingPacket(QuicStopWaitingFrame* frame) {
+ return ProcessFramePacket(QuicFrame(frame));
+ }
+
QuicPacketEntropyHash ProcessGoAwayPacket(QuicGoAwayFrame* frame) {
return ProcessFramePacket(QuicFrame(frame));
}
@@ -727,9 +827,11 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number,
QuicFecGroupNumber fec_group,
bool entropy_flag) {
- header_.public_header.guid = guid_;
+ header_.public_header.connection_id = connection_id_;
header_.public_header.reset_flag = false;
header_.public_header.version_flag = false;
+ header_.public_header.sequence_number_length = sequence_number_length_;
+ header_.public_header.connection_id_length = connection_id_length_;
header_.entropy_flag = entropy_flag;
header_.fec_flag = false;
header_.packet_sequence_number = number;
@@ -740,14 +842,14 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicFrame frame(&frame1_);
frames.push_back(frame);
QuicPacket* packet =
- framer_.BuildUnsizedDataPacket(header_, frames).packet;
+ BuildUnsizedDataPacket(&framer_, header_, frames).packet;
EXPECT_TRUE(packet != NULL);
return packet;
}
QuicPacket* ConstructClosePacket(QuicPacketSequenceNumber number,
QuicFecGroupNumber fec_group) {
- header_.public_header.guid = guid_;
+ header_.public_header.connection_id = connection_id_;
header_.packet_sequence_number = number;
header_.public_header.reset_flag = false;
header_.public_header.version_flag = false;
@@ -763,7 +865,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicFrame frame(&qccf);
frames.push_back(frame);
QuicPacket* packet =
- framer_.BuildUnsizedDataPacket(header_, frames).packet;
+ BuildUnsizedDataPacket(&framer_, header_, frames).packet;
EXPECT_TRUE(packet != NULL);
return packet;
}
@@ -781,12 +883,81 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2);
}
- QuicGuid guid_;
+ // Initialize a frame acknowledging all packets up to largest_observed.
+ const QuicAckFrame InitAckFrame(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber least_unacked) {
+ QuicAckFrame frame(MakeAckFrame(largest_observed, least_unacked));
+ if (largest_observed > 0) {
+ frame.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, largest_observed);
+ }
+ return frame;
+ }
+
+ const QuicStopWaitingFrame InitStopWaitingFrame(
+ QuicPacketSequenceNumber least_unacked) {
+ QuicStopWaitingFrame frame;
+ frame.least_unacked = least_unacked;
+ return frame;
+ }
+ // Explicitly nack a packet.
+ void NackPacket(QuicPacketSequenceNumber missing, QuicAckFrame* frame) {
+ frame->received_info.missing_packets.insert(missing);
+ frame->received_info.entropy_hash ^=
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, missing);
+ if (missing > 1) {
+ frame->received_info.entropy_hash ^=
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, missing - 1);
+ }
+ }
+
+ // Undo nacking a packet within the frame.
+ void AckPacket(QuicPacketSequenceNumber arrived, QuicAckFrame* frame) {
+ EXPECT_THAT(frame->received_info.missing_packets, Contains(arrived));
+ frame->received_info.missing_packets.erase(arrived);
+ frame->received_info.entropy_hash ^=
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, arrived);
+ if (arrived > 1) {
+ frame->received_info.entropy_hash ^=
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, arrived - 1);
+ }
+ }
+
+ void TriggerConnectionClose() {
+ // Send an erroneous packet to close the connection.
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
+ // Call ProcessDataPacket rather than ProcessPacket, as we should not get a
+ // packet call to the visitor.
+ ProcessDataPacket(6000, 0, !kEntropyFlag);
+ EXPECT_FALSE(
+ QuicConnectionPeer::GetConnectionClosePacket(&connection_) == NULL);
+ }
+
+ void BlockOnNextWrite() {
+ writer_->BlockOnNextWrite();
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1));
+ }
+
+ void CongestionBlockWrites() {
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::FromSeconds(1)));
+ }
+
+ void CongestionUnblockWrites() {
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::Zero()));
+ }
+
+ QuicConnectionId connection_id_;
QuicFramer framer_;
- QuicPacketCreator creator_;
+ QuicPacketCreator peer_creator_;
MockEntropyCalculator entropy_calculator_;
MockSendAlgorithm* send_algorithm_;
+ MockLossAlgorithm* loss_algorithm_;
TestReceiveAlgorithm* receive_algorithm_;
MockClock clock_;
MockRandom random_generator_;
@@ -799,13 +970,19 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> {
QuicStreamFrame frame1_;
QuicStreamFrame frame2_;
scoped_ptr<QuicAckFrame> outgoing_ack_;
- bool accept_packet_;
+ QuicSequenceNumberLength sequence_number_length_;
+ QuicConnectionIdLength connection_id_length_;
private:
DISALLOW_COPY_AND_ASSIGN(QuicConnectionTest);
};
-TEST_F(QuicConnectionTest, PacketsInOrder) {
+// Run all end to end tests with all supported versions.
+INSTANTIATE_TEST_CASE_P(SupportedVersion,
+ QuicConnectionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicConnectionTest, PacketsInOrder) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
@@ -821,21 +998,7 @@ TEST_F(QuicConnectionTest, PacketsInOrder) {
EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size());
}
-TEST_F(QuicConnectionTest, PacketsRejected) {
- EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-
- ProcessPacket(1);
- EXPECT_EQ(1u, outgoing_ack()->received_info.largest_observed);
- EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size());
-
- accept_packet_ = false;
- ProcessPacket(2);
- // We should not have an ack for two.
- EXPECT_EQ(1u, outgoing_ack()->received_info.largest_observed);
- EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size());
-}
-
-TEST_F(QuicConnectionTest, PacketsOutOfOrder) {
+TEST_P(QuicConnectionTest, PacketsOutOfOrder) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(3);
@@ -854,7 +1017,7 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrder) {
EXPECT_FALSE(IsMissing(1));
}
-TEST_F(QuicConnectionTest, DuplicatePacket) {
+TEST_P(QuicConnectionTest, DuplicatePacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(3);
@@ -870,7 +1033,7 @@ TEST_F(QuicConnectionTest, DuplicatePacket) {
EXPECT_TRUE(IsMissing(1));
}
-TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
+TEST_P(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(3);
@@ -891,8 +1054,9 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
// packet the peer will not retransmit. It indicates this by sending 'least
// awaiting' is 4. The connection should then realize 1 will not be
// retransmitted, and will remove it from the missing list.
- creator_.set_sequence_number(5);
- QuicAckFrame frame(0, QuicTime::Zero(), 4);
+ peer_creator_.set_sequence_number(5);
+ QuicAckFrame frame = InitAckFrame(1, 4);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _));
ProcessAckPacket(&frame);
// Force an ack to be sent.
@@ -900,31 +1064,53 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
EXPECT_TRUE(IsMissing(4));
}
-TEST_F(QuicConnectionTest, RejectPacketTooFarOut) {
+TEST_P(QuicConnectionTest, RejectPacketTooFarOut) {
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
// Call ProcessDataPacket rather than ProcessPacket, as we should not get a
// packet call to the visitor.
- EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
ProcessDataPacket(6000, 0, !kEntropyFlag);
+ EXPECT_FALSE(
+ QuicConnectionPeer::GetConnectionClosePacket(&connection_) == NULL);
}
-TEST_F(QuicConnectionTest, TruncatedAck) {
+TEST_P(QuicConnectionTest, RejectUnencryptedStreamData) {
+ // Process an unencrypted packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_UNENCRYPTED_STREAM_DATA,
+ false));
+ ProcessDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_FALSE(
+ QuicConnectionPeer::GetConnectionClosePacket(&connection_) == NULL);
+ const vector<QuicConnectionCloseFrame>& connection_close_frames =
+ writer_->connection_close_frames();
+ EXPECT_EQ(1u, connection_close_frames.size());
+ EXPECT_EQ(QUIC_UNENCRYPTED_STREAM_DATA,
+ connection_close_frames[0].error_code);
+}
+
+TEST_P(QuicConnectionTest, TruncatedAck) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacketSequenceNumber num_packets = 256 * 2 + 1;
for (QuicPacketSequenceNumber i = 0; i < num_packets; ++i) {
- SendStreamDataToPeer(1, "foo", i * 3, !kFin, NULL);
+ SendStreamDataToPeer(3, "foo", i * 3, !kFin, NULL);
}
- QuicAckFrame frame(num_packets, QuicTime::Zero(), 1);
+ QuicAckFrame frame = InitAckFrame(num_packets, 1);
+ SequenceNumberSet lost_packets;
// Create an ack with 256 nacks, none adjacent to one another.
for (QuicPacketSequenceNumber i = 1; i <= 256; ++i) {
- frame.received_info.missing_packets.insert(i * 2);
+ NackPacket(i * 2, &frame);
+ if (i < 256) { // Last packet is nacked, but not lost.
+ lost_packets.insert(i * 2);
+ }
}
- frame.received_info.entropy_hash = 0;
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
EXPECT_CALL(entropy_calculator_,
EntropyHash(511)).WillOnce(testing::Return(0));
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(256);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&frame);
QuicReceivedPacketManager* received_packet_manager =
@@ -933,40 +1119,40 @@ TEST_F(QuicConnectionTest, TruncatedAck) {
EXPECT_GT(num_packets,
received_packet_manager->peer_largest_observed_packet());
- frame.received_info.missing_packets.erase(192);
- frame.received_info.entropy_hash = 2;
+ AckPacket(192, &frame);
- // Removing one missing packet allows us to ack 192 and one more range.
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
+ // Removing one missing packet allows us to ack 192 and one more range, but
+ // 192 has already been declared lost, so it doesn't register as an ack.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&frame);
EXPECT_EQ(num_packets,
received_packet_manager->peer_largest_observed_packet());
}
-TEST_F(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) {
+TEST_P(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
// Delay sending, then queue up an ack.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
QuicConnectionPeer::SendAck(&connection_);
// Process an ack with a least unacked of the received ack.
// This causes an ack to be sent when TimeUntilSend returns 0.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
+ TimeUntilSend(_, _, _)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
// Skip a packet and then record an ack.
- creator_.set_sequence_number(2);
- QuicAckFrame frame(0, QuicTime::Zero(), 3);
+ peer_creator_.set_sequence_number(2);
+ QuicAckFrame frame = InitAckFrame(0, 3);
ProcessAckPacket(&frame);
}
-TEST_F(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) {
+TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(3);
@@ -986,52 +1172,70 @@ TEST_F(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) {
EXPECT_EQ(3u, writer_->packets_write_attempts());
}
-TEST_F(QuicConnectionTest, AckReceiptCausesAckSend) {
+TEST_P(QuicConnectionTest, AckReceiptCausesAckSend) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
+
QuicPacketSequenceNumber original;
QuicByteCount packet_size;
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&original), SaveArg<2>(&packet_size),
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&original), SaveArg<3>(&packet_size),
Return(true)));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
- QuicAckFrame frame(original, QuicTime::Zero(), 1);
- frame.received_info.missing_packets.insert(original);
- frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
- &connection_, original - 1);
+ QuicAckFrame frame = InitAckFrame(original, 1);
+ NackPacket(original, &frame);
// First nack triggers early retransmit.
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
QuicPacketSequenceNumber retransmission;
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, packet_size - kQuicVersionSize,
- NACK_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&retransmission), Return(true)));
+ OnPacketSent(_, _, _, packet_size - kQuicVersionSize, _))
+ .WillOnce(DoAll(SaveArg<2>(&retransmission), Return(true)));
ProcessAckPacket(&frame);
- QuicAckFrame frame2(retransmission, QuicTime::Zero(), 1);
- frame2.received_info.missing_packets.insert(original);
- frame2.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, retransmission) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, original);
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _));
-
+ QuicAckFrame frame2 = InitAckFrame(retransmission, 1);
+ NackPacket(original, &frame2);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
ProcessAckPacket(&frame2);
+
// Now if the peer sends an ack which still reports the retransmitted packet
- // as missing, then that will count as a packet which instigates an ack.
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
- ProcessAckPacket(&frame2);
+ // as missing, that will bundle an ack with data after two acks in a row
+ // indicate the high water mark needs to be raised.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _,
+ HAS_RETRANSMITTABLE_DATA));
+ connection_.SendStreamDataWithString(3, "foo", 3, !kFin, NULL);
+ // No ack sent.
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+
+ // No more packet loss for the rest of the test.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillRepeatedly(Return(SequenceNumberSet()));
ProcessAckPacket(&frame2);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _,
+ HAS_RETRANSMITTABLE_DATA));
+ connection_.SendStreamDataWithString(3, "foo", 3, !kFin, NULL);
+ // Ack bundled.
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ }
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(writer_->ack_frames().empty());
// But an ack with no missing packets will not send an ack.
- frame2.received_info.missing_packets.clear();
- frame2.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, retransmission);
+ AckPacket(original, &frame2);
ProcessAckPacket(&frame2);
ProcessAckPacket(&frame2);
}
-TEST_F(QuicConnectionTest, LeastUnackedLower) {
+TEST_P(QuicConnectionTest, LeastUnackedLower) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
@@ -1039,355 +1243,372 @@ TEST_F(QuicConnectionTest, LeastUnackedLower) {
SendStreamDataToPeer(1, "eep", 6, !kFin, NULL);
// Start out saying the least unacked is 2.
- creator_.set_sequence_number(5);
- QuicAckFrame frame(0, QuicTime::Zero(), 2);
- ProcessAckPacket(&frame);
+ peer_creator_.set_sequence_number(5);
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(2);
+ ProcessStopWaitingPacket(&frame);
+ } else {
+ QuicAckFrame frame = InitAckFrame(0, 2);
+ ProcessAckPacket(&frame);
+ }
// Change it to 1, but lower the sequence number to fake out-of-order packets.
// This should be fine.
- creator_.set_sequence_number(1);
- QuicAckFrame frame2(0, QuicTime::Zero(), 1);
- // The scheduler will not process out of order acks.
- EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
- ProcessAckPacket(&frame2);
+ peer_creator_.set_sequence_number(1);
+ // The scheduler will not process out of order acks, but all packet processing
+ // causes the connection to try to write.
+ EXPECT_CALL(visitor_, OnCanWrite());
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame2 = InitStopWaitingFrame(1);
+ ProcessStopWaitingPacket(&frame2);
+ } else {
+ QuicAckFrame frame2 = InitAckFrame(0, 1);
+ ProcessAckPacket(&frame2);
+ }
// Now claim it's one, but set the ordering so it was sent "after" the first
// one. This should cause a connection error.
- EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
- creator_.set_sequence_number(7);
- ProcessAckPacket(&frame2);
+ peer_creator_.set_sequence_number(7);
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_STOP_WAITING_DATA, false));
+ QuicStopWaitingFrame frame2 = InitStopWaitingFrame(1);
+ ProcessStopWaitingPacket(&frame2);
+ } else {
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false));
+ QuicAckFrame frame2 = InitAckFrame(0, 1);
+ ProcessAckPacket(&frame2);
+ }
}
-TEST_F(QuicConnectionTest, LargestObservedLower) {
+TEST_P(QuicConnectionTest, LargestObservedLower) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
SendStreamDataToPeer(1, "bar", 3, !kFin, NULL);
SendStreamDataToPeer(1, "eep", 6, !kFin, NULL);
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
// Start out saying the largest observed is 2.
- QuicAckFrame frame(2, QuicTime::Zero(), 0);
- frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
- &connection_, 2);
- ProcessAckPacket(&frame);
+ QuicAckFrame frame1 = InitAckFrame(1, 0);
+ QuicAckFrame frame2 = InitAckFrame(2, 0);
+ ProcessAckPacket(&frame2);
// Now change it to 1, and it should cause a connection error.
- QuicAckFrame frame2(1, QuicTime::Zero(), 0);
EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false));
EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
- ProcessAckPacket(&frame2);
+ ProcessAckPacket(&frame1);
}
-TEST_F(QuicConnectionTest, AckUnsentData) {
+TEST_P(QuicConnectionTest, AckUnsentData) {
// Ack a packet which has not been sent.
EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
- QuicAckFrame frame(1, QuicTime::Zero(), 0);
+ QuicAckFrame frame(MakeAckFrame(1, 0));
EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
ProcessAckPacket(&frame);
}
-TEST_F(QuicConnectionTest, AckAll) {
+TEST_P(QuicConnectionTest, AckAll) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
- creator_.set_sequence_number(1);
- QuicAckFrame frame1(0, QuicTime::Zero(), 1);
+ peer_creator_.set_sequence_number(1);
+ QuicAckFrame frame1 = InitAckFrame(0, 1);
ProcessAckPacket(&frame1);
}
-TEST_F(QuicConnectionTest, SendingDifferentSequenceNumberLengthsBandwidth) {
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return(
- QuicBandwidth::FromKBitsPerSecond(1000)));
-
+TEST_P(QuicConnectionTest, SendingDifferentSequenceNumberLengthsBandwidth) {
QuicPacketSequenceNumber last_packet;
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet);
EXPECT_EQ(1u, last_packet);
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return(
- QuicBandwidth::FromKBitsPerSecond(1000 * 256)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256));
SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet);
EXPECT_EQ(2u, last_packet);
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
// The 1 packet lag is due to the sequence number length being recalculated in
// QuicConnection after a packet is sent.
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return(
- QuicBandwidth::FromKBitsPerSecond(1000 * 256 * 256)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256 * 256));
SendStreamDataToPeer(1, "foo", 6, !kFin, &last_packet);
EXPECT_EQ(3u, last_packet);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return(
- QuicBandwidth::FromKBitsPerSecond(1000ll * 256 * 256 * 256)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256 * 256 * 256));
SendStreamDataToPeer(1, "bar", 9, !kFin, &last_packet);
EXPECT_EQ(4u, last_packet);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return(
- QuicBandwidth::FromKBitsPerSecond(1000ll * 256 * 256 * 256 * 256)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256 * 256 * 256 * 256));
SendStreamDataToPeer(1, "foo", 12, !kFin, &last_packet);
EXPECT_EQ(5u, last_packet);
EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
}
-TEST_F(QuicConnectionTest, SendingDifferentSequenceNumberLengthsUnackedDelta) {
+TEST_P(QuicConnectionTest, SendingDifferentSequenceNumberLengthsUnackedDelta) {
QuicPacketSequenceNumber last_packet;
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet);
EXPECT_EQ(1u, last_packet);
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number(100);
+ creator->set_sequence_number(100);
SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet);
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number(
- 100 * 256);
+ creator->set_sequence_number(100 * 256);
SendStreamDataToPeer(1, "foo", 6, !kFin, &last_packet);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number(
- 100 * 256 * 256);
+ creator->set_sequence_number(100 * 256 * 256);
SendStreamDataToPeer(1, "bar", 9, !kFin, &last_packet);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
- QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number(
- 100 * 256 * 256 * 256);
+ creator->set_sequence_number(100 * 256 * 256 * 256);
SendStreamDataToPeer(1, "foo", 12, !kFin, &last_packet);
EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
- connection_.options()->send_sequence_number_length);
+ creator->next_sequence_number_length());
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- last_header()->public_header.sequence_number_length);
+ writer_->header().public_header.sequence_number_length);
}
-TEST_F(QuicConnectionTest, BasicSending) {
+TEST_P(QuicConnectionTest, BasicSending) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(6);
QuicPacketSequenceNumber last_packet;
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
EXPECT_EQ(1u, last_packet);
SendAckPacketToPeer(); // Packet 2
- EXPECT_EQ(1u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(1u, least_unacked());
SendAckPacketToPeer(); // Packet 3
- EXPECT_EQ(1u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(1u, least_unacked());
SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet); // Packet 4
EXPECT_EQ(4u, last_packet);
SendAckPacketToPeer(); // Packet 5
- EXPECT_EQ(1u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(1u, least_unacked());
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
// Peer acks up to packet 3.
- QuicAckFrame frame(3, QuicTime::Zero(), 0);
- frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3);
+ QuicAckFrame frame = InitAckFrame(3, 0);
ProcessAckPacket(&frame);
SendAckPacketToPeer(); // Packet 6
// As soon as we've acked one, we skip ack packets 2 and 3 and note lack of
// ack for 4.
- EXPECT_EQ(4u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(4u, least_unacked());
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
// Peer acks up to packet 4, the last packet.
- QuicAckFrame frame2(6, QuicTime::Zero(), 0);
- frame2.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 6);
+ QuicAckFrame frame2 = InitAckFrame(6, 0);
ProcessAckPacket(&frame2); // Acks don't instigate acks.
// Verify that we did not send an ack.
- EXPECT_EQ(6u, last_header()->packet_sequence_number);
+ EXPECT_EQ(6u, writer_->header().packet_sequence_number);
// So the last ack has not changed.
- EXPECT_EQ(4u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(4u, least_unacked());
// If we force an ack, we shouldn't change our retransmit state.
SendAckPacketToPeer(); // Packet 7
- EXPECT_EQ(7u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(7u, least_unacked());
// But if we send more data it should.
SendStreamDataToPeer(1, "eep", 6, !kFin, &last_packet); // Packet 8
EXPECT_EQ(8u, last_packet);
SendAckPacketToPeer(); // Packet 9
- EXPECT_EQ(8u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(7u, least_unacked());
}
-TEST_F(QuicConnectionTest, FECSending) {
+TEST_P(QuicConnectionTest, FECSending) {
// All packets carry version info till version is negotiated.
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
size_t payload_length;
- connection_.options()->max_packet_length =
- GetPacketLengthForOneStream(
+ // GetPacketLengthForOneStream() assumes a stream offset of 0 in determining
+ // packet length. The size of the offset field in a stream frame is 0 for
+ // offset 0, and 2 for non-zero offsets up through 64K. Increase
+ // max_packet_length by 2 so that subsequent packets containing subsequent
+ // stream frames with non-zero offets will fit within the packet length.
+ size_t length = 2 + GetPacketLengthForOneStream(
connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
IN_FEC_GROUP, &payload_length);
- // And send FEC every two packets.
- connection_.options()->max_packets_per_fec_group = 2;
+ creator->set_max_packet_length(length);
+
+ // Enable FEC.
+ creator->set_max_packets_per_fec_group(2);
- // Send 4 data packets and 2 FEC packets.
+ // Send 4 protected data packets, which will also trigger 2 FEC packets.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6);
- // The first stream frame will consume 2 fewer bytes than the other three.
- const string payload(payload_length * 4 - 6, 'a');
- connection_.SendStreamDataWithString(1, payload, 0, !kFin, NULL);
+ // The first stream frame will have 2 fewer overhead bytes than the other 3.
+ const string payload(payload_length * 4 + 2, 'a');
+ connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, NULL);
// Expect the FEC group to be closed after SendStreamDataWithString.
- EXPECT_FALSE(creator_.ShouldSendFec(true));
+ EXPECT_FALSE(creator->IsFecGroupOpen());
+ EXPECT_FALSE(creator->IsFecProtected());
}
-TEST_F(QuicConnectionTest, FECQueueing) {
+TEST_P(QuicConnectionTest, FECQueueing) {
// All packets carry version info till version is negotiated.
size_t payload_length;
- connection_.options()->max_packet_length =
- GetPacketLengthForOneStream(
- connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
- IN_FEC_GROUP, &payload_length);
- // And send FEC every two packets.
- connection_.options()->max_packets_per_fec_group = 2;
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
+ size_t length = GetPacketLengthForOneStream(
+ connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
+ IN_FEC_GROUP, &payload_length);
+ creator->set_max_packet_length(length);
+ // Enable FEC.
+ creator->set_max_packets_per_fec_group(1);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
- writer_->set_blocked(true);
+ BlockOnNextWrite();
const string payload(payload_length, 'a');
- connection_.SendStreamDataWithString(1, payload, 0, !kFin, NULL);
- EXPECT_FALSE(creator_.ShouldSendFec(true));
+ connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, NULL);
+ EXPECT_FALSE(creator->IsFecGroupOpen());
+ EXPECT_FALSE(creator->IsFecProtected());
// Expect the first data packet and the fec packet to be queued.
EXPECT_EQ(2u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, AbandonFECFromCongestionWindow) {
- connection_.options()->max_packets_per_fec_group = 1;
+TEST_P(QuicConnectionTest, AbandonFECFromCongestionWindow) {
+ // Enable FEC.
+ QuicConnectionPeer::GetPacketCreator(
+ &connection_)->set_max_packets_per_fec_group(1);
+
// 1 Data and 1 FEC packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, NULL);
const QuicTime::Delta retransmission_time =
QuicTime::Delta::FromMilliseconds(5000);
clock_.AdvanceTime(retransmission_time);
// Abandon FEC packet and data packet.
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
EXPECT_CALL(visitor_, OnCanWrite());
connection_.OnRetransmissionTimeout();
}
-TEST_F(QuicConnectionTest, DontAbandonAckedFEC) {
+TEST_P(QuicConnectionTest, DontAbandonAckedFEC) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- connection_.options()->max_packets_per_fec_group = 1;
+ // Enable FEC.
+ QuicConnectionPeer::GetPacketCreator(
+ &connection_)->set_max_packets_per_fec_group(1);
// 1 Data and 1 FEC packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6);
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, NULL);
// Send some more data afterwards to ensure early retransmit doesn't trigger.
- connection_.SendStreamDataWithString(1, "foo", 3, !kFin, NULL);
- connection_.SendStreamDataWithString(1, "foo", 6, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 3, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 6, !kFin, NULL);
- QuicAckFrame ack_fec(2, QuicTime::Zero(), 1);
+ QuicAckFrame ack_fec = InitAckFrame(2, 1);
// Data packet missing.
// TODO(ianswett): Note that this is not a sensible ack, since if the FEC was
// received, it would cause the covered packet to be acked as well.
- ack_fec.received_info.missing_packets.insert(1);
- ack_fec.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
-
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
-
+ NackPacket(1, &ack_fec);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&ack_fec);
-
clock_.AdvanceTime(DefaultRetransmissionTime());
// Don't abandon the acked FEC packet, but it will abandon 2 the subsequent
// FEC packets.
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(5);
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
connection_.GetRetransmissionAlarm()->Fire();
}
-TEST_F(QuicConnectionTest, DontAbandonAllFEC) {
+TEST_P(QuicConnectionTest, AbandonAllFEC) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- connection_.options()->max_packets_per_fec_group = 1;
+ // Enable FEC.
+ QuicConnectionPeer::GetPacketCreator(
+ &connection_)->set_max_packets_per_fec_group(1);
// 1 Data and 1 FEC packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6);
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, NULL);
// Send some more data afterwards to ensure early retransmit doesn't trigger.
- connection_.SendStreamDataWithString(1, "foo", 3, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 3, !kFin, NULL);
// Advance the time so not all the FEC packets are abandoned.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
- connection_.SendStreamDataWithString(1, "foo", 6, !kFin, NULL);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 6, !kFin, NULL);
- QuicAckFrame ack_fec(5, QuicTime::Zero(), 1);
+ QuicAckFrame ack_fec = InitAckFrame(5, 1);
// Ack all data packets, but no fec packets.
- ack_fec.received_info.missing_packets.insert(2);
- ack_fec.received_info.missing_packets.insert(4);
- ack_fec.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 5) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
+ NackPacket(2, &ack_fec);
+ NackPacket(4, &ack_fec);
// Lose the first FEC packet and ack the three data packets.
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(3);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _));
- EXPECT_CALL(*send_algorithm_, OnPacketLost(2, _));
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&ack_fec);
clock_.AdvanceTime(DefaultRetransmissionTime().Subtract(
QuicTime::Delta::FromMilliseconds(1)));
- // Don't abandon the acked FEC packet, but it will abandon 1 of the subsequent
- // FEC packets.
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(4, _));
+ // Abandon all packets
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(false));
connection_.GetRetransmissionAlarm()->Fire();
- // Ensure the connection's alarm is still set, in order to abandon the third
- // FEC packet.
- EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ // Ensure the alarm is not set since all packets have been abandoned.
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, FramePacking) {
- // Block the connection.
- connection_.GetSendAlarm()->Set(
- clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1)));
+TEST_P(QuicConnectionTest, FramePacking) {
+ CongestionBlockWrites();
// Send an ack and two stream frames in 1 packet by queueing them.
connection_.SendAck();
@@ -1395,30 +1616,30 @@ TEST_F(QuicConnectionTest, FramePacking) {
IgnoreResult(InvokeWithoutArgs(&connection_,
&TestConnection::SendStreamData3)),
IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData5)),
- Return(true)));
+ &TestConnection::SendStreamData5))));
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .Times(1);
- // Unblock the connection.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ CongestionUnblockWrites();
connection_.GetSendAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
// Parse the last packet and ensure it's an ack and two stream frames from
// two different streams.
- EXPECT_EQ(3u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
- EXPECT_EQ(2u, writer_->stream_frames()->size());
- EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id);
- EXPECT_EQ(kStreamId5, (*writer_->stream_frames())[1].stream_id);
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(4u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(3u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ ASSERT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id);
}
-TEST_F(QuicConnectionTest, FramePackingNonCryptoThenCrypto) {
- // Block the connection.
- connection_.GetSendAlarm()->Set(
- clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1)));
+TEST_P(QuicConnectionTest, FramePackingNonCryptoThenCrypto) {
+ CongestionBlockWrites();
// Send an ack and two stream frames (one non-crypto, then one crypto) in 2
// packets by queueing them.
@@ -1427,100 +1648,88 @@ TEST_F(QuicConnectionTest, FramePackingNonCryptoThenCrypto) {
IgnoreResult(InvokeWithoutArgs(&connection_,
&TestConnection::SendStreamData3)),
IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendCryptoStreamData)),
- Return(true)));
+ &TestConnection::SendCryptoStreamData))));
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .Times(2);
- // Unblock the connection.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ CongestionUnblockWrites();
connection_.GetSendAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
// Parse the last packet and ensure it's the crypto stream frame.
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_EQ(1u, writer_->stream_frames()->size());
- EXPECT_EQ(kCryptoStreamId, (*writer_->stream_frames())[0].stream_id);
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(kCryptoStreamId, writer_->stream_frames()[0].stream_id);
}
-TEST_F(QuicConnectionTest, FramePackingCryptoThenNonCrypto) {
- // Block the connection.
- connection_.GetSendAlarm()->Set(
- clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1)));
+TEST_P(QuicConnectionTest, FramePackingCryptoThenNonCrypto) {
+ CongestionBlockWrites();
- // Send an ack and two stream frames (one crypto, then one non-crypto) in 3
+ // Send an ack and two stream frames (one crypto, then one non-crypto) in 2
// packets by queueing them.
connection_.SendAck();
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
IgnoreResult(InvokeWithoutArgs(&connection_,
&TestConnection::SendCryptoStreamData)),
IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData3)),
- Return(true)));
+ &TestConnection::SendStreamData3))));
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .Times(3);
- // Unblock the connection.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ CongestionUnblockWrites();
connection_.GetSendAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
// Parse the last packet and ensure it's the stream frame from stream 3.
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_EQ(1u, writer_->stream_frames()->size());
- EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id);
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
}
-TEST_F(QuicConnectionTest, FramePackingFEC) {
- // Enable fec.
- connection_.options()->max_packets_per_fec_group = 6;
- // Block the connection.
- connection_.GetSendAlarm()->Set(
- clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1)));
+TEST_P(QuicConnectionTest, FramePackingFEC) {
+ // Enable FEC.
+ QuicConnectionPeer::GetPacketCreator(
+ &connection_)->set_max_packets_per_fec_group(6);
- // Send an ack and two stream frames in 1 packet by queueing them.
- connection_.SendAck();
+ CongestionBlockWrites();
+
+ // Queue an ack and two stream frames. Ack gets flushed when FEC is turned on
+ // for sending protected data; two stream frames are packing in 1 packet.
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
- IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData3)),
- IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData5)),
- Return(true)));
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendStreamData3WithFec)),
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendStreamData5WithFec))));
+ connection_.SendAck();
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)).Times(2);
- // Unblock the connection.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
+ CongestionUnblockWrites();
connection_.GetSendAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
// Parse the last packet and ensure it's in an fec group.
- EXPECT_EQ(1u, writer_->header()->fec_group);
+ EXPECT_EQ(2u, writer_->header().fec_group);
EXPECT_EQ(0u, writer_->frame_count());
}
-TEST_F(QuicConnectionTest, FramePackingAckResponse) {
+TEST_P(QuicConnectionTest, FramePackingAckResponse) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Process a data packet to queue up a pending ack.
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
ProcessDataPacket(1, 1, kEntropyFlag);
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
IgnoreResult(InvokeWithoutArgs(&connection_,
&TestConnection::SendStreamData3)),
IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData5)),
- Return(true)));
+ &TestConnection::SendStreamData5))));
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .Times(1);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
// Process an ack to cause the visitor's OnCanWrite to be invoked.
- creator_.set_sequence_number(2);
- QuicAckFrame ack_one(0, QuicTime::Zero(), 0);
+ peer_creator_.set_sequence_number(2);
+ QuicAckFrame ack_one = InitAckFrame(0, 0);
ProcessAckPacket(&ack_one);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -1528,23 +1737,28 @@ TEST_F(QuicConnectionTest, FramePackingAckResponse) {
// Parse the last packet and ensure it's an ack and two stream frames from
// two different streams.
- EXPECT_EQ(3u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
- ASSERT_EQ(2u, writer_->stream_frames()->size());
- EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id);
- EXPECT_EQ(kStreamId5, (*writer_->stream_frames())[1].stream_id);
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(4u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(3u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ ASSERT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id);
}
-TEST_F(QuicConnectionTest, FramePackingSendv) {
+TEST_P(QuicConnectionTest, FramePackingSendv) {
// Send data in 1 packet by writing multiple blocks in a single iovector
// using writev.
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
char data[] = "ABCD";
IOVector data_iov;
data_iov.AppendNoCoalesce(data, 2);
data_iov.AppendNoCoalesce(data + 2, 2);
- connection_.SendStreamData(1, data_iov, 0, !kFin, NULL);
+ connection_.SendStreamData(1, data_iov, 0, !kFin, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
@@ -1552,87 +1766,78 @@ TEST_F(QuicConnectionTest, FramePackingSendv) {
// Parse the last packet and ensure multiple iovector blocks have
// been packed into a single stream frame from one stream.
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_EQ(1u, writer_->stream_frames()->size());
- QuicStreamFrame frame = (*writer_->stream_frames())[0];
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ QuicStreamFrame frame = writer_->stream_frames()[0];
EXPECT_EQ(1u, frame.stream_id);
EXPECT_EQ("ABCD", string(static_cast<char*>
(frame.data.iovec()[0].iov_base),
(frame.data.iovec()[0].iov_len)));
}
-TEST_F(QuicConnectionTest, FramePackingSendvQueued) {
+TEST_P(QuicConnectionTest, FramePackingSendvQueued) {
// Try to send two stream frames in 1 packet by using writev.
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
- writer_->set_blocked(true);
+ BlockOnNextWrite();
char data[] = "ABCD";
IOVector data_iov;
data_iov.AppendNoCoalesce(data, 2);
data_iov.AppendNoCoalesce(data + 2, 2);
- connection_.SendStreamData(1, data_iov, 0, !kFin, NULL);
+ connection_.SendStreamData(1, data_iov, 0, !kFin, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
EXPECT_TRUE(connection_.HasQueuedData());
- // Attempt to send all packets, but since we're actually still
- // blocked, they should all remain queued.
- EXPECT_FALSE(connection_.OnCanWrite());
- EXPECT_EQ(1u, connection_.NumQueuedPackets());
-
// Unblock the writes and actually send.
- writer_->set_blocked(false);
- EXPECT_TRUE(connection_.OnCanWrite());
+ writer_->SetWritable();
+ connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
// Parse the last packet and ensure it's one stream frame from one stream.
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_EQ(1u, writer_->stream_frames()->size());
- EXPECT_EQ(1u, (*writer_->stream_frames())[0].stream_id);
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(1u, writer_->stream_frames()[0].stream_id);
}
-TEST_F(QuicConnectionTest, SendingZeroBytes) {
+TEST_P(QuicConnectionTest, SendingZeroBytes) {
// Send a zero byte write with a fin using writev.
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
IOVector empty_iov;
- connection_.SendStreamData(1, empty_iov, 0, kFin, NULL);
+ connection_.SendStreamData(1, empty_iov, 0, kFin, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
// Parse the last packet and ensure it's one stream frame from one stream.
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_EQ(1u, writer_->stream_frames()->size());
- EXPECT_EQ(1u, (*writer_->stream_frames())[0].stream_id);
- EXPECT_TRUE((*writer_->stream_frames())[0].fin);
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(1u, writer_->stream_frames()[0].stream_id);
+ EXPECT_TRUE(writer_->stream_frames()[0].fin);
}
-TEST_F(QuicConnectionTest, OnCanWrite) {
- // Visitor's OnCanWill send data, but will return false.
+TEST_P(QuicConnectionTest, OnCanWrite) {
+ // Visitor's OnCanWrite will send data, but will have more pending writes.
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
IgnoreResult(InvokeWithoutArgs(&connection_,
&TestConnection::SendStreamData3)),
IgnoreResult(InvokeWithoutArgs(&connection_,
- &TestConnection::SendStreamData5)),
- Return(false)));
-
+ &TestConnection::SendStreamData5))));
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillOnce(Return(true));
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
+ TimeUntilSend(_, _, _)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
- // Unblock the connection.
connection_.OnCanWrite();
+
// Parse the last packet and ensure it's the two stream frames from
// two different streams.
EXPECT_EQ(2u, writer_->frame_count());
- EXPECT_EQ(2u, writer_->stream_frames()->size());
- EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id);
- EXPECT_EQ(kStreamId5, (*writer_->stream_frames())[1].stream_id);
+ EXPECT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id);
}
-TEST_F(QuicConnectionTest, RetransmitOnNack) {
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _)).Times(1);
+TEST_P(QuicConnectionTest, RetransmitOnNack) {
QuicPacketSequenceNumber last_packet;
QuicByteCount second_packet_size;
SendStreamDataToPeer(3, "foo", 0, !kFin, &last_packet); // Packet 1
@@ -1642,34 +1847,26 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- // Peer acks one but not two or three. Right now we only retransmit on
- // explicit nack, so it should not trigger a retransmission.
- QuicAckFrame ack_one(1, QuicTime::Zero(), 0);
- ack_one.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- ProcessAckPacket(&ack_one);
- ProcessAckPacket(&ack_one);
+ // Don't lose a packet on an ack, and nothing is retransmitted.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame ack_one = InitAckFrame(1, 0);
ProcessAckPacket(&ack_one);
- // Peer acks up to 3 with two explicitly missing.
- // Early retransmit causes 2 to be retransmitted on the first ack.
- QuicAckFrame nack_two(3, QuicTime::Zero(), 0);
- nack_two.received_info.missing_packets.insert(2);
- nack_two.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- // The third nack should trigger a retransmission.
+ // Lose a packet and ensure it triggers retransmission.
+ QuicAckFrame nack_two = InitAckFrame(3, 0);
+ NackPacket(2, &nack_two);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, second_packet_size - kQuicVersionSize,
- NACK_RETRANSMISSION, _)).Times(1);
+ OnPacketSent(_, _, _, second_packet_size - kQuicVersionSize, _)).
+ Times(1);
ProcessAckPacket(&nack_two);
}
-TEST_F(QuicConnectionTest, DiscardRetransmit) {
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _)).Times(1);
+TEST_P(QuicConnectionTest, DiscardRetransmit) {
QuicPacketSequenceNumber last_packet;
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
SendStreamDataToPeer(1, "foos", 3, !kFin, &last_packet); // Packet 2
@@ -1677,34 +1874,24 @@ TEST_F(QuicConnectionTest, DiscardRetransmit) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- // Peer acks one but not two or three. Right now we only retransmit on
- // explicit nack, so it should not trigger a retransmission.
- QuicAckFrame ack_one(1, QuicTime::Zero(), 0);
- ack_one.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- ProcessAckPacket(&ack_one);
- ProcessAckPacket(&ack_one);
- ProcessAckPacket(&ack_one);
-
- // Peer acks up to 3 with two explicitly missing. Two nacks should cause no
- // change.
- QuicAckFrame nack_two(3, QuicTime::Zero(), 0);
- nack_two.received_info.missing_packets.insert(2);
- nack_two.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
+ // Instigate a loss with an ack.
+ QuicAckFrame nack_two = InitAckFrame(3, 0);
+ NackPacket(2, &nack_two);
// The first nack should trigger a fast retransmission, but we'll be
// write blocked, so the packet will be queued.
- writer_->set_blocked(true);
-
+ BlockOnNextWrite();
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&nack_two);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// Now, ack the previous transmission.
- QuicAckFrame ack_all(3, QuicTime::Zero(), 0);
- ack_all.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ QuicAckFrame ack_all = InitAckFrame(3, 0);
ProcessAckPacket(&ack_all);
// Unblock the socket and attempt to send the queued packets. However,
@@ -1713,50 +1900,50 @@ TEST_F(QuicConnectionTest, DiscardRetransmit) {
EXPECT_CALL(*send_algorithm_,
OnPacketSent(_, _, _, _, _)).Times(0);
- writer_->set_blocked(false);
+ writer_->SetWritable();
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) {
+TEST_P(QuicConnectionTest, RetransmitNackedLargestObserved) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
QuicPacketSequenceNumber largest_observed;
QuicByteCount packet_size;
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size),
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&largest_observed), SaveArg<3>(&packet_size),
Return(true)));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
- QuicAckFrame frame(1, QuicTime::Zero(), largest_observed);
- frame.received_info.missing_packets.insert(largest_observed);
- frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
- &connection_, largest_observed - 1);
+
+ QuicAckFrame frame = InitAckFrame(1, largest_observed);
+ NackPacket(largest_observed, &frame);
// The first nack should retransmit the largest observed packet.
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, packet_size - kQuicVersionSize,
- NACK_RETRANSMISSION, _));
+ OnPacketSent(_, _, _, packet_size - kQuicVersionSize, _));
ProcessAckPacket(&frame);
}
-TEST_F(QuicConnectionTest, QueueAfterTwoRTOs) {
+TEST_P(QuicConnectionTest, QueueAfterTwoRTOs) {
for (int i = 0; i < 10; ++i) {
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
- connection_.SendStreamDataWithString(1, "foo", i * 3, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "foo", i * 3, !kFin, NULL);
}
// Block the congestion window and ensure they're queued.
- writer_->set_blocked(true);
+ BlockOnNextWrite();
clock_.AdvanceTime(DefaultRetransmissionTime());
// Only one packet should be retransmitted.
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(10);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
connection_.GetRetransmissionAlarm()->Fire();
EXPECT_TRUE(connection_.HasQueuedData());
// Unblock the congestion window.
- writer_->set_blocked(false);
+ writer_->SetWritable();
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(
2 * DefaultRetransmissionTime().ToMicroseconds()));
// Retransmit already retransmitted packets event though the sequence number
@@ -1766,9 +1953,8 @@ TEST_F(QuicConnectionTest, QueueAfterTwoRTOs) {
connection_.OnCanWrite();
}
-TEST_F(QuicConnectionTest, WriteBlockedThenSent) {
- writer_->set_blocked(true);
-
+TEST_P(QuicConnectionTest, WriteBlockedThenSent) {
+ BlockOnNextWrite();
writer_->set_is_write_blocked_data_buffered(true);
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
@@ -1778,22 +1964,66 @@ TEST_F(QuicConnectionTest, WriteBlockedThenSent) {
EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, ResumptionAlarmThenWriteBlocked) {
- // Set the send and resumption alarm, then block the connection.
+TEST_P(QuicConnectionTest, WriteBlockedAckedThenSent) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ // Ack the sent packet before the callback returns, which happens in
+ // rare circumstances with write blocked sockets.
+ QuicAckFrame ack = InitAckFrame(1, 0);
+ ProcessAckPacket(&ack);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ connection_.OnPacketSent(WriteResult(WRITE_STATUS_OK, 0));
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, RetransmitWriteBlockedAckedOriginalThenSent) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ // Simulate the retransmission alarm firing.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(_));
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Ack the sent packet before the callback returns, which happens in
+ // rare circumstances with write blocked sockets.
+ QuicAckFrame ack = InitAckFrame(1, 0);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&ack);
+
+ connection_.OnPacketSent(WriteResult(WRITE_STATUS_OK, 0));
+ // There is now a pending packet, but with no retransmittable frames.
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(connection_.sent_packet_manager().HasRetransmittableFrames(2));
+}
+
+TEST_P(QuicConnectionTest, AlarmsWhenWriteBlocked) {
+ // Block the connection.
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+
+ // Set the send and resumption alarms. Fire the alarms and ensure they don't
+ // attempt to write.
connection_.GetResumeWritesAlarm()->Set(clock_.ApproximateNow());
connection_.GetSendAlarm()->Set(clock_.ApproximateNow());
- QuicConnectionPeer::SetIsWriteBlocked(&connection_, true);
-
- // Fire the alarms and ensure the connection is still write blocked.
connection_.GetResumeWritesAlarm()->Fire();
connection_.GetSendAlarm()->Fire();
- EXPECT_TRUE(QuicConnectionPeer::IsWriteBlocked(&connection_));
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
}
-TEST_F(QuicConnectionTest, LimitPacketsPerNack) {
+TEST_P(QuicConnectionTest, NoLimitPacketsPerNack) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(15, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(4);
int offset = 0;
// Send packets 1 to 15.
for (int i = 0; i < 15; ++i) {
@@ -1802,29 +2032,24 @@ TEST_F(QuicConnectionTest, LimitPacketsPerNack) {
}
// Ack 15, nack 1-14.
- QuicAckFrame nack(15, QuicTime::Zero(), 0);
+ SequenceNumberSet lost_packets;
+ QuicAckFrame nack = InitAckFrame(15, 0);
for (int i = 1; i < 15; ++i) {
- nack.received_info.missing_packets.insert(i);
+ NackPacket(i, &nack);
+ lost_packets.insert(i);
}
- nack.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 15) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 14);
-
- // 13 packets have been NACK'd 3 times, but we limit retransmissions to 2.
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
- ProcessAckPacket(&nack);
-
- // The next call should trigger retransmitting 2 more packets.
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ // 14 packets have been NACK'd and lost. In TCP cubic, PRR limits
+ // the retransmission rate in the case of burst losses.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(14);
ProcessAckPacket(&nack);
}
// Test sending multiple acks from the connection to the session.
-TEST_F(QuicConnectionTest, MultipleAcks) {
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(6);
+TEST_P(QuicConnectionTest, MultipleAcks) {
QuicPacketSequenceNumber last_packet;
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
EXPECT_EQ(1u, last_packet);
@@ -1839,55 +2064,79 @@ TEST_F(QuicConnectionTest, MultipleAcks) {
EXPECT_EQ(6u, last_packet);
// Client will ack packets 1, 2, [!3], 4, 5.
- QuicAckFrame frame1(5, QuicTime::Zero(), 0);
- frame1.received_info.missing_packets.insert(3);
- frame1.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 5) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2);
-
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame1 = InitAckFrame(5, 0);
+ NackPacket(3, &frame1);
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-
ProcessAckPacket(&frame1);
// Now the client implicitly acks 3, and explicitly acks 6.
- QuicAckFrame frame2(6, QuicTime::Zero(), 0);
- frame2.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 6);
-
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame2 = InitAckFrame(6, 0);
ProcessAckPacket(&frame2);
}
-TEST_F(QuicConnectionTest, DontLatchUnackedPacket) {
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
+TEST_P(QuicConnectionTest, DontLatchUnackedPacket) {
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); // Packet 1;
+ // From now on, we send acks, so the send algorithm won't mark them pending.
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(false));
SendAckPacketToPeer(); // Packet 2
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- QuicAckFrame frame(1, QuicTime::Zero(), 0);
- frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
- &connection_, 1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame = InitAckFrame(1, 0);
ProcessAckPacket(&frame);
- // Verify that our internal state has least-unacked as 3.
+ // Verify that our internal state has least-unacked as 2, because we're still
+ // waiting for a potential ack for 2.
+ EXPECT_EQ(2u, outgoing_ack()->sent_info.least_unacked);
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ frame = InitAckFrame(2, 0);
+ ProcessAckPacket(&frame);
EXPECT_EQ(3u, outgoing_ack()->sent_info.least_unacked);
// When we send an ack, we make sure our least-unacked makes sense. In this
// case since we're not waiting on an ack for 2 and all packets are acked, we
// set it to 3.
SendAckPacketToPeer(); // Packet 3
- // Since this was an ack packet, we set least_unacked to 4.
- EXPECT_EQ(4u, outgoing_ack()->sent_info.least_unacked);
+ // Least_unacked remains at 3 until another ack is received.
+ EXPECT_EQ(3u, outgoing_ack()->sent_info.least_unacked);
// Check that the outgoing ack had its sequence number as least_unacked.
- EXPECT_EQ(3u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(3u, least_unacked());
+ // Ack the ack, which updates the rtt and raises the least unacked.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ frame = InitAckFrame(3, 0);
+ ProcessAckPacket(&frame);
+
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(true));
SendStreamDataToPeer(1, "bar", 3, false, NULL); // Packet 4
EXPECT_EQ(4u, outgoing_ack()->sent_info.least_unacked);
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(false));
SendAckPacketToPeer(); // Packet 5
- EXPECT_EQ(4u, last_ack()->sent_info.least_unacked);
+ EXPECT_EQ(4u, least_unacked());
+
+ // Send two data packets at the end, and ensure if the last one is acked,
+ // the least unacked is raised above the ack packets.
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(true));
+ SendStreamDataToPeer(1, "bar", 6, false, NULL); // Packet 6
+ SendStreamDataToPeer(1, "bar", 9, false, NULL); // Packet 7
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ frame = InitAckFrame(7, 0);
+ NackPacket(5, &frame);
+ NackPacket(6, &frame);
+ ProcessAckPacket(&frame);
+
+ EXPECT_EQ(6u, outgoing_ack()->sent_info.least_unacked);
}
-TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) {
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Don't send missing packet 1.
@@ -1896,7 +2145,63 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) {
EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
-TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
+TEST_P(QuicConnectionTest, ReviveMissingPacketWithVaryingSeqNumLengths) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Set up a debug visitor to the connection.
+ scoped_ptr<FecQuicConnectionDebugVisitor>
+ fec_visitor(new FecQuicConnectionDebugVisitor);
+ connection_.set_debug_visitor(fec_visitor.get());
+
+ QuicPacketSequenceNumber fec_packet = 0;
+ QuicSequenceNumberLength lengths[] = {PACKET_6BYTE_SEQUENCE_NUMBER,
+ PACKET_4BYTE_SEQUENCE_NUMBER,
+ PACKET_2BYTE_SEQUENCE_NUMBER,
+ PACKET_1BYTE_SEQUENCE_NUMBER};
+ // For each sequence number length size, revive a packet and check sequence
+ // number length in the revived packet.
+ for (size_t i = 0; i < arraysize(lengths); ++i) {
+ // Set sequence_number_length_ (for data and FEC packets).
+ sequence_number_length_ = lengths[i];
+ fec_packet += 2;
+ // Don't send missing packet, but send fec packet right after it.
+ ProcessFecPacket(fec_packet, fec_packet - 1, true, !kEntropyFlag, NULL);
+ // Sequence number length in the revived header should be the same as
+ // in the original data/fec packet headers.
+ EXPECT_EQ(sequence_number_length_, fec_visitor->revived_header().
+ public_header.sequence_number_length);
+ }
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketWithVaryingConnectionIdLengths) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Set up a debug visitor to the connection.
+ scoped_ptr<FecQuicConnectionDebugVisitor>
+ fec_visitor(new FecQuicConnectionDebugVisitor);
+ connection_.set_debug_visitor(fec_visitor.get());
+
+ QuicPacketSequenceNumber fec_packet = 0;
+ QuicConnectionIdLength lengths[] = {PACKET_8BYTE_CONNECTION_ID,
+ PACKET_4BYTE_CONNECTION_ID,
+ PACKET_1BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID};
+ // For each connection id length size, revive a packet and check connection
+ // id length in the revived packet.
+ for (size_t i = 0; i < arraysize(lengths); ++i) {
+ // Set connection id length (for data and FEC packets).
+ connection_id_length_ = lengths[i];
+ fec_packet += 2;
+ // Don't send missing packet, but send fec packet right after it.
+ ProcessFecPacket(fec_packet, fec_packet - 1, true, !kEntropyFlag, NULL);
+ // Connection id length in the revived header should be the same as
+ // in the original data/fec packet headers.
+ EXPECT_EQ(connection_id_length_,
+ fec_visitor->revived_header().public_header.connection_id_length);
+ }
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessFecProtectedPacket(1, false, kEntropyFlag);
@@ -1906,18 +2211,19 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
-TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) {
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessFecProtectedPacket(1, false, !kEntropyFlag);
// Don't send missing packet 2.
ProcessFecProtectedPacket(3, false, !kEntropyFlag);
ProcessFecPacket(4, 1, true, kEntropyFlag, NULL);
- // Entropy flag should be true, so entropy should not be 0.
- EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ // Ensure QUIC no longer revives entropy for lost packets.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 4));
}
-TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Don't send missing packet 1.
@@ -1928,7 +2234,7 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
-TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessFecProtectedPacket(1, false, !kEntropyFlag);
@@ -1937,31 +2243,52 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
ProcessFecProtectedPacket(3, false, kEntropyFlag);
ProcessFecProtectedPacket(4, false, kEntropyFlag);
ProcessFecProtectedPacket(5, true, !kEntropyFlag);
- // Entropy flag should be true, so entropy should be 0.
- EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ // Ensure entropy is not revived for the missing packet.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 3));
}
-TEST_F(QuicConnectionTest, RTO) {
+TEST_P(QuicConnectionTest, TLP) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicConnectionPeer::GetSentPacketManager(&connection_), 1);
+
+ SendStreamDataToPeer(3, "foo", 0, !kFin, NULL);
+ EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
+ QuicTime retransmission_time =
+ connection_.GetRetransmissionAlarm()->deadline();
+ EXPECT_NE(QuicTime::Zero(), retransmission_time);
+
+ EXPECT_EQ(1u, writer_->header().packet_sequence_number);
+ // Simulate the retransmission alarm firing and sending a tlp,
+ // so send algorithm's OnRetransmissionTimeout is not called.
+ clock_.AdvanceTime(retransmission_time.Subtract(clock_.Now()));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(2u, writer_->header().packet_sequence_number);
+ // We do not raise the high water mark yet.
+ EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
+}
+
+TEST_P(QuicConnectionTest, RTO) {
QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
DefaultRetransmissionTime());
- SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
+ SendStreamDataToPeer(3, "foo", 0, !kFin, NULL);
EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
- EXPECT_EQ(1u, last_header()->packet_sequence_number);
+ EXPECT_EQ(1u, writer_->header().packet_sequence_number);
EXPECT_EQ(default_retransmission_time,
connection_.GetRetransmissionAlarm()->deadline());
// Simulate the retransmission alarm firing.
clock_.AdvanceTime(DefaultRetransmissionTime());
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1u, _));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 2u, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
connection_.GetRetransmissionAlarm()->Fire();
- EXPECT_EQ(2u, last_header()->packet_sequence_number);
+ EXPECT_EQ(2u, writer_->header().packet_sequence_number);
// We do not raise the high water mark yet.
EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
}
-TEST_F(QuicConnectionTest, RTOWithSameEncryptionLevel) {
+TEST_P(QuicConnectionTest, RTOWithSameEncryptionLevel) {
QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
DefaultRetransmissionTime());
use_tagging_decrypter();
@@ -1969,23 +2296,21 @@ TEST_F(QuicConnectionTest, RTOWithSameEncryptionLevel) {
// A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at
// the end of the packet. We can test this to check which encrypter was used.
connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
- SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
- EXPECT_EQ(0x01010101u, final_bytes_of_last_packet());
+ SendStreamDataToPeer(3, "foo", 0, !kFin, NULL);
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(0x02));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
- SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
- EXPECT_EQ(0x02020202u, final_bytes_of_last_packet());
+ SendStreamDataToPeer(3, "foo", 0, !kFin, NULL);
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
EXPECT_EQ(default_retransmission_time,
connection_.GetRetransmissionAlarm()->deadline());
{
InSequence s;
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 3, _, RTO_RETRANSMISSION, _));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 4, _, RTO_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 3, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 4, _, _));
}
// Simulate the retransmission alarm firing.
@@ -1993,23 +2318,23 @@ TEST_F(QuicConnectionTest, RTOWithSameEncryptionLevel) {
connection_.GetRetransmissionAlarm()->Fire();
// Packet should have been sent with ENCRYPTION_NONE.
- EXPECT_EQ(0x01010101u, final_bytes_of_previous_packet());
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_previous_packet());
// Packet should have been sent with ENCRYPTION_INITIAL.
- EXPECT_EQ(0x02020202u, final_bytes_of_last_packet());
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
}
-TEST_F(QuicConnectionTest, SendHandshakeMessages) {
+TEST_P(QuicConnectionTest, SendHandshakeMessages) {
use_tagging_decrypter();
// A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at
// the end of the packet. We can test this to check which encrypter was used.
connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
- // Attempt to send a handshake message while the congestion manager
- // does not permit sending.
+ // Attempt to send a handshake message and have the socket block.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, _, _, IS_HANDSHAKE)).WillRepeatedly(
- testing::Return(QuicTime::Delta::Infinite()));
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::Zero()));
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
// The packet should be serialized, but not queued.
EXPECT_EQ(1u, connection_.NumQueuedPackets());
@@ -2019,43 +2344,43 @@ TEST_F(QuicConnectionTest, SendHandshakeMessages) {
connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
// Now become writeable and flush the packets.
- EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, _, _, IS_HANDSHAKE)).WillRepeatedly(
- testing::Return(QuicTime::Delta::Zero()));
+ writer_->SetWritable();
EXPECT_CALL(visitor_, OnCanWrite());
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
// Verify that the handshake packet went out at the null encryption.
- EXPECT_EQ(0x01010101u, final_bytes_of_last_packet());
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
}
-TEST_F(QuicConnectionTest,
+TEST_P(QuicConnectionTest,
DropRetransmitsForNullEncryptedPacketAfterForwardSecure) {
use_tagging_decrypter();
connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
QuicPacketSequenceNumber sequence_number;
- SendStreamDataToPeer(1, "foo", 0, !kFin, &sequence_number);
+ SendStreamDataToPeer(3, "foo", 0, !kFin, &sequence_number);
+ // Simulate the retransmission alarm firing and the socket blocking.
+ BlockOnNextWrite();
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Go forward secure.
connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
new TaggingEncrypter(0x02));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ connection_.NeuterUnencryptedPackets();
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(sequence_number, _)).Times(1);
-
- QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
- DefaultRetransmissionTime());
-
- EXPECT_EQ(default_retransmission_time,
+ EXPECT_EQ(QuicTime::Zero(),
connection_.GetRetransmissionAlarm()->deadline());
- // Simulate the retransmission alarm firing.
- clock_.AdvanceTime(DefaultRetransmissionTime());
- connection_.GetRetransmissionAlarm()->Fire();
+ // Unblock the socket and ensure that no packets are sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ writer_->SetWritable();
+ connection_.OnCanWrite();
}
-TEST_F(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) {
+TEST_P(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) {
use_tagging_decrypter();
connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_NONE);
@@ -2066,14 +2391,12 @@ TEST_F(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) {
connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
SendStreamDataToPeer(2, "bar", 0, !kFin, NULL);
-
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(1);
connection_.RetransmitUnackedPackets(INITIAL_ENCRYPTION_ONLY);
}
-TEST_F(QuicConnectionTest, BufferNonDecryptablePackets) {
+TEST_P(QuicConnectionTest, BufferNonDecryptablePackets) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
use_tagging_decrypter();
@@ -2082,68 +2405,66 @@ TEST_F(QuicConnectionTest, BufferNonDecryptablePackets) {
// Process an encrypted packet which can not yet be decrypted
// which should result in the packet being buffered.
- ProcessDataPacketAtLevel(1, false, kEntropyFlag, ENCRYPTION_INITIAL);
+ ProcessDataPacketAtLevel(1, 0, kEntropyFlag, ENCRYPTION_INITIAL);
// Transition to the new encryption state and process another
// encrypted packet which should result in the original packet being
// processed.
- connection_.SetDecrypter(new StrictTaggingDecrypter(tag));
+ connection_.SetDecrypter(new StrictTaggingDecrypter(tag),
+ ENCRYPTION_INITIAL);
connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag));
- EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(2).WillRepeatedly(
- Return(true));
- ProcessDataPacketAtLevel(2, false, kEntropyFlag, ENCRYPTION_INITIAL);
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(2);
+ ProcessDataPacketAtLevel(2, 0, kEntropyFlag, ENCRYPTION_INITIAL);
// Finally, process a third packet and note that we do not
// reprocess the buffered packet.
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(true));
- ProcessDataPacketAtLevel(3, false, kEntropyFlag, ENCRYPTION_INITIAL);
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ ProcessDataPacketAtLevel(3, 0, kEntropyFlag, ENCRYPTION_INITIAL);
}
-TEST_F(QuicConnectionTest, TestRetransmitOrder) {
+TEST_P(QuicConnectionTest, TestRetransmitOrder) {
QuicByteCount first_packet_size;
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillOnce(
- DoAll(SaveArg<2>(&first_packet_size), Return(true)));
+ DoAll(SaveArg<3>(&first_packet_size), Return(true)));
connection_.SendStreamDataWithString(3, "first_packet", 0, !kFin, NULL);
QuicByteCount second_packet_size;
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillOnce(
- DoAll(SaveArg<2>(&second_packet_size), Return(true)));
+ DoAll(SaveArg<3>(&second_packet_size), Return(true)));
connection_.SendStreamDataWithString(3, "second_packet", 12, !kFin, NULL);
EXPECT_NE(first_packet_size, second_packet_size);
// Advance the clock by huge time to make sure packets will be retransmitted.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
{
InSequence s;
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, first_packet_size, _, _));
+ OnPacketSent(_, _, _, first_packet_size, _));
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, second_packet_size, _, _));
+ OnPacketSent(_, _, _, second_packet_size, _));
}
connection_.GetRetransmissionAlarm()->Fire();
// Advance again and expect the packets to be sent again in the same order.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(20));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
{
InSequence s;
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, first_packet_size, _, _));
+ OnPacketSent(_, _, _, first_packet_size, _));
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, second_packet_size, _, _));
+ OnPacketSent(_, _, _, second_packet_size, _));
}
connection_.GetRetransmissionAlarm()->Fire();
}
-TEST_F(QuicConnectionTest, RetransmissionCountCalculation) {
+TEST_P(QuicConnectionTest, RetransmissionCountCalculation) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacketSequenceNumber original_sequence_number;
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&original_sequence_number), Return(true)));
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&original_sequence_number), Return(true)));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, original_sequence_number));
@@ -2151,12 +2472,10 @@ TEST_F(QuicConnectionTest, RetransmissionCountCalculation) {
&connection_, original_sequence_number));
// Force retransmission due to RTO.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_,
- OnPacketAbandoned(original_sequence_number, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
QuicPacketSequenceNumber rto_sequence_number;
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, RTO_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&rto_sequence_number), Return(true)));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&rto_sequence_number), Return(true)));
connection_.GetRetransmissionAlarm()->Fire();
EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, original_sequence_number));
@@ -2165,28 +2484,24 @@ TEST_F(QuicConnectionTest, RetransmissionCountCalculation) {
EXPECT_TRUE(QuicConnectionPeer::IsRetransmission(
&connection_, rto_sequence_number));
// Once by explicit nack.
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
- EXPECT_CALL(*send_algorithm_,
- OnPacketAbandoned(rto_sequence_number, _)).Times(1);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(rto_sequence_number);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
QuicPacketSequenceNumber nack_sequence_number = 0;
// Ack packets might generate some other packets, which are not
// retransmissions. (More ack packets).
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.Times(AnyNumber());
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NACK_RETRANSMISSION, _))
- .WillOnce(DoAll(SaveArg<1>(&nack_sequence_number), Return(true)));
- QuicAckFrame ack(rto_sequence_number, QuicTime::Zero(), 0);
- // Ack the retransmitted packet.
- ack.received_info.missing_packets.insert(original_sequence_number);
- ack.received_info.missing_packets.insert(rto_sequence_number);
- ack.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_,
- rto_sequence_number - 1) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_,
- original_sequence_number);
- for (int i = 0; i < 3; i++) {
- ProcessAckPacket(&ack);
- }
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&nack_sequence_number), Return(true)));
+ QuicAckFrame ack = InitAckFrame(rto_sequence_number, 0);
+ // Nack the retransmitted packet.
+ NackPacket(original_sequence_number, &ack);
+ NackPacket(rto_sequence_number, &ack);
+ ProcessAckPacket(&ack);
+
ASSERT_NE(0u, nack_sequence_number);
EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, rto_sequence_number));
@@ -2196,48 +2511,46 @@ TEST_F(QuicConnectionTest, RetransmissionCountCalculation) {
&connection_, nack_sequence_number));
}
-TEST_F(QuicConnectionTest, SetRTOAfterWritingToSocket) {
- writer_->set_blocked(true);
+TEST_P(QuicConnectionTest, SetRTOAfterWritingToSocket) {
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
// Make sure that RTO is not started when the packet is queued.
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
// Test that RTO is started once we write to the socket.
- writer_->set_blocked(false);
+ writer_->SetWritable();
connection_.OnCanWrite();
EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, DelayRTOWithAckReceipt) {
+TEST_P(QuicConnectionTest, DelayRTOWithAckReceipt) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _))
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.Times(2);
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
- connection_.SendStreamDataWithString(2, "bar", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(2, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "bar", 0, !kFin, NULL);
QuicAlarm* retransmission_alarm = connection_.GetRetransmissionAlarm();
EXPECT_TRUE(retransmission_alarm->IsSet());
+ EXPECT_EQ(clock_.Now().Add(DefaultRetransmissionTime()),
+ retransmission_alarm->deadline());
// Advance the time right before the RTO, then receive an ack for the first
// packet to delay the RTO.
clock_.AdvanceTime(DefaultRetransmissionTime());
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
- QuicAckFrame ack(1, QuicTime::Zero(), 0);
- ack.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame ack = InitAckFrame(1, 0);
ProcessAckPacket(&ack);
EXPECT_TRUE(retransmission_alarm->IsSet());
+ EXPECT_GT(retransmission_alarm->deadline(), clock_.Now());
// Move forward past the original RTO and ensure the RTO is still pending.
- clock_.AdvanceTime(DefaultRetransmissionTime());
+ clock_.AdvanceTime(DefaultRetransmissionTime().Multiply(2));
// Ensure the second packet gets retransmitted when it finally fires.
EXPECT_TRUE(retransmission_alarm->IsSet());
- EXPECT_GE(retransmission_alarm->deadline(), clock_.ApproximateNow());
- clock_.AdvanceTime(DefaultRetransmissionTime());
EXPECT_LT(retransmission_alarm->deadline(), clock_.ApproximateNow());
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, RTO_RETRANSMISSION, _));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
// Manually cancel the alarm to simulate a real test.
connection_.GetRetransmissionAlarm()->Fire();
@@ -2245,29 +2558,24 @@ TEST_F(QuicConnectionTest, DelayRTOWithAckReceipt) {
// than previously.
EXPECT_TRUE(retransmission_alarm->IsSet());
QuicTime next_rto_time = retransmission_alarm->deadline();
- QuicTime::Delta expected_rto =
- connection_.sent_packet_manager().GetRetransmissionDelay();
- EXPECT_EQ(next_rto_time, clock_.ApproximateNow().Add(expected_rto));
+ QuicTime expected_rto_time =
+ connection_.sent_packet_manager().GetRetransmissionTime();
+ EXPECT_EQ(next_rto_time, expected_rto_time);
}
-TEST_F(QuicConnectionTest, TestQueued) {
+TEST_P(QuicConnectionTest, TestQueued) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
- writer_->set_blocked(true);
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
- // Attempt to send all packets, but since we're actually still
- // blocked, they should all remain queued.
- EXPECT_FALSE(connection_.OnCanWrite());
- EXPECT_EQ(1u, connection_.NumQueuedPackets());
-
// Unblock the writes and actually send.
- writer_->set_blocked(false);
- EXPECT_TRUE(connection_.OnCanWrite());
+ writer_->SetWritable();
+ connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, CloseFecGroup) {
+TEST_P(QuicConnectionTest, CloseFecGroup) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Don't send missing packet 1.
// Don't send missing packet 2.
@@ -2276,45 +2584,52 @@ TEST_F(QuicConnectionTest, CloseFecGroup) {
ASSERT_EQ(1u, connection_.NumFecGroups());
// Now send non-fec protected ack packet and close the group.
- QuicAckFrame frame(0, QuicTime::Zero(), 5);
- creator_.set_sequence_number(4);
- ProcessAckPacket(&frame);
+ peer_creator_.set_sequence_number(4);
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(5);
+ ProcessStopWaitingPacket(&frame);
+ } else {
+ QuicAckFrame frame = InitAckFrame(0, 5);
+ ProcessAckPacket(&frame);
+ }
ASSERT_EQ(0u, connection_.NumFecGroups());
}
-TEST_F(QuicConnectionTest, NoQuicCongestionFeedbackFrame) {
+TEST_P(QuicConnectionTest, NoQuicCongestionFeedbackFrame) {
SendAckPacketToPeer();
- EXPECT_TRUE(last_feedback() == NULL);
+ EXPECT_TRUE(writer_->feedback_frames().empty());
}
-TEST_F(QuicConnectionTest, WithQuicCongestionFeedbackFrame) {
+TEST_P(QuicConnectionTest, WithQuicCongestionFeedbackFrame) {
QuicCongestionFeedbackFrame info;
info.type = kFixRate;
info.fix_rate.bitrate = QuicBandwidth::FromBytesPerSecond(123);
SetFeedback(&info);
SendAckPacketToPeer();
- EXPECT_EQ(kFixRate, last_feedback()->type);
- EXPECT_EQ(info.fix_rate.bitrate, last_feedback()->fix_rate.bitrate);
+ ASSERT_FALSE(writer_->feedback_frames().empty());
+ ASSERT_EQ(kFixRate, writer_->feedback_frames()[0].type);
+ ASSERT_EQ(info.fix_rate.bitrate,
+ writer_->feedback_frames()[0].fix_rate.bitrate);
}
-TEST_F(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) {
+TEST_P(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) {
SendAckPacketToPeer();
- EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _));
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
}
-TEST_F(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) {
+TEST_P(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
SendAckPacketToPeer();
// Process an FEC packet, and revive the missing data packet
// but only contact the receive_algorithm once.
- EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _));
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _));
ProcessFecPacket(2, 1, true, !kEntropyFlag, NULL);
}
-TEST_F(QuicConnectionTest, InitialTimeout) {
+TEST_P(QuicConnectionTest, InitialTimeout) {
EXPECT_TRUE(connection_.connected());
EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_CONNECTION_TIMED_OUT, false));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
@@ -2331,13 +2646,59 @@ TEST_F(QuicConnectionTest, InitialTimeout) {
EXPECT_FALSE(connection_.connected());
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
EXPECT_FALSE(connection_.GetResumeWritesAlarm()->IsSet());
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, TimeoutAfterSend) {
+TEST_P(QuicConnectionTest, PingAfterSend) {
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(true));
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+
+ // Advance to 5ms, and send a packet to the peer, which will set
+ // the ping alarm.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ SendStreamDataToPeer(1, "GET /", 0, kFin, NULL);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(15)),
+ connection_.GetPingAlarm()->deadline());
+
+ // Now recevie and ACK of the previous packet, which will move the
+ // ping alarm forward.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ QuicAckFrame frame = InitAckFrame(1, 0);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(15)),
+ connection_.GetPingAlarm()->deadline());
+
+ writer_->Reset();
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(15));
+ connection_.GetPingAlarm()->Fire();
+ EXPECT_EQ(1u, writer_->frame_count());
+ if (version() > QUIC_VERSION_17) {
+ ASSERT_EQ(1u, writer_->ping_frames().size());
+ } else {
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(kCryptoStreamId, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(0u, writer_->stream_frames()[0].offset);
+ }
+ writer_->Reset();
+
+ EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(false));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ SendAckPacketToPeer();
+
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, TimeoutAfterSend) {
EXPECT_TRUE(connection_.connected());
QuicTime default_timeout = clock_.ApproximateNow().Add(
@@ -2373,12 +2734,11 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) {
EXPECT_FALSE(connection_.connected());
}
-// TODO(ianswett): Add scheduler tests when should_retransmit is false.
-TEST_F(QuicConnectionTest, SendScheduler) {
+TEST_P(QuicConnectionTest, SendScheduler) {
// Test that if we send a packet without delay, it is not queued.
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
connection_.SendPacket(
@@ -2386,47 +2746,35 @@ TEST_F(QuicConnectionTest, SendScheduler) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelay) {
+TEST_P(QuicConnectionTest, SendSchedulerDelay) {
// Test that if we send a packet with a delay, it ends up queued.
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerForce) {
- // Test that if we force send a packet, it is not queued.
+TEST_P(QuicConnectionTest, SendSchedulerEAGAIN) {
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ BlockOnNextWrite();
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NACK_RETRANSMISSION, _, _)).Times(0);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
- connection_.SendPacket(
- ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
- // XXX: fixme. was: connection_.SendPacket(1, packet, kForce);
- EXPECT_EQ(0u, connection_.NumQueuedPackets());
-}
-
-TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) {
- QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
- writer_->set_blocked(true);
- EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) {
+TEST_P(QuicConnectionTest, SendSchedulerDelayThenSend) {
// Test that if we send a packet with a delay, it ends up queued.
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
@@ -2435,7 +2783,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) {
// Advance the clock to fire the alarm, and configure the scheduler
// to permit the packet to be sent.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
+ TimeUntilSend(_, _, _)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
@@ -2443,43 +2791,35 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) {
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _))
- .WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, 1, _, NOT_RETRANSMISSION, _));
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
+TEST_P(QuicConnectionTest, SendSchedulerDelayThenRetransmit) {
+ CongestionUnblockWrites();
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
// Advance the time for retransmission of lost packet.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(501));
// Test that if we send a retransmit with a delay, it ends up queued in the
// sent packet manager, but not yet serialized.
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, RTO_RETRANSMISSION, _, _)).WillOnce(
- testing::Return(QuicTime::Delta::FromMicroseconds(1)));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ CongestionBlockWrites();
connection_.GetRetransmissionAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
// Advance the clock to fire the alarm, and configure the scheduler
// to permit the packet to be sent.
- EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, RTO_RETRANSMISSION, _, _)).Times(2).
- WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
+ CongestionUnblockWrites();
// Ensure the scheduler is notified this is a retransmit.
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, RTO_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1));
connection_.GetSendAlarm()->Fire();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) {
+TEST_P(QuicConnectionTest, SendSchedulerDelayAndQueue) {
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
@@ -2492,11 +2832,11 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) {
EXPECT_EQ(2u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
+TEST_P(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(10)));
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
@@ -2504,9 +2844,9 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
// Now send non-retransmitting information, that we're not going to
// retransmit 3. The far end should stop waiting for it.
- QuicAckFrame frame(0, QuicTime::Zero(), 1);
+ QuicAckFrame frame = InitAckFrame(0, 1);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
+ TimeUntilSend(_, _, _)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_,
OnPacketSent(_, _, _, _, _));
@@ -2517,11 +2857,11 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) {
+TEST_P(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(10)));
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
@@ -2529,41 +2869,42 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) {
// Now send non-retransmitting information, that we're not going to
// retransmit 3. The far end should stop waiting for it.
- QuicAckFrame frame(0, QuicTime::Zero(), 1);
+ QuicAckFrame frame = InitAckFrame(0, 1);
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
ProcessAckPacket(&frame);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) {
+TEST_P(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) {
+ // TODO(ianswett): This test is unrealistic, because we would not serialize
+ // new data if the send algorithm said not to.
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
- EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
- testing::Return(QuicTime::Delta::FromMicroseconds(10)));
+ CongestionBlockWrites();
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
- // OnCanWrite should not send the packet (because of the delay)
- // but should still return true.
- EXPECT_TRUE(connection_.OnCanWrite());
- EXPECT_EQ(1u, connection_.NumQueuedPackets());
+ // OnCanWrite should send the packet, because it won't consult the send
+ // algorithm for queued packets.
+ connection_.OnCanWrite();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) {
+TEST_P(QuicConnectionTest, TestQueueLimitsOnSendStreamData) {
// All packets carry version info till version is negotiated.
size_t payload_length;
- connection_.options()->max_packet_length =
- GetPacketLengthForOneStream(
- connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
- NOT_IN_FEC_GROUP, &payload_length);
+ size_t length = GetPacketLengthForOneStream(
+ connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
+ NOT_IN_FEC_GROUP, &payload_length);
+ QuicConnectionPeer::GetPacketCreator(&connection_)->set_max_packet_length(
+ length);
// Queue the first packet.
EXPECT_CALL(*send_algorithm_,
- TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ TimeUntilSend(_, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(10)));
const string payload(payload_length, 'a');
EXPECT_EQ(0u,
@@ -2572,27 +2913,66 @@ TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
-TEST_F(QuicConnectionTest, LoopThroughSendingPackets) {
+TEST_P(QuicConnectionTest, LoopThroughSendingPackets) {
// All packets carry version info till version is negotiated.
size_t payload_length;
- connection_.options()->max_packet_length =
- GetPacketLengthForOneStream(
+ // GetPacketLengthForOneStream() assumes a stream offset of 0 in determining
+ // packet length. The size of the offset field in a stream frame is 0 for
+ // offset 0, and 2 for non-zero offsets up through 16K. Increase
+ // max_packet_length by 2 so that subsequent packets containing subsequent
+ // stream frames with non-zero offets will fit within the packet length.
+ size_t length = 2 + GetPacketLengthForOneStream(
connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
NOT_IN_FEC_GROUP, &payload_length);
+ QuicConnectionPeer::GetPacketCreator(&connection_)->set_max_packet_length(
+ length);
// Queue the first packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(7);
- // The first stream frame will consume 2 fewer bytes than the other six.
- const string payload(payload_length * 7 - 12, 'a');
+ // The first stream frame will have 2 fewer overhead bytes than the other six.
+ const string payload(payload_length * 7 + 2, 'a');
EXPECT_EQ(payload.size(),
connection_.SendStreamDataWithString(1, payload, 0,
!kFin, NULL).bytes_consumed);
}
-TEST_F(QuicConnectionTest, SendDelayedAckOnTimer) {
+TEST_P(QuicConnectionTest, SendDelayedAck) {
QuicTime ack_time = clock_.ApproximateNow().Add(DefaultDelayedAckTime());
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8 tag = 0x07;
+ connection_.SetDecrypter(new StrictTaggingDecrypter(tag),
+ ENCRYPTION_INITIAL);
+ framer_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+ // instead of ENCRYPTION_NONE.
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ ProcessDataPacketAtLevel(1, 0, !kEntropyFlag, ENCRYPTION_INITIAL);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+ // Simulate delayed ack alarm firing.
+ connection_.GetAckAlarm()->Fire();
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(1u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendEarlyDelayedAckForCrypto) {
+ QuicTime ack_time = clock_.ApproximateNow();
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // Process a packet from the crypto stream, which is frame1_'s default.
ProcessPacket(1);
// Check if delayed ack timer is running for the expected interval.
EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -2600,90 +2980,185 @@ TEST_F(QuicConnectionTest, SendDelayedAckOnTimer) {
// Simulate delayed ack alarm firing.
connection_.GetAckAlarm()->Fire();
// Check that ack is sent and that delayed ack alarm is reset.
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(1u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, SendDelayedAckOnSecondPacket) {
+TEST_P(QuicConnectionTest, SendDelayedAckOnSecondPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
ProcessPacket(2);
// Check that ack is sent and that delayed ack alarm is reset.
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(1u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, NoAckOnOldNacks) {
+TEST_P(QuicConnectionTest, NoAckOnOldNacks) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Drop one packet, triggering a sequence of acks.
ProcessPacket(2);
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ size_t frames_per_ack = version() > QUIC_VERSION_15 ? 2 : 1;
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
writer_->Reset();
ProcessPacket(3);
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
writer_->Reset();
ProcessPacket(4);
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
writer_->Reset();
ProcessPacket(5);
- EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
- // Now only set the timer on the 6th packet, instead of sending another ack.
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
writer_->Reset();
+ // Now only set the timer on the 6th packet, instead of sending another ack.
ProcessPacket(6);
EXPECT_EQ(0u, writer_->frame_count());
EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) {
+TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
- connection_.SendStreamDataWithString(kStreamId3, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 0,
+ !kFin, NULL);
// Check that ack is bundled with outgoing data and that delayed ack
// alarm is reset.
- EXPECT_EQ(2u, writer_->frame_count());
- EXPECT_TRUE(writer_->ack());
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, DontSendDelayedAckOnOutgoingCryptoPacket) {
+TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingCryptoPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
connection_.SendStreamDataWithString(kCryptoStreamId, "foo", 0, !kFin, NULL);
- // Check that ack is not bundled with outgoing data.
+ // Check that ack is bundled with outgoing crypto data.
+ EXPECT_EQ(version() <= QUIC_VERSION_15 ? 2u : 3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, BundleAckForSecondCHLO) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendCryptoStreamData)));
+ // Process a packet from the crypto stream, which is frame1_'s default.
+ // Receiving the CHLO as packet 2 first will cause the connection to
+ // immediately send an ack, due to the packet gap.
+ ProcessPacket(2);
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ }
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, BundleAckWithDataOnIncomingAck) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 0,
+ !kFin, NULL);
+ connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 3,
+ !kFin, NULL);
+ // Ack the second packet, which will retransmit the first packet.
+ QuicAckFrame ack = InitAckFrame(2, 0);
+ NackPacket(1, &ack);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&ack);
EXPECT_EQ(1u, writer_->frame_count());
- EXPECT_FALSE(writer_->ack());
- EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ writer_->Reset();
+
+ // Now ack the retransmission, which will both raise the high water mark
+ // and see if there is more data to send.
+ ack = InitAckFrame(3, 0);
+ NackPacket(1, &ack);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&ack);
+
+ // Check that no packet is sent and the ack alarm isn't set.
+ EXPECT_EQ(0u, writer_->frame_count());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ writer_->Reset();
+
+ // Send the same ack, but send both data and an ack together.
+ ack = InitAckFrame(3, 0);
+ NackPacket(1, &ack);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_,
+ &TestConnection::EnsureWritableAndSendStreamData5)));
+ ProcessAckPacket(&ack);
+
+ // Check that ack is bundled with outgoing data and the delayed ack
+ // alarm is reset.
+ if (version() > QUIC_VERSION_15) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
}
-TEST_F(QuicConnectionTest, NoAckForClose) {
+TEST_P(QuicConnectionTest, NoAckSentForClose) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(0);
EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, true));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
ProcessClosePacket(2, 0);
}
-TEST_F(QuicConnectionTest, SendWhenDisconnected) {
+TEST_P(QuicConnectionTest, SendWhenDisconnected) {
EXPECT_TRUE(connection_.connected());
EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, false));
connection_.CloseConnection(QUIC_PEER_GOING_AWAY, false);
EXPECT_FALSE(connection_.connected());
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
connection_.SendPacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
}
-TEST_F(QuicConnectionTest, PublicReset) {
+TEST_P(QuicConnectionTest, PublicReset) {
QuicPublicResetPacket header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = true;
header.public_header.version_flag = false;
header.rejected_sequence_number = 10101;
@@ -2693,7 +3168,7 @@ TEST_F(QuicConnectionTest, PublicReset) {
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *packet);
}
-TEST_F(QuicConnectionTest, GoAway) {
+TEST_P(QuicConnectionTest, GoAway) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicGoAwayFrame goaway;
@@ -2704,27 +3179,52 @@ TEST_F(QuicConnectionTest, GoAway) {
ProcessGoAwayPacket(&goaway);
}
-TEST_F(QuicConnectionTest, InvalidPacket) {
+TEST_P(QuicConnectionTest, WindowUpdate) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicWindowUpdateFrame window_update;
+ window_update.stream_id = 3;
+ window_update.byte_offset = 1234;
+ EXPECT_CALL(visitor_, OnWindowUpdateFrames(_));
+ ProcessFramePacket(QuicFrame(&window_update));
+}
+
+TEST_P(QuicConnectionTest, Blocked) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicBlockedFrame blocked;
+ blocked.stream_id = 3;
+ EXPECT_CALL(visitor_, OnBlockedFrames(_));
+ ProcessFramePacket(QuicFrame(&blocked));
+}
+
+TEST_P(QuicConnectionTest, InvalidPacket) {
EXPECT_CALL(visitor_,
OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
QuicEncryptedPacket encrypted(NULL, 0);
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), encrypted);
// The connection close packet should have error details.
- ASSERT_TRUE(last_close() != NULL);
- EXPECT_EQ("Unable to read public flags.", last_close()->error_details);
+ ASSERT_FALSE(writer_->connection_close_frames().empty());
+ EXPECT_EQ("Unable to read public flags.",
+ writer_->connection_close_frames()[0].error_details);
}
-TEST_F(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) {
- QuicAckFrame ack(0, QuicTime::Zero(), 4);
+TEST_P(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) {
// Set the sequence number of the ack packet to be least unacked (4).
- creator_.set_sequence_number(3);
+ peer_creator_.set_sequence_number(3);
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- ProcessAckPacket(&ack);
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(4);
+ ProcessStopWaitingPacket(&frame);
+ } else {
+ QuicAckFrame ack = InitAckFrame(0, 4);
+ ProcessAckPacket(&ack);
+ }
EXPECT_TRUE(outgoing_ack()->received_info.missing_packets.empty());
}
-TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+TEST_P(QuicConnectionTest, ReceivedEntropyHashCalculation) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(4, 1, kEntropyFlag);
@@ -2733,40 +3233,66 @@ TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) {
EXPECT_EQ(146u, outgoing_ack()->received_info.entropy_hash);
}
-TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+TEST_P(QuicConnectionTest, ReceivedEntropyHashCalculationHalfFEC) {
+ // FEC packets should not change the entropy hash calculation.
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessDataPacket(1, 1, kEntropyFlag);
+ ProcessFecPacket(4, 1, false, kEntropyFlag, NULL);
+ ProcessDataPacket(3, 3, !kEntropyFlag);
+ ProcessFecPacket(7, 3, false, kEntropyFlag, NULL);
+ EXPECT_EQ(146u, outgoing_ack()->received_info.entropy_hash);
+}
+
+TEST_P(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(5, 1, kEntropyFlag);
ProcessDataPacket(4, 1, !kEntropyFlag);
EXPECT_EQ(34u, outgoing_ack()->received_info.entropy_hash);
// Make 4th packet my least unacked, and update entropy for 2, 3 packets.
- QuicAckFrame ack(0, QuicTime::Zero(), 4);
- QuicPacketEntropyHash kRandomEntropyHash = 129u;
- ack.sent_info.entropy_hash = kRandomEntropyHash;
- creator_.set_sequence_number(5);
+ peer_creator_.set_sequence_number(5);
QuicPacketEntropyHash six_packet_entropy_hash = 0;
- if (ProcessAckPacket(&ack)) {
- six_packet_entropy_hash = 1 << 6;
+ QuicPacketEntropyHash kRandomEntropyHash = 129u;
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(4);
+ frame.entropy_hash = kRandomEntropyHash;
+ if (ProcessStopWaitingPacket(&frame)) {
+ six_packet_entropy_hash = 1 << 6;
+ }
+ } else {
+ QuicAckFrame ack = InitAckFrame(0, 4);
+ ack.sent_info.entropy_hash = kRandomEntropyHash;
+ if (ProcessAckPacket(&ack)) {
+ six_packet_entropy_hash = 1 << 6;
+ }
}
EXPECT_EQ((kRandomEntropyHash + (1 << 5) + six_packet_entropy_hash),
outgoing_ack()->received_info.entropy_hash);
}
-TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+TEST_P(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(5, 1, !kEntropyFlag);
ProcessDataPacket(22, 1, kEntropyFlag);
EXPECT_EQ(66u, outgoing_ack()->received_info.entropy_hash);
- creator_.set_sequence_number(22);
+ peer_creator_.set_sequence_number(22);
QuicPacketEntropyHash kRandomEntropyHash = 85u;
// Current packet is the least unacked packet.
- QuicAckFrame ack(0, QuicTime::Zero(), 23);
- ack.sent_info.entropy_hash = kRandomEntropyHash;
- QuicPacketEntropyHash ack_entropy_hash = ProcessAckPacket(&ack);
+ QuicPacketEntropyHash ack_entropy_hash;
+ if (version() > QUIC_VERSION_15) {
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(23);
+ frame.entropy_hash = kRandomEntropyHash;
+ ack_entropy_hash = ProcessStopWaitingPacket(&frame);
+ } else {
+ QuicAckFrame ack = InitAckFrame(0, 23);
+ ack.sent_info.entropy_hash = kRandomEntropyHash;
+ ack_entropy_hash = ProcessAckPacket(&ack);
+ }
EXPECT_EQ((kRandomEntropyHash + ack_entropy_hash),
outgoing_ack()->received_info.entropy_hash);
ProcessDataPacket(25, 1, kEntropyFlag);
@@ -2774,13 +3300,13 @@ TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
outgoing_ack()->received_info.entropy_hash);
}
-TEST_F(QuicConnectionTest, EntropyCalculationForTruncatedAck) {
- EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+TEST_P(QuicConnectionTest, EntropyCalculationForTruncatedAck) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacketEntropyHash entropy[51];
entropy[0] = 0;
for (int i = 1; i < 51; ++i) {
- bool should_send = i % 10 != 0;
+ bool should_send = i % 10 != 1;
bool entropy_flag = (i & (i - 1)) != 0;
if (!should_send) {
entropy[i] = entropy[i - 1];
@@ -2793,15 +3319,14 @@ TEST_F(QuicConnectionTest, EntropyCalculationForTruncatedAck) {
}
ProcessDataPacket(i, 1, entropy_flag);
}
- // Till 50 since 50th packet is not sent.
for (int i = 1; i < 50; ++i) {
EXPECT_EQ(entropy[i], QuicConnectionPeer::ReceivedEntropyHash(
&connection_, i));
}
}
-TEST_F(QuicConnectionTest, CheckSentEntropyHash) {
- creator_.set_sequence_number(1);
+TEST_P(QuicConnectionTest, CheckSentEntropyHash) {
+ peer_creator_.set_sequence_number(1);
SequenceNumberSet missing_packets;
QuicPacketEntropyHash entropy_hash = 0;
QuicPacketSequenceNumber max_sequence_number = 51;
@@ -2829,11 +3354,12 @@ TEST_F(QuicConnectionTest, CheckSentEntropyHash) {
<< "";
}
-TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacket) {
+TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacket) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED);
QuicPacketHeader header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.entropy_flag = false;
@@ -2845,17 +3371,17 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacket) {
QuicFrame frame(&frame1_);
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
- framer_.set_version(QuicVersionMax());
+ framer_.set_version(version());
connection_.set_is_server(true);
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
EXPECT_TRUE(writer_->version_negotiation_packet() != NULL);
size_t num_versions = arraysize(kSupportedQuicVersions);
- EXPECT_EQ(num_versions,
+ ASSERT_EQ(num_versions,
writer_->version_negotiation_packet()->versions.size());
// We expect all versions in kSupportedQuicVersions to be
@@ -2866,11 +3392,12 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacket) {
}
}
-TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) {
+TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED);
QuicPacketHeader header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.entropy_flag = false;
@@ -2882,24 +3409,23 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) {
QuicFrame frame(&frame1_);
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
- framer_.set_version(QuicVersionMax());
+ framer_.set_version(version());
connection_.set_is_server(true);
- writer_->set_blocked(true);
+ BlockOnNextWrite();
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
EXPECT_EQ(0u, writer_->last_packet_size());
EXPECT_TRUE(connection_.HasQueuedData());
- EXPECT_TRUE(QuicConnectionPeer::IsWriteBlocked(&connection_));
- writer_->set_blocked(false);
+ writer_->SetWritable();
connection_.OnCanWrite();
EXPECT_TRUE(writer_->version_negotiation_packet() != NULL);
size_t num_versions = arraysize(kSupportedQuicVersions);
- EXPECT_EQ(num_versions,
+ ASSERT_EQ(num_versions,
writer_->version_negotiation_packet()->versions.size());
// We expect all versions in kSupportedQuicVersions to be
@@ -2910,12 +3436,13 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) {
}
}
-TEST_F(QuicConnectionTest,
+TEST_P(QuicConnectionTest,
ServerSendsVersionNegotiationPacketSocketBlockedDataBuffered) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED);
QuicPacketHeader header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.entropy_flag = false;
@@ -2927,27 +3454,26 @@ TEST_F(QuicConnectionTest,
QuicFrame frame(&frame1_);
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
- framer_.set_version(QuicVersionMax());
+ framer_.set_version(version());
connection_.set_is_server(true);
- writer_->set_blocked(true);
+ BlockOnNextWrite();
writer_->set_is_write_blocked_data_buffered(true);
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
EXPECT_EQ(0u, writer_->last_packet_size());
EXPECT_FALSE(connection_.HasQueuedData());
- EXPECT_TRUE(QuicConnectionPeer::IsWriteBlocked(&connection_));
}
-TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) {
+TEST_P(QuicConnectionTest, ClientHandlesVersionNegotiation) {
// Start out with some unsupported version.
QuicConnectionPeer::GetFramer(&connection_)->set_version_for_tests(
QUIC_VERSION_UNSUPPORTED);
QuicPacketHeader header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.entropy_flag = false;
@@ -2973,7 +3499,7 @@ TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) {
QuicFrame frame(&frame1_);
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
encrypted.reset(framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
@@ -2983,9 +3509,9 @@ TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) {
QuicConnectionPeer::GetPacketCreator(&connection_)));
}
-TEST_F(QuicConnectionTest, BadVersionNegotiation) {
+TEST_P(QuicConnectionTest, BadVersionNegotiation) {
QuicPacketHeader header;
- header.public_header.guid = guid_;
+ header.public_header.connection_id = connection_id_;
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.entropy_flag = false;
@@ -3009,51 +3535,37 @@ TEST_F(QuicConnectionTest, BadVersionNegotiation) {
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
}
-TEST_F(QuicConnectionTest, CheckSendStats) {
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
+TEST_P(QuicConnectionTest, CheckSendStats) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
connection_.SendStreamDataWithString(3, "first", 0, !kFin, NULL);
- size_t first_packet_size = last_sent_packet_size();
+ size_t first_packet_size = writer_->last_packet_size();
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
connection_.SendStreamDataWithString(5, "second", 0, !kFin, NULL);
- size_t second_packet_size = last_sent_packet_size();
+ size_t second_packet_size = writer_->last_packet_size();
// 2 retransmissions due to rto, 1 due to explicit nack.
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, RTO_RETRANSMISSION, _)).Times(2);
- EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, NACK_RETRANSMISSION, _));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(3);
- EXPECT_CALL(visitor_, OnCanWrite()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
// Retransmit due to RTO.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
connection_.GetRetransmissionAlarm()->Fire();
// Retransmit due to explicit nacks.
- QuicAckFrame nack_three(4, QuicTime::Zero(), 0);
- nack_three.received_info.missing_packets.insert(3);
- nack_three.received_info.missing_packets.insert(1);
- nack_three.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- QuicFrame frame(&nack_three);
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1);
- EXPECT_CALL(visitor_, OnCanWrite()).Times(4).WillRepeatedly(Return(true));
- EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-
- ProcessFramePacket(frame);
- ProcessFramePacket(frame);
- ProcessFramePacket(frame);
-
- EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillOnce(
- Return(QuicTime::Delta::Zero()));
+ QuicAckFrame nack_three = InitAckFrame(4, 0);
+ NackPacket(3, &nack_three);
+ NackPacket(1, &nack_three);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ lost_packets.insert(3);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(2);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessAckPacket(&nack_three);
+
EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(
Return(QuicBandwidth::Zero()));
@@ -3067,7 +3579,7 @@ TEST_F(QuicConnectionTest, CheckSendStats) {
EXPECT_EQ(1u, stats.rto_count);
}
-TEST_F(QuicConnectionTest, CheckReceiveStats) {
+TEST_P(QuicConnectionTest, CheckReceiveStats) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
size_t received_bytes = 0;
@@ -3077,8 +3589,6 @@ TEST_F(QuicConnectionTest, CheckReceiveStats) {
received_bytes += ProcessDataPacket(3, 1, !kEntropyFlag);
received_bytes += ProcessFecPacket(4, 1, true, !kEntropyFlag, NULL);
- EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillOnce(
- Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(
Return(QuicBandwidth::Zero()));
@@ -3090,7 +3600,7 @@ TEST_F(QuicConnectionTest, CheckReceiveStats) {
EXPECT_EQ(1u, stats.packets_dropped);
}
-TEST_F(QuicConnectionTest, TestFecGroupLimits) {
+TEST_P(QuicConnectionTest, TestFecGroupLimits) {
// Create and return a group for 1.
ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) != NULL);
@@ -3112,9 +3622,9 @@ TEST_F(QuicConnectionTest, TestFecGroupLimits) {
ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) == NULL);
}
-TEST_F(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) {
+TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) {
// Construct a packet with stream frame and connection close frame.
- header_.public_header.guid = guid_;
+ header_.public_header.connection_id = connection_id_;
header_.packet_sequence_number = 1;
header_.public_header.reset_flag = false;
header_.public_header.version_flag = false;
@@ -3131,7 +3641,7 @@ TEST_F(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) {
frames.push_back(stream_frame);
frames.push_back(close_frame);
scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header_, frames).packet);
+ BuildUnsizedDataPacket(&framer_, header_, frames).packet);
EXPECT_TRUE(NULL != packet.get());
scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(
ENCRYPTION_NONE, 1, *packet));
@@ -3143,7 +3653,8 @@ TEST_F(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) {
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
}
-TEST_F(QuicConnectionTest, SelectMutualVersion) {
+TEST_P(QuicConnectionTest, SelectMutualVersion) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
// Set the connection to speak the lowest quic version.
connection_.set_version(QuicVersionMin());
EXPECT_EQ(QuicVersionMin(), connection_.version());
@@ -3172,74 +3683,61 @@ TEST_F(QuicConnectionTest, SelectMutualVersion) {
EXPECT_FALSE(connection_.SelectMutualVersion(unsupported_version));
}
-TEST_F(QuicConnectionTest, ConnectionCloseWhenNotWriteBlocked) {
- writer_->set_blocked(false); // Already default.
+TEST_P(QuicConnectionTest, ConnectionCloseWhenWritable) {
+ EXPECT_FALSE(writer_->IsWriteBlocked());
- // Send a packet (but write will not block).
+ // Send a packet.
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_EQ(1u, writer_->packets_write_attempts());
- // Send an erroneous packet to close the connection.
- EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
- ProcessDataPacket(6000, 0, !kEntropyFlag);
+ TriggerConnectionClose();
EXPECT_EQ(2u, writer_->packets_write_attempts());
}
-TEST_F(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) {
- EXPECT_EQ(0u, connection_.NumQueuedPackets());
- writer_->set_blocked(true);
+TEST_P(QuicConnectionTest, ConnectionCloseGettingWriteBlocked) {
+ BlockOnNextWrite();
+ TriggerConnectionClose();
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+}
- // Send a packet to so that write will really block.
+TEST_P(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) {
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
EXPECT_EQ(1u, writer_->packets_write_attempts());
-
- // Send an erroneous packet to close the connection.
- EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
- ProcessDataPacket(6000, 0, !kEntropyFlag);
- EXPECT_EQ(1u, writer_->packets_write_attempts());
-}
-
-TEST_F(QuicConnectionTest, ConnectionCloseWhenNothingPending) {
- writer_->set_blocked(true);
-
- // Send an erroneous packet to close the connection.
- EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
- ProcessDataPacket(6000, 0, !kEntropyFlag);
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ TriggerConnectionClose();
EXPECT_EQ(1u, writer_->packets_write_attempts());
}
-TEST_F(QuicConnectionTest, AckNotifierTriggerCallback) {
+TEST_P(QuicConnectionTest, AckNotifierTriggerCallback) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Create a delegate which we expect to be called.
- MockAckNotifierDelegate delegate;
- EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1);
// Send some data, which will register the delegate to be notified.
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, &delegate);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
// Process an ACK from the server which should trigger the callback.
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
- QuicAckFrame frame(1, QuicTime::Zero(), 0);
- frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame = InitAckFrame(1, 0);
ProcessAckPacket(&frame);
}
-TEST_F(QuicConnectionTest, AckNotifierFailToTriggerCallback) {
+TEST_P(QuicConnectionTest, AckNotifierFailToTriggerCallback) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Create a delegate which we don't expect to be called.
- MockAckNotifierDelegate delegate;
- EXPECT_CALL(delegate, OnAckNotification()).Times(0);;
-
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2);
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(0);
// Send some data, which will register the delegate to be notified. This will
// not be ACKed and so the delegate should never be called.
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, &delegate);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
// Send some other data which we will ACK.
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
@@ -3247,82 +3745,183 @@ TEST_F(QuicConnectionTest, AckNotifierFailToTriggerCallback) {
// Now we receive ACK for packets 2 and 3, but importantly missing packet 1
// which we registered to be notified about.
- QuicAckFrame frame(3, QuicTime::Zero(), 0);
- frame.received_info.missing_packets.insert(1);
- frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _));
+ QuicAckFrame frame = InitAckFrame(3, 0);
+ NackPacket(1, &frame);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
ProcessAckPacket(&frame);
}
-TEST_F(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) {
+TEST_P(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Create a delegate which we expect to be called.
- MockAckNotifierDelegate delegate;
- EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
-
- // In total expect ACKs for all 4 packets.
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(4);
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1);
// Send four packets, and register to be notified on ACK of packet 2.
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
- connection_.SendStreamDataWithString(1, "bar", 0, !kFin, &delegate);
- connection_.SendStreamDataWithString(1, "baz", 0, !kFin, NULL);
- connection_.SendStreamDataWithString(1, "qux", 0, !kFin, NULL);
-
- // Now we receive ACK for packets 1, 3, and 4, which invokes fast retransmit.
- QuicAckFrame frame(4, QuicTime::Zero(), 0);
- frame.received_info.missing_packets.insert(2);
- frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(2, _));
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "bar", 0, !kFin, delegate.get());
+ connection_.SendStreamDataWithString(3, "baz", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "qux", 0, !kFin, NULL);
+
+ // Now we receive ACK for packets 1, 3, and 4 and lose 2.
+ QuicAckFrame frame = InitAckFrame(4, 0);
+ NackPacket(2, &frame);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
ProcessAckPacket(&frame);
// Now we get an ACK for packet 5 (retransmitted packet 2), which should
// trigger the callback.
- QuicAckFrame second_ack_frame(5, QuicTime::Zero(), 0);
- second_ack_frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 5);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillRepeatedly(Return(SequenceNumberSet()));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame second_ack_frame = InitAckFrame(5, 0);
+ ProcessAckPacket(&second_ack_frame);
+}
+
+// AckNotifierCallback is triggered by the ack of a packet that timed
+// out and was retransmitted, even though the retransmission has a
+// different sequence number.
+TEST_P(QuicConnectionTest, AckNotifierCallbackForAckAfterRTO) {
+ InSequence s;
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
+ DefaultRetransmissionTime());
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, delegate.get());
+ EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
+
+ EXPECT_EQ(1u, writer_->header().packet_sequence_number);
+ EXPECT_EQ(default_retransmission_time,
+ connection_.GetRetransmissionAlarm()->deadline());
+ // Simulate the retransmission alarm firing.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(2u, writer_->header().packet_sequence_number);
+ // We do not raise the high water mark yet.
+ EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked);
+
+ // Ack the original packet.
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*delegate, OnAckNotification(1, _, 1, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame ack_frame = InitAckFrame(1, 0);
+ ProcessAckPacket(&ack_frame);
+
+ // Delegate is not notified again when the retransmit is acked.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame second_ack_frame = InitAckFrame(2, 0);
+ ProcessAckPacket(&second_ack_frame);
+}
+
+// AckNotifierCallback is triggered by the ack of a packet that was
+// previously nacked, even though the retransmission has a different
+// sequence number.
+TEST_P(QuicConnectionTest, AckNotifierCallbackForAckOfNackedPacket) {
+ InSequence s;
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ // Send four packets, and register to be notified on ACK of packet 2.
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "bar", 0, !kFin, delegate.get());
+ connection_.SendStreamDataWithString(3, "baz", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(3, "qux", 0, !kFin, NULL);
+
+ // Now we receive ACK for packets 1, 3, and 4 and lose 2.
+ QuicAckFrame frame = InitAckFrame(4, 0);
+ NackPacket(2, &frame);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ ProcessAckPacket(&frame);
+
+ // Now we get an ACK for packet 2, which was previously nacked.
+ SequenceNumberSet no_lost_packets;
+ EXPECT_CALL(*delegate, OnAckNotification(1, _, 1, _, _));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(no_lost_packets));
+ QuicAckFrame second_ack_frame = InitAckFrame(4, 0);
ProcessAckPacket(&second_ack_frame);
+
+ // Verify that the delegate is not notified again when the
+ // retransmit is acked.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(no_lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame third_ack_frame = InitAckFrame(5, 0);
+ ProcessAckPacket(&third_ack_frame);
}
-// TODO(rjshade): Add a similar test that FEC recovery on peer (and resulting
-// ACK) triggers notification on our end.
-TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
+TEST_P(QuicConnectionTest, AckNotifierFECTriggerCallback) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(visitor_, OnCanWrite()).Times(1).WillOnce(Return(true));
// Create a delegate which we expect to be called.
- MockAckNotifierDelegate delegate;
- EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1);
+
+ // Send some data, which will register the delegate to be notified.
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
+ connection_.SendStreamDataWithString(2, "bar", 0, !kFin, NULL);
+
+ // Process an ACK from the server with a revived packet, which should trigger
+ // the callback.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame = InitAckFrame(2, 0);
+ NackPacket(1, &frame);
+ frame.received_info.revived_packets.insert(1);
+ ProcessAckPacket(&frame);
+ // If the ack is processed again, the notifier should not be called again.
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnCanWrite());
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1);
// Expect ACKs for 1 packet.
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
// Send one packet, and register to be notified on ACK.
- connection_.SendStreamDataWithString(1, "foo", 0, !kFin, &delegate);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
// Ack packet gets dropped, but we receive an FEC packet that covers it.
// Should recover the Ack packet and trigger the notification callback.
QuicFrames frames;
- QuicAckFrame ack_frame(1, QuicTime::Zero(), 0);
- ack_frame.received_info.entropy_hash =
- QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
+ QuicAckFrame ack_frame = InitAckFrame(1, 0);
frames.push_back(QuicFrame(&ack_frame));
// Dummy stream frame to satisfy expectations set elsewhere.
frames.push_back(QuicFrame(&frame1_));
QuicPacketHeader ack_header;
- ack_header.public_header.guid = guid_;
+ ack_header.public_header.connection_id = connection_id_;
ack_header.public_header.reset_flag = false;
ack_header.public_header.version_flag = false;
ack_header.entropy_flag = !kEntropyFlag;
@@ -3332,7 +3931,7 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
ack_header.fec_group = 1;
QuicPacket* packet =
- framer_.BuildUnsizedDataPacket(ack_header, frames).packet;
+ BuildUnsizedDataPacket(&framer_, ack_header, frames).packet;
// Take the packet which contains the ACK frame, and construct and deliver an
// FEC packet which allows the ACK packet to be recovered.
@@ -3340,14 +3939,15 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
}
class MockQuicConnectionDebugVisitor
- : public QuicConnectionDebugVisitorInterface {
+ : public QuicConnectionDebugVisitor {
public:
MOCK_METHOD1(OnFrameAddedToPacket,
void(const QuicFrame&));
- MOCK_METHOD4(OnPacketSent,
+ MOCK_METHOD5(OnPacketSent,
void(QuicPacketSequenceNumber,
EncryptionLevel,
+ TransmissionType,
const QuicEncryptedPacket&,
WriteResult));
@@ -3375,6 +3975,9 @@ class MockQuicConnectionDebugVisitor
MOCK_METHOD1(OnCongestionFeedbackFrame,
void(const QuicCongestionFeedbackFrame&));
+ MOCK_METHOD1(OnStopWaitingFrame,
+ void(const QuicStopWaitingFrame&));
+
MOCK_METHOD1(OnRstStreamFrame,
void(const QuicRstStreamFrame&));
@@ -3391,7 +3994,7 @@ class MockQuicConnectionDebugVisitor
void(const QuicPacketHeader&, StringPiece payload));
};
-TEST_F(QuicConnectionTest, OnPacketHeaderDebugVisitor) {
+TEST_P(QuicConnectionTest, OnPacketHeaderDebugVisitor) {
QuicPacketHeader header;
scoped_ptr<MockQuicConnectionDebugVisitor>
@@ -3401,17 +4004,40 @@ TEST_F(QuicConnectionTest, OnPacketHeaderDebugVisitor) {
connection_.OnPacketHeader(header);
}
-TEST_F(QuicConnectionTest, Pacing) {
+TEST_P(QuicConnectionTest, Pacing) {
ValueRestore<bool> old_flag(&FLAGS_enable_quic_pacing, true);
- TestConnection server(guid_, IPEndPoint(), helper_.get(), writer_.get(),
- true);
- TestConnection client(guid_, IPEndPoint(), helper_.get(), writer_.get(),
- false);
+ TestConnection server(connection_id_, IPEndPoint(), helper_.get(),
+ writer_.get(), true, version());
+ TestConnection client(connection_id_, IPEndPoint(), helper_.get(),
+ writer_.get(), false, version());
EXPECT_TRUE(client.sent_packet_manager().using_pacing());
EXPECT_FALSE(server.sent_packet_manager().using_pacing());
}
+TEST_P(QuicConnectionTest, ControlFramesInstigateAcks) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Send a WINDOW_UPDATE frame.
+ QuicWindowUpdateFrame window_update;
+ window_update.stream_id = 3;
+ window_update.byte_offset = 1234;
+ EXPECT_CALL(visitor_, OnWindowUpdateFrames(_));
+ ProcessFramePacket(QuicFrame(&window_update));
+
+ // Ensure that this has caused the ACK alarm to be set.
+ QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
+ EXPECT_TRUE(ack_alarm->IsSet());
+
+ // Cancel alarm, and try again with BLOCKED frame.
+ ack_alarm->Cancel();
+ QuicBlockedFrame blocked;
+ blocked.stream_id = 3;
+ EXPECT_CALL(visitor_, OnBlockedFrames(_));
+ ProcessFramePacket(QuicFrame(&blocked));
+ EXPECT_TRUE(ack_alarm->IsSet());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_crypto_client_stream.cc b/chromium/net/quic/quic_crypto_client_stream.cc
index 3e5f84059fc..3993637e104 100644
--- a/chromium/net/quic/quic_crypto_client_stream.cc
+++ b/chromium/net/quic/quic_crypto_client_stream.cc
@@ -4,42 +4,23 @@
#include "net/quic/quic_crypto_client_stream.h"
-#include "net/base/completion_callback.h"
-#include "net/base/net_errors.h"
+#include "net/quic/crypto/channel_id.h"
#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/crypto_utils.h"
#include "net/quic/crypto/null_encrypter.h"
#include "net/quic/crypto/proof_verifier.h"
-#include "net/quic/crypto/proof_verifier_chromium.h"
+#include "net/quic/quic_client_session_base.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_session.h"
-#include "net/ssl/ssl_connection_status_flags.h"
-#include "net/ssl/ssl_info.h"
namespace net {
-namespace {
-
-// Copies CertVerifyResult from |verify_details| to |cert_verify_result|.
-void CopyCertVerifyResult(
- const ProofVerifyDetails* verify_details,
- scoped_ptr<CertVerifyResult>* cert_verify_result) {
- const CertVerifyResult* cert_verify_result_other =
- &(reinterpret_cast<const ProofVerifyDetailsChromium*>(
- verify_details))->cert_verify_result;
- CertVerifyResult* result_copy = new CertVerifyResult;
- result_copy->CopyFrom(*cert_verify_result_other);
- cert_verify_result->reset(result_copy);
-}
-
-} // namespace
-
QuicCryptoClientStream::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
QuicCryptoClientStream* stream)
: stream_(stream) {}
QuicCryptoClientStream::ProofVerifierCallbackImpl::
- ~ProofVerifierCallbackImpl() {}
+~ProofVerifierCallbackImpl() {}
void QuicCryptoClientStream::ProofVerifierCallbackImpl::Run(
bool ok,
@@ -63,18 +44,19 @@ void QuicCryptoClientStream::ProofVerifierCallbackImpl::Cancel() {
stream_ = NULL;
}
-
QuicCryptoClientStream::QuicCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
QuicCryptoClientConfig* crypto_config)
: QuicCryptoStream(session),
next_state_(STATE_IDLE),
num_client_hellos_(0),
crypto_config_(crypto_config),
- server_hostname_(server_hostname),
+ server_id_(server_id),
generation_counter_(0),
- proof_verify_callback_(NULL) {
+ proof_verify_callback_(NULL),
+ verify_context_(verify_context) {
}
QuicCryptoClientStream::~QuicCryptoClientStream() {
@@ -91,7 +73,7 @@ void QuicCryptoClientStream::OnHandshakeMessage(
}
bool QuicCryptoClientStream::CryptoConnect() {
- next_state_ = STATE_SEND_CHLO;
+ next_state_ = STATE_INITIALIZE;
DoHandshakeLoop(NULL);
return true;
}
@@ -100,42 +82,6 @@ int QuicCryptoClientStream::num_sent_client_hellos() const {
return num_client_hellos_;
}
-// TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways
-// we learn about SSL info (sync vs async vs cached).
-bool QuicCryptoClientStream::GetSSLInfo(SSLInfo* ssl_info) {
- ssl_info->Reset();
- if (!cert_verify_result_) {
- return false;
- }
-
- ssl_info->cert_status = cert_verify_result_->cert_status;
- ssl_info->cert = cert_verify_result_->verified_cert;
-
- // TODO(rtenneti): Figure out what to set for the following.
- // Temporarily hard coded cipher_suite as 0xc031 to represent
- // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (from
- // net/ssl/ssl_cipher_suite_names.cc) and encryption as 256.
- int cipher_suite = 0xc02f;
- int ssl_connection_status = 0;
- ssl_connection_status |=
- (cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK) <<
- SSL_CONNECTION_CIPHERSUITE_SHIFT;
- ssl_connection_status |=
- (SSL_CONNECTION_VERSION_TLS1_2 & SSL_CONNECTION_VERSION_MASK) <<
- SSL_CONNECTION_VERSION_SHIFT;
-
- ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes;
- ssl_info->is_issued_by_known_root =
- cert_verify_result_->is_issued_by_known_root;
-
- ssl_info->connection_status = ssl_connection_status;
- ssl_info->client_cert_sent = false;
- ssl_info->channel_id_sent = false;
- ssl_info->security_bits = 256;
- ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL;
- return true;
-}
-
// kMaxClientHellos is the maximum number of times that we'll send a client
// hello. The value 3 accounts for:
// * One failure due to an incorrect or missing source-address token.
@@ -149,7 +95,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
QuicErrorCode error;
string error_details;
QuicCryptoClientConfig::CachedState* cached =
- crypto_config_->LookupOrCreate(server_hostname_);
+ crypto_config_->LookupOrCreate(server_id_);
if (in != NULL) {
DVLOG(1) << "Client: Received " << in->DebugString();
@@ -159,6 +105,17 @@ void QuicCryptoClientStream::DoHandshakeLoop(
const State state = next_state_;
next_state_ = STATE_IDLE;
switch (state) {
+ case STATE_INITIALIZE: {
+ if (!cached->IsEmpty() && !cached->signature().empty() &&
+ server_id_.is_https()) {
+ DCHECK(crypto_config_->proof_verifier());
+ // If the cached state needs to be verified, do it now.
+ next_state_ = STATE_VERIFY_PROOF;
+ } else {
+ next_state_ = STATE_SEND_CHLO;
+ }
+ break;
+ }
case STATE_SEND_CHLO: {
// Send the client hello in plaintext.
session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE);
@@ -170,13 +127,13 @@ void QuicCryptoClientStream::DoHandshakeLoop(
if (!cached->IsComplete(session()->connection()->clock()->WallNow())) {
crypto_config_->FillInchoateClientHello(
- server_hostname_,
+ server_id_,
session()->connection()->supported_versions().front(),
cached, &crypto_negotiated_params_, &out);
// Pad the inchoate client hello to fill up a packet.
const size_t kFramingOverhead = 50; // A rough estimate.
const size_t max_packet_size =
- session()->connection()->options()->max_packet_length;
+ session()->connection()->max_packet_length();
if (max_packet_size <= kFramingOverhead) {
DLOG(DFATAL) << "max_packet_length (" << max_packet_size
<< ") has no room for framing overhead.";
@@ -195,13 +152,43 @@ void QuicCryptoClientStream::DoHandshakeLoop(
return;
}
session()->config()->ToHandshakeMessage(&out);
+
+ scoped_ptr<ChannelIDKey> channel_id_key;
+ bool do_channel_id = false;
+ if (crypto_config_->channel_id_source()) {
+ const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
+ DCHECK(scfg);
+ const QuicTag* their_proof_demands;
+ size_t num_their_proof_demands;
+ if (scfg->GetTaglist(kPDMD, &their_proof_demands,
+ &num_their_proof_demands) == QUIC_NO_ERROR) {
+ for (size_t i = 0; i < num_their_proof_demands; i++) {
+ if (their_proof_demands[i] == kCHID) {
+ do_channel_id = true;
+ break;
+ }
+ }
+ }
+ }
+ if (do_channel_id) {
+ QuicAsyncStatus status =
+ crypto_config_->channel_id_source()->GetChannelIDKey(
+ server_id_.host(), &channel_id_key, NULL);
+ if (status != QUIC_SUCCESS) {
+ CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE,
+ "Channel ID lookup failed");
+ return;
+ }
+ }
+
error = crypto_config_->FillClientHello(
- server_hostname_,
- session()->connection()->guid(),
+ server_id_,
+ session()->connection()->connection_id(),
session()->connection()->supported_versions().front(),
cached,
session()->connection()->clock()->WallNow(),
session()->connection()->random_generator(),
+ channel_id_key.get(),
&crypto_negotiated_params_,
&out,
&error_details);
@@ -213,10 +200,8 @@ void QuicCryptoClientStream::DoHandshakeLoop(
return;
}
if (cached->proof_verify_details()) {
- CopyCertVerifyResult(cached->proof_verify_details(),
- &cert_verify_result_);
- } else {
- cert_verify_result_.reset();
+ client_session()->OnProofVerifyDetailsAvailable(
+ *cached->proof_verify_details());
}
next_state_ = STATE_RECV_SHLO;
DVLOG(1) << "Client: Sending " << out.DebugString();
@@ -224,6 +209,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
// Be prepared to decrypt with the new server write key.
session()->connection()->SetAlternativeDecrypter(
crypto_negotiated_params_.initial_crypters.decrypter.release(),
+ ENCRYPTION_INITIAL,
true /* latch once used */);
// Send subsequent packets under encryption on the assumption that the
// server will accept the handshake.
@@ -260,10 +246,9 @@ void QuicCryptoClientStream::DoHandshakeLoop(
return;
}
if (!cached->proof_valid()) {
- ProofVerifier* verifier = crypto_config_->proof_verifier();
- if (!verifier) {
- // If no verifier is set then we don't check the certificates.
- cached->SetProofValid();
+ if (!server_id_.is_https()) {
+ // We don't check the certificates for insecure QUIC connections.
+ SetCachedProofValid(cached);
} else if (!cached->signature().empty()) {
next_state_ = STATE_VERIFY_PROOF;
break;
@@ -282,23 +267,26 @@ void QuicCryptoClientStream::DoHandshakeLoop(
verify_ok_ = false;
- ProofVerifier::Status status = verifier->VerifyProof(
- server_hostname_,
+ QuicAsyncStatus status = verifier->VerifyProof(
+ server_id_.host(),
cached->server_config(),
cached->certs(),
cached->signature(),
+ verify_context_.get(),
&verify_error_details_,
&verify_details_,
proof_verify_callback);
switch (status) {
- case ProofVerifier::PENDING:
+ case QUIC_PENDING:
proof_verify_callback_ = proof_verify_callback;
DVLOG(1) << "Doing VerifyProof";
return;
- case ProofVerifier::FAILURE:
+ case QUIC_FAILURE:
+ delete proof_verify_callback;
break;
- case ProofVerifier::SUCCESS:
+ case QUIC_SUCCESS:
+ delete proof_verify_callback;
verify_ok_ = true;
break;
}
@@ -306,7 +294,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
}
case STATE_VERIFY_PROOF_COMPLETE:
if (!verify_ok_) {
- CopyCertVerifyResult(verify_details_.get(), &cert_verify_result_);
+ client_session()->OnProofVerifyDetailsAvailable(*verify_details_);
CloseConnectionWithDetails(
QUIC_PROOF_INVALID, "Proof invalid: " + verify_error_details_);
return;
@@ -316,7 +304,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
if (generation_counter_ != cached->generation_counter()) {
next_state_ = STATE_VERIFY_PROOF;
} else {
- cached->SetProofValid();
+ SetCachedProofValid(cached);
cached->SetProofVerifyDetails(verify_details_.release());
next_state_ = STATE_SEND_CHLO;
}
@@ -352,7 +340,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
return;
}
error = crypto_config_->ProcessServerHello(
- *in, session()->connection()->guid(),
+ *in, session()->connection()->connection_id(),
session()->connection()->server_supported_versions(),
cached, &crypto_negotiated_params_, &error_details);
@@ -361,7 +349,8 @@ void QuicCryptoClientStream::DoHandshakeLoop(
error, "Server hello invalid: " + error_details);
return;
}
- error = session()->config()->ProcessServerHello(*in, &error_details);
+ error =
+ session()->config()->ProcessPeerHello(*in, SERVER, &error_details);
if (error != QUIC_NO_ERROR) {
CloseConnectionWithDetails(
error, "Server hello invalid: " + error_details);
@@ -376,7 +365,8 @@ void QuicCryptoClientStream::DoHandshakeLoop(
// with the FORWARD_SECURE key until it receives a FORWARD_SECURE
// packet from the client.
session()->connection()->SetAlternativeDecrypter(
- crypters->decrypter.release(), false /* don't latch */);
+ crypters->decrypter.release(), ENCRYPTION_FORWARD_SECURE,
+ false /* don't latch */);
session()->connection()->SetEncrypter(
ENCRYPTION_FORWARD_SECURE, crypters->encrypter.release());
session()->connection()->SetDefaultEncryptionLevel(
@@ -394,4 +384,14 @@ void QuicCryptoClientStream::DoHandshakeLoop(
}
}
+void QuicCryptoClientStream::SetCachedProofValid(
+ QuicCryptoClientConfig::CachedState* cached) {
+ cached->SetProofValid();
+ client_session()->OnProofValid(*cached);
+}
+
+QuicClientSessionBase* QuicCryptoClientStream::client_session() {
+ return reinterpret_cast<QuicClientSessionBase*>(session());
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_crypto_client_stream.h b/chromium/net/quic/quic_crypto_client_stream.h
index ca2fea74d75..5bb50ef1506 100644
--- a/chromium/net/quic/quic_crypto_client_stream.h
+++ b/chromium/net/quic/quic_crypto_client_stream.h
@@ -7,18 +7,15 @@
#include <string>
-#include "net/cert/cert_verify_result.h"
-#include "net/cert/x509_certificate.h"
#include "net/quic/crypto/proof_verifier.h"
#include "net/quic/crypto/quic_crypto_client_config.h"
#include "net/quic/quic_config.h"
#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_server_id.h"
namespace net {
-class ProofVerifyDetails;
-class QuicSession;
-class SSLInfo;
+class QuicClientSessionBase;
namespace test {
class CryptoTestUtils;
@@ -26,8 +23,9 @@ class CryptoTestUtils;
class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
public:
- QuicCryptoClientStream(const string& server_hostname,
- QuicSession* session,
+ QuicCryptoClientStream(const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
QuicCryptoClientConfig* crypto_config);
virtual ~QuicCryptoClientStream();
@@ -45,9 +43,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
// than the number of round-trips needed for the handshake.
int num_sent_client_hellos() const;
- // Gets the SSL connection information.
- bool GetSSLInfo(SSLInfo* ssl_info);
-
private:
// ProofVerifierCallbackImpl is passed as the callback method to VerifyProof.
// The ProofVerifier calls this class with the result of proof verification
@@ -75,6 +70,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
enum State {
STATE_IDLE,
+ STATE_INITIALIZE,
STATE_SEND_CHLO,
STATE_RECV_REJ,
STATE_VERIFY_PROOF,
@@ -83,9 +79,15 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
};
// DoHandshakeLoop performs a step of the handshake state machine. Note that
- // |in| may be NULL if the call did not result from a received message
+ // |in| may be NULL if the call did not result from a received message.
void DoHandshakeLoop(const CryptoHandshakeMessage* in);
+ // Called to set the proof of |cached| valid. Also invokes the session's
+ // OnProofValid() method.
+ void SetCachedProofValid(QuicCryptoClientConfig::CachedState* cached);
+
+ QuicClientSessionBase* client_session();
+
State next_state_;
// num_client_hellos_ contains the number of client hello messages that this
// connection has sent.
@@ -95,8 +97,8 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
// Client's connection nonce (4-byte timestamp + 28 random bytes)
std::string nonce_;
- // Server's hostname
- std::string server_hostname_;
+ // Server's (hostname, port, is_https, privacy_mode) tuple.
+ const QuicServerId server_id_;
// Generation counter from QuicCryptoClientConfig's CachedState.
uint64 generation_counter_;
@@ -111,9 +113,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
bool verify_ok_;
string verify_error_details_;
scoped_ptr<ProofVerifyDetails> verify_details_;
-
- // The result of certificate verification.
- scoped_ptr<CertVerifyResult> cert_verify_result_;
+ scoped_ptr<ProofVerifyContext> verify_context_;
DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientStream);
};
diff --git a/chromium/net/quic/quic_crypto_client_stream_factory.h b/chromium/net/quic/quic_crypto_client_stream_factory.h
index 4fa5f573ea0..293fa8217b3 100644
--- a/chromium/net/quic/quic_crypto_client_stream_factory.h
+++ b/chromium/net/quic/quic_crypto_client_stream_factory.h
@@ -11,8 +11,9 @@
namespace net {
+class QuicClientSession;
class QuicCryptoClientStream;
-class QuicSession;
+class QuicServerId;
// An interface used to instantiate QuicCryptoClientStream objects. Used to
// facilitate testing code with mock implementations.
@@ -21,8 +22,8 @@ class NET_EXPORT QuicCryptoClientStreamFactory {
virtual ~QuicCryptoClientStreamFactory() {}
virtual QuicCryptoClientStream* CreateQuicCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSession* session,
QuicCryptoClientConfig* crypto_config) = 0;
};
diff --git a/chromium/net/quic/quic_crypto_client_stream_test.cc b/chromium/net/quic/quic_crypto_client_stream_test.cc
index 8154d177988..3515d32aac7 100644
--- a/chromium/net/quic/quic_crypto_client_stream_test.cc
+++ b/chromium/net/quic/quic_crypto_client_stream_test.cc
@@ -8,7 +8,9 @@
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/simple_quic_framer.h"
@@ -20,15 +22,18 @@ namespace test {
namespace {
const char kServerHostname[] = "example.com";
+const uint16 kServerPort = 80;
class QuicCryptoClientStreamTest : public ::testing::Test {
public:
QuicCryptoClientStreamTest()
: connection_(new PacketSavingConnection(false)),
- session_(new TestSession(connection_, DefaultQuicConfig())),
- stream_(new QuicCryptoClientStream(kServerHostname, session_.get(),
- &crypto_config_)) {
+ session_(new TestClientSession(connection_, DefaultQuicConfig())),
+ server_id_(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED),
+ stream_(new QuicCryptoClientStream(
+ server_id_, session_.get(), NULL, &crypto_config_)) {
session_->SetCryptoStream(stream_.get());
+ session_->config()->SetDefaults();
crypto_config_.SetDefaults();
}
@@ -43,7 +48,8 @@ class QuicCryptoClientStreamTest : public ::testing::Test {
}
PacketSavingConnection* connection_;
- scoped_ptr<TestSession> session_;
+ scoped_ptr<TestClientSession> session_;
+ QuicServerId server_id_;
scoped_ptr<QuicCryptoClientStream> stream_;
CryptoHandshakeMessage message_;
scoped_ptr<QuicData> message_data_;
@@ -87,7 +93,7 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) {
const QuicConfig* config = session_->config();
EXPECT_EQ(FLAGS_enable_quic_pacing ? kPACE : kQBIC,
- config->congestion_control());
+ config->congestion_feedback());
EXPECT_EQ(kDefaultTimeoutSecs,
config->idle_connection_state_lifetime().ToSeconds());
EXPECT_EQ(kDefaultMaxStreamsPerConnection,
@@ -96,12 +102,13 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) {
const QuicCryptoNegotiatedParameters& crypto_params(
stream_->crypto_negotiated_params());
- EXPECT_EQ(kAESG, crypto_params.aead);
- EXPECT_EQ(kC255, crypto_params.key_exchange);
+ EXPECT_EQ(crypto_config_.aead[0], crypto_params.aead);
+ EXPECT_EQ(crypto_config_.kexs[0], crypto_params.key_exchange);
}
TEST_F(QuicCryptoClientStreamTest, InvalidHostname) {
- stream_.reset(new QuicCryptoClientStream("invalid", session_.get(),
+ QuicServerId server_id("invalid", 80, false, PRIVACY_MODE_DISABLED);
+ stream_.reset(new QuicCryptoClientStream(server_id, session_.get(), NULL,
&crypto_config_));
session_->SetCryptoStream(stream_.get());
@@ -115,8 +122,8 @@ TEST_F(QuicCryptoClientStreamTest, ExpiredServerConfig) {
CompleteCryptoHandshake();
connection_ = new PacketSavingConnection(true);
- session_.reset(new TestSession(connection_, DefaultQuicConfig()));
- stream_.reset(new QuicCryptoClientStream(kServerHostname, session_.get(),
+ session_.reset(new TestClientSession(connection_, DefaultQuicConfig()));
+ stream_.reset(new QuicCryptoClientStream(server_id_, session_.get(), NULL,
&crypto_config_));
session_->SetCryptoStream(stream_.get());
diff --git a/chromium/net/quic/quic_crypto_server_stream.cc b/chromium/net/quic/quic_crypto_server_stream.cc
index c0ae1f19b92..7bd2c034d93 100644
--- a/chromium/net/quic/quic_crypto_server_stream.cc
+++ b/chromium/net/quic/quic_crypto_server_stream.cc
@@ -20,11 +20,16 @@ QuicCryptoServerStream::QuicCryptoServerStream(
QuicSession* session)
: QuicCryptoStream(session),
crypto_config_(crypto_config),
- validate_client_hello_cb_(NULL) {
+ validate_client_hello_cb_(NULL),
+ num_handshake_messages_(0) {
}
QuicCryptoServerStream::~QuicCryptoServerStream() {
- // Detach from the validation callback.
+ CancelOutstandingCallbacks();
+}
+
+void QuicCryptoServerStream::CancelOutstandingCallbacks() {
+ // Detach from the validation callback. Calling this multiple times is safe.
if (validate_client_hello_cb_ != NULL) {
validate_client_hello_cb_->Cancel();
}
@@ -33,6 +38,7 @@ QuicCryptoServerStream::~QuicCryptoServerStream() {
void QuicCryptoServerStream::OnHandshakeMessage(
const CryptoHandshakeMessage& message) {
QuicCryptoStream::OnHandshakeMessage(message);
+ ++num_handshake_messages_;
// Do not process handshake messages after the handshake is confirmed.
if (handshake_confirmed_) {
@@ -85,7 +91,8 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
// If we are returning a SHLO then we accepted the handshake.
QuicConfig* config = session()->config();
- error = config->ProcessClientHello(message, &error_details);
+ OverrideQuicConfigDefaults(config);
+ error = config->ProcessPeerHello(message, CLIENT, &error_details);
if (error != QUIC_NO_ERROR) {
CloseConnectionWithDetails(error, error_details);
return;
@@ -107,7 +114,8 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
// Set the decrypter immediately so that we no longer accept unencrypted
// packets.
session()->connection()->SetDecrypter(
- crypto_negotiated_params_.initial_crypters.decrypter.release());
+ crypto_negotiated_params_.initial_crypters.decrypter.release(),
+ ENCRYPTION_INITIAL);
SendHandshakeMessage(reply);
session()->connection()->SetEncrypter(
@@ -117,7 +125,7 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
ENCRYPTION_FORWARD_SECURE);
session()->connection()->SetAlternativeDecrypter(
crypto_negotiated_params_.forward_secure_crypters.decrypter.release(),
- false /* don't latch */);
+ ENCRYPTION_FORWARD_SECURE, false /* don't latch */);
encryption_established_ = true;
handshake_confirmed_ = true;
@@ -161,7 +169,7 @@ QuicErrorCode QuicCryptoServerStream::ProcessClientHello(
string* error_details) {
return crypto_config_.ProcessClientHello(
result,
- session()->connection()->guid(),
+ session()->connection()->connection_id(),
session()->connection()->peer_address(),
session()->connection()->version(),
session()->connection()->supported_versions(),
@@ -170,6 +178,9 @@ QuicErrorCode QuicCryptoServerStream::ProcessClientHello(
&crypto_negotiated_params_, reply, error_details);
}
+void QuicCryptoServerStream::OverrideQuicConfigDefaults(QuicConfig* config) {
+}
+
QuicCryptoServerStream::ValidateCallback::ValidateCallback(
QuicCryptoServerStream* parent) : parent_(parent) {
}
diff --git a/chromium/net/quic/quic_crypto_server_stream.h b/chromium/net/quic/quic_crypto_server_stream.h
index 43924d54d93..ee4013120e4 100644
--- a/chromium/net/quic/quic_crypto_server_stream.h
+++ b/chromium/net/quic/quic_crypto_server_stream.h
@@ -29,6 +29,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
explicit QuicCryptoServerStream(QuicSession* session);
virtual ~QuicCryptoServerStream();
+ // Cancel any outstanding callbacks, such as asynchronous validation of client
+ // hello.
+ void CancelOutstandingCallbacks();
+
// CryptoFramerVisitorInterface implementation
virtual void OnHandshakeMessage(
const CryptoHandshakeMessage& message) OVERRIDE;
@@ -38,6 +42,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
// presented a ChannelID. Otherwise it returns false.
bool GetBase64SHA256ClientChannelID(std::string* output) const;
+ uint8 num_handshake_messages() const { return num_handshake_messages_; }
+
protected:
virtual QuicErrorCode ProcessClientHello(
const CryptoHandshakeMessage& message,
@@ -45,6 +51,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
CryptoHandshakeMessage* reply,
std::string* error_details);
+ // Hook that allows the server to set QuicConfig defaults just
+ // before going through the parameter negotiation step.
+ virtual void OverrideQuicConfigDefaults(QuicConfig* config);
+
private:
friend class test::CryptoTestUtils;
@@ -79,6 +89,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
// FinishProcessingHandshakeMessage for processing. NULL if no
// handshake message is being validated.
ValidateCallback* validate_client_hello_cb_;
+
+ uint8 num_handshake_messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerStream);
};
} // namespace net
diff --git a/chromium/net/quic/quic_crypto_server_stream_test.cc b/chromium/net/quic/quic_crypto_server_stream_test.cc
index 9897e22f738..800c245a0c4 100644
--- a/chromium/net/quic/quic_crypto_server_stream_test.cc
+++ b/chromium/net/quic/quic_crypto_server_stream_test.cc
@@ -49,7 +49,10 @@ class QuicCryptoServerConfigPeer {
namespace {
-class QuicCryptoServerStreamTest : public testing::TestWithParam<bool> {
+const char kServerHostname[] = "test.example.com";
+const uint16 kServerPort = 80;
+
+class QuicCryptoServerStreamTest : public ::testing::TestWithParam<bool> {
public:
QuicCryptoServerStreamTest()
: connection_(new PacketSavingConnection(true)),
@@ -102,7 +105,7 @@ class QuicCryptoServerStreamTest : public testing::TestWithParam<bool> {
protected:
PacketSavingConnection* connection_;
- TestSession session_;
+ TestClientSession session_;
QuicConfig config_;
QuicCryptoServerConfig crypto_config_;
QuicCryptoServerStream stream_;
@@ -137,13 +140,15 @@ TEST_P(QuicCryptoServerStreamTest, ZeroRTT) {
QuicConfig client_config;
client_config.SetDefaults();
- scoped_ptr<TestSession> client_session(
- new TestSession(client_conn, client_config));
+ scoped_ptr<TestClientSession> client_session(
+ new TestClientSession(client_conn, client_config));
QuicCryptoClientConfig client_crypto_config;
client_crypto_config.SetDefaults();
+ QuicServerId server_id(kServerHostname, kServerPort, false,
+ PRIVACY_MODE_DISABLED);
scoped_ptr<QuicCryptoClientStream> client(new QuicCryptoClientStream(
- "test.example.com", client_session.get(), &client_crypto_config));
+ server_id, client_session.get(), NULL, &client_crypto_config));
client_session->SetCryptoStream(client.get());
// Do a first handshake in order to prime the client config with the server's
@@ -173,10 +178,10 @@ TEST_P(QuicCryptoServerStreamTest, ZeroRTT) {
// This causes the client's nonce to be different and thus stops the
// strike-register from rejecting the repeated nonce.
reinterpret_cast<MockRandom*>(client_conn->random_generator())->ChangeValue();
- client_session.reset(new TestSession(client_conn, client_config));
+ client_session.reset(new TestClientSession(client_conn, client_config));
server_session.reset(new TestSession(server_conn, config_));
client.reset(new QuicCryptoClientStream(
- "test.example.com", client_session.get(), &client_crypto_config));
+ server_id, client_session.get(), NULL, &client_crypto_config));
client_session->SetCryptoStream(client.get());
server.reset(new QuicCryptoServerStream(crypto_config_,
@@ -249,7 +254,6 @@ TEST_P(QuicCryptoServerStreamTest, WithoutCertificates) {
TEST_P(QuicCryptoServerStreamTest, ChannelID) {
client_options_.channel_id_enabled = true;
- // TODO(rtenneti): Enable testing of ProofVerifier.
// CompleteCryptoHandshake verifies
// stream_.crypto_negotiated_params().channel_id is correct.
EXPECT_EQ(2, CompleteCryptoHandshake());
diff --git a/chromium/net/quic/quic_crypto_stream.cc b/chromium/net/quic/quic_crypto_stream.cc
index d79fa735786..0e447127af5 100644
--- a/chromium/net/quic/quic_crypto_stream.cc
+++ b/chromium/net/quic/quic_crypto_stream.cc
@@ -10,6 +10,7 @@
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_session.h"
+#include "net/quic/quic_utils.h"
using std::string;
using base::StringPiece;
@@ -21,6 +22,7 @@ QuicCryptoStream::QuicCryptoStream(QuicSession* session)
encryption_established_(false),
handshake_confirmed_(false) {
crypto_framer_.set_visitor(this);
+ DisableFlowControl();
}
void QuicCryptoStream::OnError(CryptoFramer* framer) {
@@ -55,12 +57,8 @@ void QuicCryptoStream::SendHandshakeMessage(
const CryptoHandshakeMessage& message) {
session()->OnCryptoHandshakeMessageSent(message);
const QuicData& data = message.GetSerialized();
- // To make reasoning about crypto frames easier, we don't combine them with
- // any other frames in a single packet.
- session()->connection()->Flush();
// TODO(wtc): check the return value.
- WriteOrBufferData(string(data.data(), data.length()), false);
- session()->connection()->Flush();
+ WriteOrBufferData(string(data.data(), data.length()), false, NULL);
}
const QuicCryptoNegotiatedParameters&
diff --git a/chromium/net/quic/quic_crypto_stream.h b/chromium/net/quic/quic_crypto_stream.h
index a082d50dbf2..2c61a18fe78 100644
--- a/chromium/net/quic/quic_crypto_stream.h
+++ b/chromium/net/quic/quic_crypto_stream.h
@@ -5,6 +5,7 @@
#ifndef NET_QUIC_QUIC_CRYPTO_STREAM_H_
#define NET_QUIC_QUIC_CRYPTO_STREAM_H_
+#include "base/basictypes.h"
#include "net/quic/crypto/crypto_framer.h"
#include "net/quic/crypto/crypto_utils.h"
#include "net/quic/quic_config.h"
@@ -45,8 +46,8 @@ class NET_EXPORT_PRIVATE QuicCryptoStream
// TODO(wtc): return a success/failure status.
void SendHandshakeMessage(const CryptoHandshakeMessage& message);
- bool encryption_established() { return encryption_established_; }
- bool handshake_confirmed() { return handshake_confirmed_; }
+ bool encryption_established() const { return encryption_established_; }
+ bool handshake_confirmed() const { return handshake_confirmed_; }
const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const;
diff --git a/chromium/net/quic/quic_crypto_stream_test.cc b/chromium/net/quic/quic_crypto_stream_test.cc
index c8dbab51453..2e4b34fe34a 100644
--- a/chromium/net/quic/quic_crypto_stream_test.cc
+++ b/chromium/net/quic/quic_crypto_stream_test.cc
@@ -10,6 +10,7 @@
#include "base/memory/scoped_ptr.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -101,6 +102,11 @@ TEST_F(QuicCryptoStreamTest, ProcessBadData) {
EXPECT_EQ(0u, stream_.ProcessRawData(bad.data(), bad.length()));
}
+TEST_F(QuicCryptoStreamTest, NoFlowControl) {
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+ EXPECT_FALSE(stream_.flow_controller()->IsEnabled());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_data_reader.h b/chromium/net/quic/quic_data_reader.h
index 9effa1cc896..1686de028cc 100644
--- a/chromium/net/quic/quic_data_reader.h
+++ b/chromium/net/quic/quic_data_reader.h
@@ -124,6 +124,8 @@ class NET_EXPORT_PRIVATE QuicDataReader {
// The location of the next read from our data buffer.
size_t pos_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDataReader);
};
} // namespace net
diff --git a/chromium/net/quic/quic_data_stream.cc b/chromium/net/quic/quic_data_stream.cc
index 3c992a777b0..7963f4f41eb 100644
--- a/chromium/net/quic/quic_data_stream.cc
+++ b/chromium/net/quic/quic_data_stream.cc
@@ -6,8 +6,8 @@
#include "base/logging.h"
#include "net/quic/quic_session.h"
-#include "net/quic/quic_spdy_decompressor.h"
-#include "net/spdy/write_blocked_list.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/quic_write_blocked_list.h"
using base::StringPiece;
using std::min;
@@ -24,26 +24,6 @@ namespace {
// priority in the middle.
QuicPriority kDefaultPriority = 3;
-// Appends bytes from data into partial_data_buffer. Once partial_data_buffer
-// reaches 4 bytes, copies the data into 'result' and clears
-// partial_data_buffer.
-// Returns the number of bytes consumed.
-uint32 StripUint32(const char* data, uint32 data_len,
- string* partial_data_buffer,
- uint32* result) {
- DCHECK_GT(4u, partial_data_buffer->length());
- size_t missing_size = 4 - partial_data_buffer->length();
- if (data_len < missing_size) {
- StringPiece(data, data_len).AppendToString(partial_data_buffer);
- return data_len;
- }
- StringPiece(data, missing_size).AppendToString(partial_data_buffer);
- DCHECK_EQ(4u, partial_data_buffer->length());
- memcpy(result, partial_data_buffer->data(), 4);
- partial_data_buffer->clear();
- return missing_size;
-}
-
} // namespace
QuicDataStream::QuicDataStream(QuicStreamId id,
@@ -52,15 +32,31 @@ QuicDataStream::QuicDataStream(QuicStreamId id,
visitor_(NULL),
headers_decompressed_(false),
priority_(kDefaultPriority),
- headers_id_(0),
decompression_failed_(false),
priority_parsed_(false) {
DCHECK_NE(kCryptoStreamId, id);
+ // Don't receive any callbacks from the sequencer until headers
+ // are complete.
+ sequencer()->SetBlockedUntilFlush();
}
QuicDataStream::~QuicDataStream() {
}
+size_t QuicDataStream::WriteHeaders(
+ const SpdyHeaderBlock& header_block,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ size_t bytes_written = session()->WriteHeaders(
+ id(), header_block, fin, ack_notifier_delegate);
+ if (fin) {
+ // TODO(rch): Add test to ensure fin_sent_ is set whenever a fin is sent.
+ set_fin_sent(true);
+ CloseWriteSide();
+ }
+ return bytes_written;
+}
+
size_t QuicDataStream::Readv(const struct iovec* iov, size_t iov_len) {
if (FinishedReadingHeaders()) {
// If the headers have been read, simply delegate to the sequencer's
@@ -81,6 +77,9 @@ size_t QuicDataStream::Readv(const struct iovec* iov, size_t iov_len) {
++iov_index;
}
decompressed_headers_.erase(0, bytes_consumed);
+ if (FinishedReadingHeaders()) {
+ sequencer()->FlushBufferedFrames();
+ }
return bytes_consumed;
}
@@ -118,105 +117,17 @@ QuicPriority QuicDataStream::EffectivePriority() const {
}
uint32 QuicDataStream::ProcessRawData(const char* data, uint32 data_len) {
- DCHECK_NE(0u, data_len);
-
- uint32 total_bytes_consumed = 0;
- if (headers_id_ == 0u) {
- total_bytes_consumed += StripPriorityAndHeaderId(data, data_len);
- data += total_bytes_consumed;
- data_len -= total_bytes_consumed;
- if (data_len == 0 || total_bytes_consumed == 0) {
- return total_bytes_consumed;
- }
- }
- DCHECK_NE(0u, headers_id_);
-
- // Once the headers are finished, we simply pass the data through.
- if (headers_decompressed_) {
- // Some buffered header data remains.
- if (!decompressed_headers_.empty()) {
- ProcessHeaderData();
- }
- if (decompressed_headers_.empty()) {
- DVLOG(1) << "Delegating procesing to ProcessData";
- total_bytes_consumed += ProcessData(data, data_len);
- }
- return total_bytes_consumed;
- }
-
- QuicHeaderId current_header_id =
- session()->decompressor()->current_header_id();
- // Ensure that this header id looks sane.
- if (headers_id_ < current_header_id ||
- headers_id_ > kMaxHeaderIdDelta + current_header_id) {
- DVLOG(1) << ENDPOINT
- << "Invalid headers for stream: " << id()
- << " header_id: " << headers_id_
- << " current_header_id: " << current_header_id;
- session()->connection()->SendConnectionClose(QUIC_INVALID_HEADER_ID);
- return total_bytes_consumed;
- }
-
- // If we are head-of-line blocked on decompression, then back up.
- if (current_header_id != headers_id_) {
- session()->MarkDecompressionBlocked(headers_id_, id());
- DVLOG(1) << ENDPOINT
- << "Unable to decompress header data for stream: " << id()
- << " header_id: " << headers_id_;
- return total_bytes_consumed;
- }
-
- // Decompressed data will be delivered to decompressed_headers_.
- size_t bytes_consumed = session()->decompressor()->DecompressData(
- StringPiece(data, data_len), this);
- DCHECK_NE(0u, bytes_consumed);
- if (bytes_consumed > data_len) {
- DCHECK(false) << "DecompressData returned illegal value";
- OnDecompressionError();
- return total_bytes_consumed;
- }
- total_bytes_consumed += bytes_consumed;
- data += bytes_consumed;
- data_len -= bytes_consumed;
-
- if (decompression_failed_) {
- // The session will have been closed in OnDecompressionError.
- return total_bytes_consumed;
- }
-
- // Headers are complete if the decompressor has moved on to the
- // next stream.
- headers_decompressed_ =
- session()->decompressor()->current_header_id() != headers_id_;
- if (!headers_decompressed_) {
- DCHECK_EQ(0u, data_len);
- }
-
- ProcessHeaderData();
-
- if (!headers_decompressed_ || !decompressed_headers_.empty()) {
- return total_bytes_consumed;
- }
-
- // We have processed all of the decompressed data but we might
- // have some more raw data to process.
- if (data_len > 0) {
- total_bytes_consumed += ProcessData(data, data_len);
+ if (!FinishedReadingHeaders()) {
+ LOG(DFATAL) << "ProcessRawData called before headers have been finished";
+ return 0;
}
-
- // The sequencer will push any additional buffered frames if this data
- // has been completely consumed.
- return total_bytes_consumed;
+ return ProcessData(data, data_len);
}
const IPEndPoint& QuicDataStream::GetPeerAddress() {
return session()->peer_address();
}
-QuicSpdyCompressor* QuicDataStream::compressor() {
- return session()->compressor();
-}
-
bool QuicDataStream::GetSSLInfo(SSLInfo* ssl_info) {
return session()->GetSSLInfo(ssl_info);
}
@@ -236,53 +147,27 @@ uint32 QuicDataStream::ProcessHeaderData() {
return bytes_processed;
}
-void QuicDataStream::OnDecompressorAvailable() {
- DCHECK_EQ(headers_id_,
- session()->decompressor()->current_header_id());
- DCHECK(!headers_decompressed_);
- DCHECK(!decompression_failed_);
- DCHECK_EQ(0u, decompressed_headers_.length());
-
- while (!headers_decompressed_) {
- struct iovec iovec;
- if (sequencer()->GetReadableRegions(&iovec, 1) == 0) {
- return;
- }
+void QuicDataStream::OnStreamHeaders(StringPiece headers_data) {
+ headers_data.AppendToString(&decompressed_headers_);
+ ProcessHeaderData();
+}
- size_t bytes_consumed = session()->decompressor()->DecompressData(
- StringPiece(static_cast<char*>(iovec.iov_base),
- iovec.iov_len),
- this);
- DCHECK_LE(bytes_consumed, iovec.iov_len);
- if (decompression_failed_) {
- return;
- }
- sequencer()->MarkConsumed(bytes_consumed);
+void QuicDataStream::OnStreamHeadersPriority(QuicPriority priority) {
+ DCHECK(session()->connection()->is_server());
+ set_priority(priority);
+}
- headers_decompressed_ =
- session()->decompressor()->current_header_id() != headers_id_;
+void QuicDataStream::OnStreamHeadersComplete(bool fin, size_t frame_len) {
+ headers_decompressed_ = true;
+ if (fin) {
+ sequencer()->OnStreamFrame(QuicStreamFrame(id(), fin, 0, IOVector()));
}
-
- // Either the headers are complete, or the all data as been consumed.
- ProcessHeaderData(); // Unprocessed headers remain in decompressed_headers_.
- if (IsDoneReading()) {
- OnFinRead();
- } else if (FinishedReadingHeaders()) {
+ ProcessHeaderData();
+ if (FinishedReadingHeaders()) {
sequencer()->FlushBufferedFrames();
}
}
-bool QuicDataStream::OnDecompressedData(StringPiece data) {
- data.AppendToString(&decompressed_headers_);
- return true;
-}
-
-void QuicDataStream::OnDecompressionError() {
- DCHECK(!decompression_failed_);
- decompression_failed_ = true;
- session()->connection()->SendConnectionClose(QUIC_DECOMPRESSION_FAILURE);
-}
-
void QuicDataStream::OnClose() {
ReliableQuicStream::OnClose();
@@ -295,37 +180,6 @@ void QuicDataStream::OnClose() {
}
}
-uint32 QuicDataStream::StripPriorityAndHeaderId(
- const char* data, uint32 data_len) {
- uint32 total_bytes_parsed = 0;
-
- if (!priority_parsed_ && session()->connection()->is_server()) {
- QuicPriority temporary_priority = priority_;
- total_bytes_parsed = StripUint32(
- data, data_len, &headers_id_and_priority_buffer_, &temporary_priority);
- if (total_bytes_parsed > 0 && headers_id_and_priority_buffer_.size() == 0) {
- priority_parsed_ = true;
-
- // Spdy priorities are inverted, so the highest numerical value is the
- // lowest legal priority.
- if (temporary_priority > QuicUtils::LowestPriority()) {
- session()->connection()->SendConnectionClose(QUIC_INVALID_PRIORITY);
- return 0;
- }
- priority_ = temporary_priority;
- }
- data += total_bytes_parsed;
- data_len -= total_bytes_parsed;
- }
- if (data_len > 0 && headers_id_ == 0u) {
- // The headers ID has not yet been read. Strip it from the beginning of
- // the data stream.
- total_bytes_parsed += StripUint32(
- data, data_len, &headers_id_and_priority_buffer_, &headers_id_);
- }
- return total_bytes_parsed;
-}
-
bool QuicDataStream::FinishedReadingHeaders() {
return headers_decompressed_ && decompressed_headers_.empty();
}
diff --git a/chromium/net/quic/quic_data_stream.h b/chromium/net/quic/quic_data_stream.h
index 84990439e55..1af9004ef33 100644
--- a/chromium/net/quic/quic_data_stream.h
+++ b/chromium/net/quic/quic_data_stream.h
@@ -13,14 +13,15 @@
#include <list>
+#include "base/basictypes.h"
#include "base/strings/string_piece.h"
#include "net/base/iovec.h"
#include "net/base/net_export.h"
#include "net/quic/quic_ack_notifier.h"
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
+#include "net/quic/quic_protocol.h"
#include "net/quic/quic_stream_sequencer.h"
#include "net/quic/reliable_quic_stream.h"
+#include "net/spdy/spdy_framer.h"
namespace net {
@@ -34,8 +35,7 @@ class QuicSession;
class SSLInfo;
// All this does right now is send data to subclasses via the sequencer.
-class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream,
- public QuicSpdyDecompressor::Visitor {
+class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream {
public:
// Visitor receives callbacks from the stream.
class Visitor {
@@ -58,19 +58,39 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream,
// ReliableQuicStream implementation
virtual void OnClose() OVERRIDE;
+ virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE;
// By default, this is the same as priority(), however it allows streams
// to temporarily alter effective priority. For example if a SPDY stream has
// compressed but not written headers it can write the headers with a higher
// priority.
virtual QuicPriority EffectivePriority() const OVERRIDE;
- virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE;
-
- // QuicSpdyDecompressor::Visitor implementation.
- virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE;
- virtual void OnDecompressionError() OVERRIDE;
+ // Overridden by subclasses to process data. The headers will be delivered
+ // via OnStreamHeaders, so only data will be delivered through this method.
virtual uint32 ProcessData(const char* data, uint32 data_len) = 0;
+ // Called by the session when decompressed headers data is received
+ // for this stream.
+ // May be called multiple times, with each call providing additional headers
+ // data until OnStreamHeadersComplete is called.
+ virtual void OnStreamHeaders(base::StringPiece headers_data);
+
+ // Called by the session when headers with a priority have been received
+ // for this stream. This method will only be called for server streams.
+ virtual void OnStreamHeadersPriority(QuicPriority priority);
+
+ // Called by the session when decompressed headers have been completely
+ // delilvered to this stream. If |fin| is true, then this stream
+ // should be closed; no more data will be sent by the peer.
+ virtual void OnStreamHeadersComplete(bool fin, size_t frame_len);
+
+ // Writes the headers contained in |header_block| to the dedicated
+ // headers stream.
+ virtual size_t WriteHeaders(
+ const SpdyHeaderBlock& header_block,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
// This block of functions wraps the sequencer's functions of the same
// name. These methods return uncompressed data until that has
// been fully processed. Then they simply delegate to the sequencer.
@@ -80,18 +100,12 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream,
virtual bool IsDoneReading() const;
virtual bool HasBytesToRead() const;
- // Called by the session when a decompression blocked stream
- // becomes unblocked.
- virtual void OnDecompressorAvailable();
-
void set_visitor(Visitor* visitor) { visitor_ = visitor; }
bool headers_decompressed() const { return headers_decompressed_; }
const IPEndPoint& GetPeerAddress();
- QuicSpdyCompressor* compressor();
-
// Gets the SSL connection information.
bool GetSSLInfo(SSLInfo* ssl_info);
@@ -110,8 +124,6 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream,
uint32 ProcessHeaderData();
- uint32 StripPriorityAndHeaderId(const char* data, uint32 data_len);
-
bool FinishedReadingHeaders();
Visitor* visitor_;
@@ -119,11 +131,6 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream,
bool headers_decompressed_;
// The priority of the stream, once parsed.
QuicPriority priority_;
- // ID of the header block sent by the peer, once parsed.
- QuicHeaderId headers_id_;
- // Buffer into which we write bytes from priority_ and headers_id_
- // until each is fully parsed.
- string headers_id_and_priority_buffer_;
// Contains a copy of the decompressed headers until they are consumed
// via ProcessData or Readv.
string decompressed_headers_;
diff --git a/chromium/net/quic/quic_data_stream_test.cc b/chromium/net/quic/quic_data_stream_test.cc
index 551ef445715..b72f3cebf06 100644
--- a/chromium/net/quic/quic_data_stream_test.cc
+++ b/chromium/net/quic/quic_data_stream_test.cc
@@ -6,28 +6,30 @@
#include "net/quic/quic_ack_notifier.h"
#include "net/quic/quic_connection.h"
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
+#include "net/quic/quic_write_blocked_list.h"
#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
#include "net/quic/test_tools/quic_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
using base::StringPiece;
using std::min;
-using testing::_;
+using testing::AnyNumber;
using testing::InSequence;
using testing::Return;
using testing::SaveArg;
-using testing::StrEq;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace test {
namespace {
-const QuicGuid kStreamId = 3;
const bool kIsServer = true;
const bool kShouldProcessData = true;
@@ -57,7 +59,7 @@ class TestStream : public QuicDataStream {
string data_;
};
-class QuicDataStreamTest : public ::testing::TestWithParam<bool> {
+class QuicDataStreamTest : public ::testing::TestWithParam<QuicVersion> {
public:
QuicDataStreamTest() {
headers_[":host"] = "www.google.com";
@@ -90,16 +92,15 @@ class QuicDataStreamTest : public ::testing::TestWithParam<bool> {
}
void Initialize(bool stream_should_process_data) {
- connection_ = new StrictMock<MockConnection>(kIsServer);
- session_.reset(new StrictMock<MockSession>(connection_));
- stream_.reset(new TestStream(kStreamId, session_.get(),
- stream_should_process_data));
- stream2_.reset(new TestStream(kStreamId + 2, session_.get(),
+ connection_ = new testing::StrictMock<MockConnection>(
+ kIsServer, SupportedVersions(GetParam()));
+ session_.reset(new testing::StrictMock<MockSession>(connection_));
+ stream_.reset(new TestStream(kClientDataStreamId1, session_.get(),
stream_should_process_data));
- compressor_.reset(new QuicSpdyCompressor());
- decompressor_.reset(new QuicSpdyDecompressor);
+ stream2_.reset(new TestStream(kClientDataStreamId2, session_.get(),
+ stream_should_process_data));
write_blocked_list_ =
- QuicSessionPeer::GetWriteblockedStreams(session_.get());
+ QuicSessionPeer::GetWriteBlockedStreams(session_.get());
}
protected:
@@ -107,172 +108,160 @@ class QuicDataStreamTest : public ::testing::TestWithParam<bool> {
scoped_ptr<MockSession> session_;
scoped_ptr<TestStream> stream_;
scoped_ptr<TestStream> stream2_;
- scoped_ptr<QuicSpdyCompressor> compressor_;
- scoped_ptr<QuicSpdyDecompressor> decompressor_;
SpdyHeaderBlock headers_;
- WriteBlockedList<QuicStreamId>* write_blocked_list_;
+ QuicWriteBlockedList* write_blocked_list_;
};
-TEST_F(QuicDataStreamTest, ProcessHeaders) {
- Initialize(kShouldProcessData);
-
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(compressed_headers));
-
- stream_->OnStreamFrame(frame);
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_), stream_->data());
- EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority());
-}
+INSTANTIATE_TEST_CASE_P(Tests, QuicDataStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
-TEST_F(QuicDataStreamTest, ProcessHeadersWithInvalidHeaderId) {
+TEST_P(QuicDataStreamTest, ProcessHeaders) {
Initialize(kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- compressed_headers[4] = '\xFF'; // Illegal header id.
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(compressed_headers));
-
- EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_HEADER_ID));
- stream_->OnStreamFrame(frame);
-}
-
-TEST_F(QuicDataStreamTest, ProcessHeadersWithInvalidPriority) {
- Initialize(kShouldProcessData);
-
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- compressed_headers[0] = '\xFF'; // Illegal priority.
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(compressed_headers));
-
- EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_PRIORITY));
- stream_->OnStreamFrame(frame);
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeadersPriority(QuicUtils::HighestPriority());
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority());
+ EXPECT_EQ(headers, stream_->data());
+ EXPECT_FALSE(stream_->IsDoneReading());
}
-TEST_F(QuicDataStreamTest, ProcessHeadersAndBody) {
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBody) {
Initialize(kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
string body = "this is the body";
- string data = compressed_headers + body;
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data));
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame);
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body,
- stream_->data());
-}
-TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyFragments) {
- Initialize(kShouldProcessData);
+ EXPECT_EQ(headers + body, stream_->data());
+}
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::LowestPriority(), headers_);
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragments) {
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
string body = "this is the body";
- string data = compressed_headers + body;
- for (size_t fragment_size = 1; fragment_size < data.size(); ++fragment_size) {
+ for (size_t fragment_size = 1; fragment_size < body.size();
+ ++fragment_size) {
Initialize(kShouldProcessData);
- for (size_t offset = 0; offset < data.size(); offset += fragment_size) {
- size_t remaining_data = data.length() - offset;
- StringPiece fragment(data.data() + offset,
+ for (size_t offset = 0; offset < headers.size();
+ offset += fragment_size) {
+ size_t remaining_data = headers.size() - offset;
+ StringPiece fragment(headers.data() + offset,
min(fragment_size, remaining_data));
- QuicStreamFrame frame(kStreamId, false, offset, MakeIOVector(fragment));
-
+ stream_->OnStreamHeaders(fragment);
+ }
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ for (size_t offset = 0; offset < body.size(); offset += fragment_size) {
+ size_t remaining_data = body.size() - offset;
+ StringPiece fragment(body.data() + offset,
+ min(fragment_size, remaining_data));
+ QuicStreamFrame frame(kClientDataStreamId1, false, offset,
+ MakeIOVector(fragment));
stream_->OnStreamFrame(frame);
}
- ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body,
+ ASSERT_EQ(headers + body,
stream_->data()) << "fragment_size: " << fragment_size;
}
+}
- for (size_t split_point = 1; split_point < data.size() - 1; ++split_point) {
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragmentsSplit) {
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body = "this is the body";
+
+ for (size_t split_point = 1; split_point < body.size() - 1; ++split_point) {
Initialize(kShouldProcessData);
+ StringPiece headers1(headers.data(), split_point);
+ stream_->OnStreamHeaders(headers1);
+
+ StringPiece headers2(headers.data() + split_point,
+ headers.size() - split_point);
+ stream_->OnStreamHeaders(headers2);
+ stream_->OnStreamHeadersComplete(false, headers.size());
- StringPiece fragment1(data.data(), split_point);
- QuicStreamFrame frame1(kStreamId, false, 0, MakeIOVector(fragment1));
+ StringPiece fragment1(body.data(), split_point);
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0,
+ MakeIOVector(fragment1));
stream_->OnStreamFrame(frame1);
- StringPiece fragment2(data.data() + split_point, data.size() - split_point);
- QuicStreamFrame frame2(
- kStreamId, false, split_point, MakeIOVector(fragment2));
+ StringPiece fragment2(body.data() + split_point,
+ body.size() - split_point);
+ QuicStreamFrame frame2(kClientDataStreamId1, false, split_point,
+ MakeIOVector(fragment2));
stream_->OnStreamFrame(frame2);
- ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body,
+ ASSERT_EQ(headers + body,
stream_->data()) << "split_point: " << split_point;
}
- EXPECT_EQ(QuicUtils::LowestPriority(), stream_->EffectivePriority());
}
-TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyReadv) {
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyReadv) {
Initialize(!kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
string body = "this is the body";
- string data = compressed_headers + body;
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data));
- string uncompressed_headers =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
- string uncompressed_data = uncompressed_headers + body;
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame);
- EXPECT_EQ(uncompressed_headers, stream_->data());
char buffer[2048];
- ASSERT_LT(data.length(), arraysize(buffer));
+ ASSERT_LT(headers.length() + body.length(), arraysize(buffer));
struct iovec vec;
vec.iov_base = buffer;
vec.iov_len = arraysize(buffer);
size_t bytes_read = stream_->Readv(&vec, 1);
- EXPECT_EQ(uncompressed_headers.length(), bytes_read);
- EXPECT_EQ(uncompressed_headers, string(buffer, bytes_read));
+ EXPECT_EQ(headers.length(), bytes_read);
+ EXPECT_EQ(headers, string(buffer, bytes_read));
bytes_read = stream_->Readv(&vec, 1);
EXPECT_EQ(body.length(), bytes_read);
EXPECT_EQ(body, string(buffer, bytes_read));
}
-TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyIncrementalReadv) {
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyIncrementalReadv) {
Initialize(!kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
string body = "this is the body";
- string data = compressed_headers + body;
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data));
- string uncompressed_headers =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
- string uncompressed_data = uncompressed_headers + body;
-
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame);
- EXPECT_EQ(uncompressed_headers, stream_->data());
char buffer[1];
struct iovec vec;
vec.iov_base = buffer;
vec.iov_len = arraysize(buffer);
- for (size_t i = 0; i < uncompressed_data.length(); ++i) {
+
+ string data = headers + body;
+ for (size_t i = 0; i < data.length(); ++i) {
size_t bytes_read = stream_->Readv(&vec, 1);
ASSERT_EQ(1u, bytes_read);
- EXPECT_EQ(uncompressed_data.data()[i], buffer[0]);
+ EXPECT_EQ(data.data()[i], buffer[0]);
}
}
-TEST_F(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) {
+TEST_P(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) {
Initialize(!kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
string body = "this is the body";
- string data = compressed_headers + body;
- QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data));
- string uncompressed_headers =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
- string uncompressed_data = uncompressed_headers + body;
-
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame);
- EXPECT_EQ(uncompressed_headers, stream_->data());
char buffer1[1];
char buffer2[1];
@@ -281,168 +270,302 @@ TEST_F(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) {
vec[0].iov_len = arraysize(buffer1);
vec[1].iov_base = buffer2;
vec[1].iov_len = arraysize(buffer2);
- for (size_t i = 0; i < uncompressed_data.length(); i += 2) {
+ string data = headers + body;
+ for (size_t i = 0; i < data.length(); i += 2) {
size_t bytes_read = stream_->Readv(vec, 2);
ASSERT_EQ(2u, bytes_read) << i;
- ASSERT_EQ(uncompressed_data.data()[i], buffer1[0]) << i;
- ASSERT_EQ(uncompressed_data.data()[i + 1], buffer2[0]) << i;
+ ASSERT_EQ(data.data()[i], buffer1[0]) << i;
+ ASSERT_EQ(data.data()[i + 1], buffer2[0]) << i;
}
}
-TEST_F(QuicDataStreamTest, ProcessCorruptHeadersEarly) {
+TEST_P(QuicDataStreamTest, StreamFlowControlBlocked) {
+ // Tests that we send a BLOCKED frame to the peer when we attempt to write,
+ // but are flow control blocked.
+ if (GetParam() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
Initialize(kShouldProcessData);
- string compressed_headers1 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame1(
- stream_->id(), false, 0, MakeIOVector(compressed_headers1));
- string decompressed_headers1 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- headers_["content-type"] = "text/plain";
- string compressed_headers2 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- // Corrupt the compressed data.
- compressed_headers2[compressed_headers2.length() - 1] ^= 0xA1;
- QuicStreamFrame frame2(
- stream2_->id(), false, 0, MakeIOVector(compressed_headers2));
- string decompressed_headers2 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- // Deliver frame2 to stream2 out of order. The decompressor is not
- // available yet, so no data will be processed. The compressed data
- // will be buffered until OnDecompressorAvailable() is called
- // to process it.
- stream2_->OnStreamFrame(frame2);
- EXPECT_EQ("", stream2_->data());
+ // Set a small flow control limit.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetSendWindowOffset(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::SendWindowOffset(
+ stream_->flow_controller()));
+
+ // Try to send more data than the flow control limit allows.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body;
+ const uint64 kOverflow = 15;
+ GenerateBody(&body, kWindow + kOverflow);
+
+ EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1));
+ EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(kWindow, true)));
+ stream_->WriteOrBufferData(body, false, NULL);
+
+ // Should have sent as much as possible, resulting in no send window left.
+ EXPECT_EQ(0u,
+ QuicFlowControllerPeer::SendWindowSize(stream_->flow_controller()));
+
+ // And we should have queued the overflowed data.
+ EXPECT_EQ(kOverflow,
+ ReliableQuicStreamPeer::SizeOfQueuedData(stream_.get()));
+}
+
+TEST_P(QuicDataStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) {
+ // The flow control receive window decreases whenever we add new bytes to the
+ // sequencer, whether they are consumed immediately or buffered. However we
+ // only send WINDOW_UPDATE frames based on increasing number of bytes
+ // consumed.
+ if (GetParam() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+ // Don't process data - it will be buffered instead.
+ Initialize(!kShouldProcessData);
- // Now deliver frame1 to stream1. The decompressor is available so
- // the data will be processed, and the decompressor will become
- // available for stream2.
+ // Expect no WINDOW_UPDATE frames to be sent.
+ EXPECT_CALL(*connection_, SendWindowUpdate(_, _)).Times(0);
+
+ // Set a small flow control receive window.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream receives enough data to fill a fraction of the receive window.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body;
+ GenerateBody(&body, kWindow / 3);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame1);
- EXPECT_EQ(decompressed_headers1, stream_->data());
-
- // Verify that the decompressor is available, and inform stream2
- // that it can now decompress the buffered compressed data. Since
- // the compressed data is corrupt, the stream will shutdown the session.
- EXPECT_EQ(2u, session_->decompressor()->current_header_id());
- EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE));
- stream2_->OnDecompressorAvailable();
- EXPECT_EQ("", stream2_->data());
+ EXPECT_EQ(kWindow - (kWindow / 3), QuicFlowControllerPeer::ReceiveWindowSize(
+ stream_->flow_controller()));
+
+ // Now receive another frame which results in the receive window being over
+ // half full. This should all be buffered, decreasing the receive window but
+ // not sending WINDOW_UPDATE.
+ QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3,
+ MakeIOVector(body));
+ stream_->OnStreamFrame(frame2);
+ EXPECT_EQ(
+ kWindow - (2 * kWindow / 3),
+ QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller()));
}
-TEST_F(QuicDataStreamTest, ProcessPartialHeadersEarly) {
- Initialize(kShouldProcessData);
+TEST_P(QuicDataStreamTest, StreamFlowControlWindowUpdate) {
+ // Tests that on receipt of data, the stream updates its receive window offset
+ // appropriately, and sends WINDOW_UPDATE frames when its receive window drops
+ // too low.
+ if (GetParam() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
- string compressed_headers1 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame1(
- stream_->id(), false, 0, MakeIOVector(compressed_headers1));
- string decompressed_headers1 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- headers_["content-type"] = "text/plain";
- string compressed_headers2 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- string partial_compressed_headers =
- compressed_headers2.substr(0, compressed_headers2.length() / 2);
- QuicStreamFrame frame2(
- stream2_->id(), false, 0, MakeIOVector(partial_compressed_headers));
- string decompressed_headers2 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- // Deliver frame2 to stream2 out of order. The decompressor is not
- // available yet, so no data will be processed. The compressed data
- // will be buffered until OnDecompressorAvailable() is called
- // to process it.
- stream2_->OnStreamFrame(frame2);
- EXPECT_EQ("", stream2_->data());
+ Initialize(kShouldProcessData);
- // Now deliver frame1 to stream1. The decompressor is available so
- // the data will be processed, and the decompressor will become
- // available for stream2.
+ // Set a small flow control limit.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream receives enough data to fill a fraction of the receive window.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body;
+ GenerateBody(&body, kWindow / 3);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body));
stream_->OnStreamFrame(frame1);
- EXPECT_EQ(decompressed_headers1, stream_->data());
-
- // Verify that the decompressor is available, and inform stream2
- // that it can now decompress the buffered compressed data. Since
- // the compressed data is incomplete it will not be passed to
- // the stream.
- EXPECT_EQ(2u, session_->decompressor()->current_header_id());
- stream2_->OnDecompressorAvailable();
- EXPECT_EQ("", stream2_->data());
-
- // Now send remaining data and verify that we have now received the
- // compressed headers.
- string remaining_compressed_headers =
- compressed_headers2.substr(partial_compressed_headers.length());
-
- QuicStreamFrame frame3(stream2_->id(), false,
- partial_compressed_headers.length(),
- MakeIOVector(remaining_compressed_headers));
- stream2_->OnStreamFrame(frame3);
- EXPECT_EQ(decompressed_headers2, stream2_->data());
+ EXPECT_EQ(kWindow - (kWindow / 3), QuicFlowControllerPeer::ReceiveWindowSize(
+ stream_->flow_controller()));
+
+ // Now receive another frame which results in the receive window being over
+ // half full. This will trigger the stream to increase its receive window
+ // offset and send a WINDOW_UPDATE. The result will be again an available
+ // window of kWindow bytes.
+ QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3,
+ MakeIOVector(body));
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(kClientDataStreamId1,
+ QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()) +
+ 2 * kWindow / 3));
+ stream_->OnStreamFrame(frame2);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowSize(
+ stream_->flow_controller()));
}
-TEST_F(QuicDataStreamTest, ProcessHeadersEarly) {
+TEST_P(QuicDataStreamTest, ConnectionFlowControlWindowUpdate) {
+ // Tests that on receipt of data, the connection updates its receive window
+ // offset appropriately, and sends WINDOW_UPDATE frames when its receive
+ // window drops too low.
+ if (GetParam() < QUIC_VERSION_19) {
+ return;
+ }
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+
Initialize(kShouldProcessData);
- string compressed_headers1 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame1(
- stream_->id(), false, 0, MakeIOVector(compressed_headers1));
- string decompressed_headers1 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- headers_["content-type"] = "text/plain";
- string compressed_headers2 = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame2(
- stream2_->id(), false, 0, MakeIOVector(compressed_headers2));
- string decompressed_headers2 =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
-
- // Deliver frame2 to stream2 out of order. The decompressor is not
- // available yet, so no data will be processed. The compressed data
- // will be buffered until OnDecompressorAvailable() is called
- // to process it.
+ // Set a small flow control limit for streams and connection.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream2_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream2_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(session_->flow_controller(),
+ kWindow);
+
+ // Supply headers to both streams so that they are happy to receive data.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeaders(headers);
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ stream2_->OnStreamHeaders(headers);
+ stream2_->OnStreamHeadersComplete(false, headers.size());
+
+ // Each stream gets a quarter window of data. This should not trigger a
+ // WINDOW_UPDATE for either stream, nor for the connection.
+ string body;
+ GenerateBody(&body, kWindow / 4);
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ stream_->OnStreamFrame(frame1);
+ QuicStreamFrame frame2(kClientDataStreamId2, false, 0, MakeIOVector(body));
stream2_->OnStreamFrame(frame2);
- EXPECT_EQ("", stream2_->data());
- // Now deliver frame1 to stream1. The decompressor is available so
- // the data will be processed, and the decompressor will become
- // available for stream2.
- stream_->OnStreamFrame(frame1);
- EXPECT_EQ(decompressed_headers1, stream_->data());
+ // Now receive a further single byte on one stream - again this does not
+ // trigger a stream WINDOW_UPDATE, but now the connection flow control window
+ // is over half full and thus a connection WINDOW_UPDATE is sent.
+ EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId1, _)).Times(0);
+ EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId2, _)).Times(0);
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(0, QuicFlowControllerPeer::ReceiveWindowOffset(
+ session_->flow_controller()) +
+ 1 + kWindow / 2));
+ QuicStreamFrame frame3(kClientDataStreamId1, false, (kWindow / 4),
+ MakeIOVector("a"));
+ stream_->OnStreamFrame(frame3);
+}
+
+TEST_P(QuicDataStreamTest, StreamFlowControlViolation) {
+ // Tests that on if the peer sends too much data (i.e. violates the flow
+ // control protocol), then we terminate the connection.
+ if (GetParam() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+ // Stream should not process data, so that data gets buffered in the
+ // sequencer, triggering flow control limits.
+ Initialize(!kShouldProcessData);
- // Verify that the decompressor is available, and inform stream2
- // that it can now decompress the buffered compressed data.
- EXPECT_EQ(2u, session_->decompressor()->current_header_id());
- stream2_->OnDecompressorAvailable();
- EXPECT_EQ(decompressed_headers2, stream2_->data());
+ // Set a small flow control limit.
+ const uint64 kWindow = 50;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ // Receive data to overflow the window, violating flow control.
+ string body;
+ GenerateBody(&body, kWindow + 1);
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA));
+ stream_->OnStreamFrame(frame);
}
-TEST_F(QuicDataStreamTest, ProcessHeadersDelay) {
+TEST_P(QuicDataStreamTest, ConnectionFlowControlViolation) {
+ // Tests that on if the peer sends too much data (i.e. violates the flow
+ // control protocol), at the connection level (rather than the stream level)
+ // then we terminate the connection.
+ if (GetParam() < QUIC_VERSION_19) {
+ return;
+ }
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+
+ // Stream should not process data, so that data gets buffered in the
+ // sequencer, triggering flow control limits.
Initialize(!kShouldProcessData);
- string compressed_headers = compressor_->CompressHeadersWithPriority(
- QuicUtils::HighestPriority(), headers_);
- QuicStreamFrame frame1(
- stream_->id(), false, 0, MakeIOVector(compressed_headers));
- string decompressed_headers =
- SpdyUtils::SerializeUncompressedHeaders(headers_);
+ // Set a small flow control window on streams, and connection.
+ const uint64 kStreamWindow = 50;
+ const uint64 kConnectionWindow = 10;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kStreamWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(),
+ kConnectionWindow);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ // Send enough data to overflow the connection level flow control window.
+ string body;
+ GenerateBody(&body, kConnectionWindow + 1);
+ EXPECT_LT(body.size(), kStreamWindow);
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA));
+ stream_->OnStreamFrame(frame);
+}
- // Send the headers to the stream and verify they were decompressed.
- stream_->OnStreamFrame(frame1);
- EXPECT_EQ(2u, session_->decompressor()->current_header_id());
-
- // Verify that we are now able to handle the body data,
- // even though the stream has not processed the headers.
- EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_HEADER_ID))
- .Times(0);
- QuicStreamFrame frame2(stream_->id(), false, compressed_headers.length(),
- MakeIOVector("body data"));
- stream_->OnStreamFrame(frame2);
+TEST_P(QuicDataStreamTest, StreamFlowControlFinNotBlocked) {
+ // An attempt to write a FIN with no data should not be flow control blocked,
+ // even if the send window is 0.
+ if (GetParam() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+ Initialize(kShouldProcessData);
+
+ // Set a flow control limit of zero.
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), 0);
+ EXPECT_EQ(0u, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Send a frame with a FIN but no data. This should not be blocked.
+ string body = "";
+ bool fin = true;
+
+ EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1)).Times(0);
+ EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, fin)));
+
+ stream_->WriteOrBufferData(body, fin, NULL);
}
} // namespace
diff --git a/chromium/net/quic/quic_data_writer.cc b/chromium/net/quic/quic_data_writer.cc
index b7aff26fe01..70b0fdaed31 100644
--- a/chromium/net/quic/quic_data_writer.cc
+++ b/chromium/net/quic/quic_data_writer.cc
@@ -167,7 +167,10 @@ void QuicDataWriter::WritePadding() {
}
bool QuicDataWriter::WriteUInt8ToOffset(uint8 value, size_t offset) {
- DCHECK_LT(offset, capacity_);
+ if (offset >= capacity_) {
+ LOG(DFATAL) << "offset: " << offset << " >= capacity: " << capacity_;
+ return false;
+ }
size_t latched_length = length_;
length_ = offset;
bool success = WriteUInt8(value);
diff --git a/chromium/net/quic/quic_data_writer.h b/chromium/net/quic/quic_data_writer.h
index f8a3751bf1b..0e14e87c692 100644
--- a/chromium/net/quic/quic_data_writer.h
+++ b/chromium/net/quic/quic_data_writer.h
@@ -74,6 +74,8 @@ class NET_EXPORT_PRIVATE QuicDataWriter {
char* buffer_;
size_t capacity_; // Allocation size of payload (or -1 if buffer is const).
size_t length_; // Current length of the buffer.
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDataWriter);
};
} // namespace net
diff --git a/chromium/net/quic/quic_data_writer_test.cc b/chromium/net/quic/quic_data_writer_test.cc
index 9106947b52e..07346c78891 100644
--- a/chromium/net/quic/quic_data_writer_test.cc
+++ b/chromium/net/quic/quic_data_writer_test.cc
@@ -6,6 +6,7 @@
#include "base/memory/scoped_ptr.h"
#include "net/quic/quic_data_reader.h"
+#include "net/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
@@ -32,13 +33,8 @@ TEST(QuicDataWriterTest, WriteUInt8ToOffset) {
TEST(QuicDataWriterDeathTest, WriteUInt8ToOffset) {
QuicDataWriter writer(4);
-#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST)
-#if !defined(DCHECK_ALWAYS_ON)
- EXPECT_DEBUG_DEATH(writer.WriteUInt8ToOffset(5, 4), "Check failed");
-#else
- EXPECT_DEATH(writer.WriteUInt8ToOffset(5, 4), "Check failed");
-#endif
-#endif
+ EXPECT_DFATAL(EXPECT_FALSE(writer.WriteUInt8ToOffset(5, 4)),
+ "offset: 4 >= capacity: 4");
}
TEST(QuicDataWriterTest, SanityCheckUFloat16Consts) {
diff --git a/chromium/net/quic/quic_default_packet_writer.cc b/chromium/net/quic/quic_default_packet_writer.cc
index 74b86af2a98..5dd80c6fd3d 100644
--- a/chromium/net/quic/quic_default_packet_writer.cc
+++ b/chromium/net/quic/quic_default_packet_writer.cc
@@ -17,7 +17,8 @@ QuicDefaultPacketWriter::QuicDefaultPacketWriter() : weak_factory_(this) {
QuicDefaultPacketWriter::QuicDefaultPacketWriter(DatagramClientSocket* socket)
: weak_factory_(this),
- socket_(socket) {
+ socket_(socket),
+ write_blocked_(false) {
}
QuicDefaultPacketWriter::~QuicDefaultPacketWriter() {}
@@ -25,10 +26,10 @@ QuicDefaultPacketWriter::~QuicDefaultPacketWriter() {}
WriteResult QuicDefaultPacketWriter::WritePacket(
const char* buffer, size_t buf_len,
const net::IPAddressNumber& self_address,
- const net::IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) {
+ const net::IPEndPoint& peer_address) {
scoped_refptr<StringIOBuffer> buf(
new StringIOBuffer(std::string(buffer, buf_len)));
+ DCHECK(!IsWriteBlocked());
int rv = socket_->Write(buf.get(),
buf_len,
base::Bind(&QuicDefaultPacketWriter::OnWriteComplete,
@@ -40,6 +41,7 @@ WriteResult QuicDefaultPacketWriter::WritePacket(
status = WRITE_STATUS_ERROR;
} else {
status = WRITE_STATUS_BLOCKED;
+ write_blocked_ = true;
}
}
@@ -52,8 +54,17 @@ bool QuicDefaultPacketWriter::IsWriteBlockedDataBuffered() const {
return true;
}
+bool QuicDefaultPacketWriter::IsWriteBlocked() const {
+ return write_blocked_;
+}
+
+void QuicDefaultPacketWriter::SetWritable() {
+ write_blocked_ = false;
+}
+
void QuicDefaultPacketWriter::OnWriteComplete(int rv) {
DCHECK_NE(rv, ERR_IO_PENDING);
+ write_blocked_ = false;
WriteResult result(rv < 0 ? WRITE_STATUS_ERROR : WRITE_STATUS_OK, rv);
connection_->OnPacketSent(result);
connection_->OnCanWrite();
diff --git a/chromium/net/quic/quic_default_packet_writer.h b/chromium/net/quic/quic_default_packet_writer.h
index f60c76ea9be..ed3b5975cee 100644
--- a/chromium/net/quic/quic_default_packet_writer.h
+++ b/chromium/net/quic/quic_default_packet_writer.h
@@ -15,7 +15,7 @@
namespace net {
-class QuicBlockedWriterInterface;
+struct WriteResult;
// Chrome specific packet writer which uses a DatagramClientSocket for writing
// data.
@@ -26,23 +26,31 @@ class NET_EXPORT_PRIVATE QuicDefaultPacketWriter : public QuicPacketWriter {
virtual ~QuicDefaultPacketWriter();
// QuicPacketWriter
- virtual WriteResult WritePacket(
- const char* buffer, size_t buf_len,
- const net::IPAddressNumber& self_address,
- const net::IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) OVERRIDE;
-
+ virtual WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) OVERRIDE;
virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual bool IsWriteBlocked() const OVERRIDE;
+ virtual void SetWritable() OVERRIDE;
void OnWriteComplete(int rv);
void SetConnection(QuicConnection* connection) {
connection_ = connection;
}
+ protected:
+ void set_write_blocked(bool is_blocked) {
+ write_blocked_ = is_blocked;
+ }
+
private:
base::WeakPtrFactory<QuicDefaultPacketWriter> weak_factory_;
DatagramClientSocket* socket_;
QuicConnection* connection_;
+ bool write_blocked_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDefaultPacketWriter);
};
} // namespace net
diff --git a/chromium/net/quic/quic_dispatcher.cc b/chromium/net/quic/quic_dispatcher.cc
new file mode 100644
index 00000000000..280622da381
--- /dev/null
+++ b/chromium/net/quic/quic_dispatcher.cc
@@ -0,0 +1,426 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_dispatcher.h"
+
+#include <errno.h>
+
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_time_wait_list_manager.h"
+#include "net/quic/quic_utils.h"
+
+namespace net {
+
+using base::StringPiece;
+using std::make_pair;
+using std::find;
+
+class DeleteSessionsAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher) {
+ }
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ dispatcher_->DeleteSessions();
+ return QuicTime::Zero();
+ }
+
+ private:
+ QuicDispatcher* dispatcher_;
+};
+
+class QuicDispatcher::QuicFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ explicit QuicFramerVisitor(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher),
+ connection_id_(0) {}
+
+ // QuicFramerVisitorInterface implementation
+ virtual void OnPacket() OVERRIDE {}
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE {
+ connection_id_ = header.connection_id;
+ return dispatcher_->OnUnauthenticatedPublicHeader(header);
+ }
+ virtual bool OnUnauthenticatedHeader(
+ const QuicPacketHeader& header) OVERRIDE {
+ dispatcher_->OnUnauthenticatedHeader(header);
+ return false;
+ }
+ virtual void OnError(QuicFramer* framer) OVERRIDE {
+ DVLOG(1) << QuicUtils::ErrorToString(framer->error());
+ }
+
+ virtual bool OnProtocolVersionMismatch(
+ QuicVersion /*received_version*/) OVERRIDE {
+ if (dispatcher_->time_wait_list_manager()->IsConnectionIdInTimeWait(
+ connection_id_)) {
+ // Keep processing after protocol mismatch - this will be dealt with by
+ // the TimeWaitListManager.
+ return true;
+ } else {
+ DLOG(DFATAL) << "Version mismatch, connection ID (" << connection_id_
+ << ") not in time wait list.";
+ return false;
+ }
+ }
+
+ // The following methods should never get called because we always return
+ // false from OnUnauthenticatedHeader(). As a result, we never process the
+ // payload of the packet.
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& /*packet*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& /*packet*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual bool OnPacketHeader(const QuicPacketHeader& /*header*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual void OnRevivedPacket() OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnFecProtectedPayload(StringPiece /*payload*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual bool OnStreamFrame(const QuicStreamFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnAckFrame(const QuicAckFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnStopWaitingFrame(
+ const QuicStopWaitingFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnPingFrame(const QuicPingFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame & /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& /*frame*/)
+ OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual void OnFecData(const QuicFecData& /*fec*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnPacketComplete() OVERRIDE {
+ DCHECK(false);
+ }
+
+ private:
+ QuicDispatcher* dispatcher_;
+
+ // Latched in OnUnauthenticatedPublicHeader for use later.
+ QuicConnectionId connection_id_;
+};
+
+QuicDispatcher::QuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ const QuicVersionVector& supported_versions,
+ QuicConnectionHelperInterface* helper)
+ : config_(config),
+ crypto_config_(crypto_config),
+ helper_(helper),
+ delete_sessions_alarm_(
+ helper_->CreateAlarm(new DeleteSessionsAlarm(this))),
+ supported_versions_(supported_versions),
+ supported_versions_no_flow_control_(supported_versions),
+ supported_versions_no_connection_flow_control_(supported_versions),
+ current_packet_(NULL),
+ framer_(supported_versions, /*unused*/ QuicTime::Zero(), true),
+ framer_visitor_(new QuicFramerVisitor(this)) {
+ framer_.set_visitor(framer_visitor_.get());
+}
+
+QuicDispatcher::~QuicDispatcher() {
+ STLDeleteValues(&session_map_);
+ STLDeleteElements(&closed_session_list_);
+}
+
+void QuicDispatcher::Initialize(QuicPacketWriter* writer) {
+ DCHECK(writer_ == NULL);
+ writer_.reset(writer);
+ time_wait_list_manager_.reset(CreateQuicTimeWaitListManager());
+
+ // Remove all versions > QUIC_VERSION_16 from the
+ // supported_versions_no_flow_control_ vector.
+ QuicVersionVector::iterator it =
+ find(supported_versions_no_flow_control_.begin(),
+ supported_versions_no_flow_control_.end(), QUIC_VERSION_17);
+ if (it != supported_versions_no_flow_control_.end()) {
+ supported_versions_no_flow_control_.erase(
+ supported_versions_no_flow_control_.begin(), it + 1);
+ }
+ CHECK(!supported_versions_no_flow_control_.empty());
+
+ // Remove all versions > QUIC_VERSION_18 from the
+ // supported_versions_no_connection_flow_control_ vector.
+ QuicVersionVector::iterator connection_it =
+ find(supported_versions_no_connection_flow_control_.begin(),
+ supported_versions_no_connection_flow_control_.end(),
+ QUIC_VERSION_19);
+ if (connection_it != supported_versions_no_connection_flow_control_.end()) {
+ supported_versions_no_connection_flow_control_.erase(
+ supported_versions_no_connection_flow_control_.begin(),
+ connection_it + 1);
+ }
+ CHECK(!supported_versions_no_connection_flow_control_.empty());
+}
+
+void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ const QuicEncryptedPacket& packet) {
+ current_server_address_ = server_address;
+ current_client_address_ = client_address;
+ current_packet_ = &packet;
+ // ProcessPacket will cause the packet to be dispatched in
+ // OnUnauthenticatedPublicHeader, or sent to the time wait list manager
+ // in OnAuthenticatedHeader.
+ framer_.ProcessPacket(packet);
+ // TODO(rjshade): Return a status describing if/why a packet was dropped,
+ // and log somehow. Maybe expose as a varz.
+}
+
+bool QuicDispatcher::OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) {
+ QuicSession* session = NULL;
+
+ QuicConnectionId connection_id = header.connection_id;
+ SessionMap::iterator it = session_map_.find(connection_id);
+ if (it == session_map_.end()) {
+ if (header.reset_flag) {
+ return false;
+ }
+ if (time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) {
+ return HandlePacketForTimeWait(header);
+ }
+
+ // Ensure the packet has a version negotiation bit set before creating a new
+ // session for it. All initial packets for a new connection are required to
+ // have the flag set. Otherwise it may be a stray packet.
+ if (header.version_flag) {
+ session = CreateQuicSession(connection_id, current_server_address_,
+ current_client_address_);
+ }
+
+ if (session == NULL) {
+ DVLOG(1) << "Failed to create session for " << connection_id;
+ // Add this connection_id fo the time-wait state, to safely reject future
+ // packets.
+
+ if (header.version_flag &&
+ !framer_.IsSupportedVersion(header.versions.front())) {
+ // TODO(ianswett): Produce a no-version version negotiation packet.
+ return false;
+ }
+
+ // Use the version in the packet if possible, otherwise assume the latest.
+ QuicVersion version = header.version_flag ? header.versions.front() :
+ supported_versions_.front();
+ time_wait_list_manager_->AddConnectionIdToTimeWait(
+ connection_id, version, NULL);
+ DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
+ return HandlePacketForTimeWait(header);
+ }
+ DVLOG(1) << "Created new session for " << connection_id;
+ session_map_.insert(make_pair(connection_id, session));
+ } else {
+ session = it->second;
+ }
+
+ session->connection()->ProcessUdpPacket(
+ current_server_address_, current_client_address_, *current_packet_);
+
+ // Do not parse the packet further. The session will process it completely.
+ return false;
+}
+
+void QuicDispatcher::OnUnauthenticatedHeader(const QuicPacketHeader& header) {
+ DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(
+ header.public_header.connection_id));
+ time_wait_list_manager_->ProcessPacket(current_server_address_,
+ current_client_address_,
+ header.public_header.connection_id,
+ header.packet_sequence_number,
+ *current_packet_);
+}
+
+void QuicDispatcher::CleanUpSession(SessionMap::iterator it) {
+ QuicConnection* connection = it->second->connection();
+ QuicEncryptedPacket* connection_close_packet =
+ connection->ReleaseConnectionClosePacket();
+ write_blocked_list_.erase(connection);
+ time_wait_list_manager_->AddConnectionIdToTimeWait(it->first,
+ connection->version(),
+ connection_close_packet);
+ session_map_.erase(it);
+}
+
+void QuicDispatcher::DeleteSessions() {
+ STLDeleteElements(&closed_session_list_);
+}
+
+void QuicDispatcher::OnCanWrite() {
+ // We got an EPOLLOUT: the socket should not be blocked.
+ writer_->SetWritable();
+
+ // Give each writer one attempt to write.
+ int num_writers = write_blocked_list_.size();
+ for (int i = 0; i < num_writers; ++i) {
+ if (write_blocked_list_.empty()) {
+ return;
+ }
+ QuicBlockedWriterInterface* blocked_writer =
+ write_blocked_list_.begin()->first;
+ write_blocked_list_.erase(write_blocked_list_.begin());
+ blocked_writer->OnCanWrite();
+ if (writer_->IsWriteBlocked()) {
+ // We were unable to write. Wait for the next EPOLLOUT. The writer is
+ // responsible for adding itself to the blocked list via OnWriteBlocked().
+ return;
+ }
+ }
+}
+
+bool QuicDispatcher::HasPendingWrites() const {
+ return !write_blocked_list_.empty();
+}
+
+void QuicDispatcher::Shutdown() {
+ while (!session_map_.empty()) {
+ QuicSession* session = session_map_.begin()->second;
+ session->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
+ // Validate that the session removes itself from the session map on close.
+ DCHECK(session_map_.empty() || session_map_.begin()->second != session);
+ }
+ DeleteSessions();
+}
+
+void QuicDispatcher::OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) {
+ SessionMap::iterator it = session_map_.find(connection_id);
+ if (it == session_map_.end()) {
+ LOG(DFATAL) << "ConnectionId " << connection_id
+ << " does not exist in the session map. "
+ << "Error: " << QuicUtils::ErrorToString(error);
+ LOG(DFATAL) << base::debug::StackTrace().ToString();
+ return;
+ }
+ DVLOG_IF(1, error != QUIC_NO_ERROR) << "Closing connection ("
+ << connection_id
+ << ") due to error: "
+ << QuicUtils::ErrorToString(error);
+ if (closed_session_list_.empty()) {
+ delete_sessions_alarm_->Set(helper_->GetClock()->ApproximateNow());
+ }
+ closed_session_list_.push_back(it->second);
+ CleanUpSession(it);
+}
+
+void QuicDispatcher::OnWriteBlocked(QuicBlockedWriterInterface* writer) {
+ DCHECK(writer_->IsWriteBlocked());
+ write_blocked_list_.insert(make_pair(writer, true));
+}
+
+QuicSession* QuicDispatcher::CreateQuicSession(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address) {
+ QuicServerSession* session = new QuicServerSession(
+ config_,
+ CreateQuicConnection(connection_id, server_address, client_address),
+ this);
+ session->InitializeSession(crypto_config_);
+ return session;
+}
+
+QuicConnection* QuicDispatcher::CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address) {
+ if (FLAGS_enable_quic_stream_flow_control_2 &&
+ FLAGS_enable_quic_connection_flow_control_2) {
+ DVLOG(1) << "Creating QuicDispatcher with all versions.";
+ return new QuicConnection(connection_id, client_address, helper_,
+ writer_.get(), true, supported_versions_);
+ }
+
+ if (FLAGS_enable_quic_stream_flow_control_2 &&
+ !FLAGS_enable_quic_connection_flow_control_2) {
+ DVLOG(1) << "Connection flow control disabled, creating QuicDispatcher "
+ << "WITHOUT version 19 or higher.";
+ return new QuicConnection(connection_id, client_address, helper_,
+ writer_.get(), true,
+ supported_versions_no_connection_flow_control_);
+ }
+
+ DVLOG(1) << "Flow control disabled, creating QuicDispatcher WITHOUT "
+ << "version 17 or higher.";
+ return new QuicConnection(connection_id, client_address, helper_,
+ writer_.get(), true,
+ supported_versions_no_flow_control_);
+}
+
+QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() {
+ return new QuicTimeWaitListManager(
+ writer_.get(), this, helper_, supported_versions());
+}
+
+bool QuicDispatcher::HandlePacketForTimeWait(
+ const QuicPacketPublicHeader& header) {
+ if (header.reset_flag) {
+ // Public reset packets do not have sequence numbers, so ignore the packet.
+ return false;
+ }
+
+ // Switch the framer to the correct version, so that the sequence number can
+ // be parsed correctly.
+ framer_.set_version(time_wait_list_manager_->GetQuicVersionFromConnectionId(
+ header.connection_id));
+
+ // Continue parsing the packet to extract the sequence number. Then
+ // send it to the time wait manager in OnUnathenticatedHeader.
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_dispatcher.h b/chromium/net/quic/quic_dispatcher.h
new file mode 100644
index 00000000000..1f666742e3f
--- /dev/null
+++ b/chromium/net/quic/quic_dispatcher.h
@@ -0,0 +1,230 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A server side dispatcher which dispatches a given client's data to their
+// stream.
+
+#ifndef NET_QUIC_QUIC_DISPATCHER_H_
+#define NET_QUIC_QUIC_DISPATCHER_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_session.h"
+#include "net/quic/quic_time_wait_list_manager.h"
+
+#if defined(COMPILER_GCC)
+namespace BASE_HASH_NAMESPACE {
+template <>
+struct hash<net::QuicBlockedWriterInterface*> {
+ std::size_t operator()(const net::QuicBlockedWriterInterface* ptr) const {
+ return hash<size_t>()(reinterpret_cast<size_t>(ptr));
+ }
+};
+}
+#endif
+
+namespace net {
+
+class QuicConfig;
+class QuicCryptoServerConfig;
+class QuicPacketWriterWrapper;
+class QuicSession;
+class UDPServerSocket;
+
+namespace test {
+class QuicDispatcherPeer;
+} // namespace test
+
+class DeleteSessionsAlarm;
+
+class QuicDispatcher : public QuicServerSessionVisitor {
+ public:
+ // Ideally we'd have a linked_hash_set: the boolean is unused.
+ typedef linked_hash_map<QuicBlockedWriterInterface*, bool> WriteBlockedList;
+
+ // Due to the way delete_sessions_closure_ is registered, the Dispatcher
+ // must live until epoll_server Shutdown. |supported_versions| specifies the
+ // list of supported QUIC versions.
+ QuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ const QuicVersionVector& supported_versions,
+ QuicConnectionHelperInterface* helper);
+
+ virtual ~QuicDispatcher();
+
+ // Takes ownership of the packet writer
+ virtual void Initialize(QuicPacketWriter* writer);
+
+ // Process the incoming packet by creating a new session, passing it to
+ // an existing session, or passing it to the TimeWaitListManager.
+ virtual void ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ const QuicEncryptedPacket& packet);
+
+ // Called when the socket becomes writable to allow queued writes to happen.
+ virtual void OnCanWrite();
+
+ // Returns true if there's anything in the blocked writer list.
+ virtual bool HasPendingWrites() const;
+
+ // Sends ConnectionClose frames to all connected clients.
+ void Shutdown();
+
+ // QuicServerSessionVisitor interface implementation:
+ // Ensure that the closed connection is cleaned up asynchronously.
+ virtual void OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) OVERRIDE;
+
+ // Queues the blocked writer for later resumption.
+ virtual void OnWriteBlocked(QuicBlockedWriterInterface* writer) OVERRIDE;
+
+ typedef base::hash_map<QuicConnectionId, QuicSession*> SessionMap;
+
+ // Deletes all sessions on the closed session list and clears the list.
+ void DeleteSessions();
+
+ const SessionMap& session_map() const { return session_map_; }
+
+ WriteBlockedList* write_blocked_list() { return &write_blocked_list_; }
+
+ protected:
+ virtual QuicSession* CreateQuicSession(QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address);
+
+ virtual QuicConnection* CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address);
+
+ // Called by |framer_visitor_| when the public header has been parsed.
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header);
+
+ // Create and return the time wait list manager for this dispatcher, which
+ // will be owned by the dispatcher as time_wait_list_manager_
+ virtual QuicTimeWaitListManager* CreateQuicTimeWaitListManager();
+
+ // Replaces the packet writer with |writer|. Takes ownership of |writer|.
+ void set_writer(QuicPacketWriter* writer) {
+ writer_.reset(writer);
+ }
+
+ QuicTimeWaitListManager* time_wait_list_manager() {
+ return time_wait_list_manager_.get();
+ }
+
+ const QuicVersionVector& supported_versions() const {
+ return supported_versions_;
+ }
+
+ const QuicVersionVector& supported_versions_no_flow_control() const {
+ return supported_versions_no_flow_control_;
+ }
+
+ const QuicVersionVector& supported_versions_no_connection_flow_control()
+ const {
+ return supported_versions_no_connection_flow_control_;
+ }
+
+ const IPEndPoint& current_server_address() {
+ return current_server_address_;
+ }
+ const IPEndPoint& current_client_address() {
+ return current_client_address_;
+ }
+ const QuicEncryptedPacket& current_packet() {
+ return *current_packet_;
+ }
+
+ const QuicConfig& config() const { return config_; }
+
+ const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; }
+
+ QuicFramer* framer() { return &framer_; }
+
+ QuicConnectionHelperInterface* helper() { return helper_; }
+
+ QuicPacketWriter* writer() { return writer_.get(); }
+
+ private:
+ class QuicFramerVisitor;
+ friend class net::test::QuicDispatcherPeer;
+
+ // Called by |framer_visitor_| when the private header has been parsed
+ // of a data packet that is destined for the time wait manager.
+ void OnUnauthenticatedHeader(const QuicPacketHeader& header);
+
+ // Removes the session from the session map and write blocked list, and
+ // adds the ConnectionId to the time-wait list.
+ void CleanUpSession(SessionMap::iterator it);
+
+ bool HandlePacketForTimeWait(const QuicPacketPublicHeader& header);
+
+ const QuicConfig& config_;
+
+ const QuicCryptoServerConfig& crypto_config_;
+
+ // The list of connections waiting to write.
+ WriteBlockedList write_blocked_list_;
+
+ SessionMap session_map_;
+
+ // Entity that manages connection_ids in time wait state.
+ scoped_ptr<QuicTimeWaitListManager> time_wait_list_manager_;
+
+ // The helper used for all connections. Owned by the server.
+ QuicConnectionHelperInterface* helper_;
+
+ // An alarm which deletes closed sessions.
+ scoped_ptr<QuicAlarm> delete_sessions_alarm_;
+
+ // The list of closed but not-yet-deleted sessions.
+ std::list<QuicSession*> closed_session_list_;
+
+ // The writer to write to the socket with.
+ scoped_ptr<QuicPacketWriter> writer_;
+
+ // This vector contains QUIC versions which we currently support.
+ // This should be ordered such that the highest supported version is the first
+ // element, with subsequent elements in descending order (versions can be
+ // skipped as necessary).
+ const QuicVersionVector supported_versions_;
+
+ // Versions which do not support flow control (introduced in QUIC_VERSION_17).
+ // This is used to construct new QuicConnections when flow control is disabled
+ // via flag.
+ // TODO(rjshade): Remove this when
+ // FLAGS_enable_quic_stream_flow_control_2 is removed.
+ QuicVersionVector supported_versions_no_flow_control_;
+ // Versions which do not support *connection* flow control (introduced in
+ // QUIC_VERSION_19).
+ // This is used to construct new QuicConnections when connection flow control
+ // is disabled via flag.
+ // TODO(rjshade): Remove this when
+ // FLAGS_enable_quic_connection_flow_control_2 is removed.
+ QuicVersionVector supported_versions_no_connection_flow_control_;
+
+ // Information about the packet currently being handled.
+ IPEndPoint current_client_address_;
+ IPEndPoint current_server_address_;
+ const QuicEncryptedPacket* current_packet_;
+
+ QuicFramer framer_;
+ scoped_ptr<QuicFramerVisitor> framer_visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDispatcher);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_DISPATCHER_H_
diff --git a/chromium/net/quic/quic_end_to_end_unittest.cc b/chromium/net/quic/quic_end_to_end_unittest.cc
index 90c670bfbeb..89622b0483d 100644
--- a/chromium/net/quic/quic_end_to_end_unittest.cc
+++ b/chromium/net/quic/quic_end_to_end_unittest.cc
@@ -17,20 +17,21 @@
#include "net/http/http_network_session.h"
#include "net/http/http_network_transaction.h"
#include "net/http/http_server_properties_impl.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/http/transport_security_state.h"
#include "net/proxy/proxy_service.h"
+#include "net/quic/test_tools/quic_test_utils.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_server.h"
#include "net/tools/quic/test_tools/quic_in_memory_cache_peer.h"
#include "net/tools/quic/test_tools/server_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
-extern int FLAGS_fake_packet_loss_percentage;
-
using base::StringPiece;
using net::tools::QuicInMemoryCache;
+using net::tools::QuicServer;
using net::tools::test::QuicInMemoryCachePeer;
using net::tools::test::ServerThread;
@@ -48,14 +49,11 @@ class TestTransactionFactory : public HttpTransactionFactory {
: session_(new HttpNetworkSession(params)) {}
virtual ~TestTransactionFactory() {
- FLAGS_fake_packet_loss_percentage = 0;
}
// HttpTransactionFactory methods
virtual int CreateTransaction(RequestPriority priority,
- scoped_ptr<HttpTransaction>* trans,
- HttpTransactionDelegate* delegate) OVERRIDE {
- EXPECT_TRUE(delegate == NULL);
+ scoped_ptr<HttpTransaction>* trans) OVERRIDE {
trans->reset(new HttpNetworkTransaction(priority, session_));
return OK;
}
@@ -137,13 +135,20 @@ class QuicEndToEndTest : public PlatformTest {
CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &ip));
server_address_ = IPEndPoint(ip, 0);
server_config_.SetDefaults();
- server_thread_.reset(new ServerThread(server_address_, server_config_,
- QuicSupportedVersions(),
- strike_register_no_startup_period_));
- server_thread_->Start();
- server_thread_->WaitForServerStartup();
+ server_config_.SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ server_config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ server_config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ server_thread_.reset(new ServerThread(
+ new QuicServer(server_config_, QuicSupportedVersions()),
+ server_address_,
+ strike_register_no_startup_period_));
+ server_thread_->Initialize();
server_address_ = IPEndPoint(server_address_.address(),
server_thread_->GetPort());
+ server_thread_->Start();
server_started_ = true;
}
diff --git a/chromium/net/quic/quic_fec_group.cc b/chromium/net/quic/quic_fec_group.cc
index 4be48100e76..344f21679ec 100644
--- a/chromium/net/quic/quic_fec_group.cc
+++ b/chromium/net/quic/quic_fec_group.cc
@@ -6,6 +6,7 @@
#include <limits>
+#include "base/basictypes.h"
#include "base/logging.h"
using base::StringPiece;
@@ -22,12 +23,13 @@ QuicFecGroup::QuicFecGroup()
: min_protected_packet_(kNoSequenceNumber),
max_protected_packet_(kNoSequenceNumber),
payload_parity_len_(0),
- entropy_parity_(false) {
+ effective_encryption_level_(NUM_ENCRYPTION_LEVELS) {
}
QuicFecGroup::~QuicFecGroup() {}
-bool QuicFecGroup::Update(const QuicPacketHeader& header,
+bool QuicFecGroup::Update(EncryptionLevel encryption_level,
+ const QuicPacketHeader& header,
StringPiece decrypted_payload) {
if (received_packets_.count(header.packet_sequence_number) != 0) {
return false;
@@ -40,34 +42,39 @@ bool QuicFecGroup::Update(const QuicPacketHeader& header,
<< header.packet_sequence_number;
return false;
}
- if (!UpdateParity(decrypted_payload, header.entropy_flag)) {
+ if (!UpdateParity(decrypted_payload)) {
return false;
}
received_packets_.insert(header.packet_sequence_number);
+ if (encryption_level < effective_encryption_level_) {
+ effective_encryption_level_ = encryption_level;
+ }
return true;
}
bool QuicFecGroup::UpdateFec(
+ EncryptionLevel encryption_level,
QuicPacketSequenceNumber fec_packet_sequence_number,
- bool fec_packet_entropy,
const QuicFecData& fec) {
if (min_protected_packet_ != kNoSequenceNumber) {
return false;
}
SequenceNumberSet::const_iterator it = received_packets_.begin();
while (it != received_packets_.end()) {
- if ((*it < fec.fec_group) ||
- (*it >= fec_packet_sequence_number)) {
+ if ((*it < fec.fec_group) || (*it >= fec_packet_sequence_number)) {
DLOG(ERROR) << "FEC group does not cover received packet: " << *it;
return false;
}
++it;
}
- if (!UpdateParity(fec.redundancy, fec_packet_entropy)) {
+ if (!UpdateParity(fec.redundancy)) {
return false;
}
min_protected_packet_ = fec.fec_group;
max_protected_packet_ = fec_packet_sequence_number - 1;
+ if (encryption_level < effective_encryption_level_) {
+ effective_encryption_level_ = encryption_level;
+ }
return true;
}
@@ -109,7 +116,7 @@ size_t QuicFecGroup::Revive(QuicPacketHeader* header,
}
header->packet_sequence_number = missing;
- header->entropy_flag = entropy_parity_;
+ header->entropy_flag = false; // Unknown entropy.
received_packets_.insert(missing);
return payload_parity_len_;
@@ -119,12 +126,12 @@ bool QuicFecGroup::ProtectsPacketsBefore(QuicPacketSequenceNumber num) const {
if (max_protected_packet_ != kNoSequenceNumber) {
return max_protected_packet_ < num;
}
- // Since we might not yet have recevied the FEC packet, we must check
+ // Since we might not yet have received the FEC packet, we must check
// the packets we have received.
return *received_packets_.begin() < num;
}
-bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) {
+bool QuicFecGroup::UpdateParity(StringPiece payload) {
DCHECK_LE(payload.size(), kMaxPacketSize);
if (payload.size() > kMaxPacketSize) {
DLOG(ERROR) << "Illegal payload size: " << payload.size();
@@ -143,7 +150,6 @@ bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) {
memset(payload_parity_ + payload.size(), 0,
kMaxPacketSize - payload.size());
}
- entropy_parity_ = entropy;
return true;
}
// Update the parity by XORing in the data (padding with 0s if necessary).
@@ -151,8 +157,6 @@ bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) {
uint8 byte = i < payload.size() ? payload[i] : 0x00;
payload_parity_[i] ^= byte;
}
- // xor of boolean values.
- entropy_parity_ = (entropy_parity_ != entropy);
return true;
}
diff --git a/chromium/net/quic/quic_fec_group.h b/chromium/net/quic/quic_fec_group.h
index d905d03236e..718d09f83ad 100644
--- a/chromium/net/quic/quic_fec_group.h
+++ b/chromium/net/quic/quic_fec_group.h
@@ -9,8 +9,6 @@
#ifndef NET_QUIC_QUIC_FEC_GROUP_H_
#define NET_QUIC_QUIC_FEC_GROUP_H_
-#include <set>
-
#include "base/strings/string_piece.h"
#include "net/quic/quic_protocol.h"
@@ -21,17 +19,19 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
QuicFecGroup();
~QuicFecGroup();
- // Updates the FEC group based on the delivery of a data packet.
- // Returns false if this packet has already been seen, true otherwise.
- bool Update(const QuicPacketHeader& header,
+ // Updates the FEC group based on the delivery of a data packet decrypted at
+ // |encryption_level|. Returns false if this packet has already been seen,
+ // true otherwise.
+ bool Update(EncryptionLevel encryption_level,
+ const QuicPacketHeader& header,
base::StringPiece decrypted_payload);
- // Updates the FEC group based on the delivery of an FEC packet.
- // Returns false if this packet has already been seen or if it does
- // not claim to protect all the packets previously seen in this group.
- // |fec_packet_entropy|: XOR of entropy of all packets in the fec group.
- bool UpdateFec(QuicPacketSequenceNumber fec_packet_sequence_number,
- bool fec_packet_entropy,
+ // Updates the FEC group based on the delivery of an FEC packet decrypted at
+ // |encryption_level|. Returns false if this packet has already been seen or
+ // if it does not claim to protect all the packets previously seen in this
+ // group.
+ bool UpdateFec(EncryptionLevel encryption_level,
+ QuicPacketSequenceNumber fec_packet_sequence_number,
const QuicFecData& fec);
// Returns true if a packet can be revived from this FEC group.
@@ -57,10 +57,6 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
return base::StringPiece(payload_parity_, payload_parity_len_);
}
- bool entropy_parity() const {
- return entropy_parity_;
- }
-
QuicPacketSequenceNumber min_protected_packet() const {
return min_protected_packet_;
}
@@ -69,8 +65,13 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
return received_packets_.size();
}
+ // Returns the effective encryption level of the FEC group.
+ EncryptionLevel effective_encryption_level() const {
+ return effective_encryption_level_;
+ }
+
private:
- bool UpdateParity(base::StringPiece payload, bool entropy);
+ bool UpdateParity(base::StringPiece payload);
// Returns the number of missing packets, or size_t max if the number
// of missing packets is not known.
size_t NumMissingPackets() const;
@@ -88,7 +89,9 @@ class NET_EXPORT_PRIVATE QuicFecGroup {
// The cumulative parity calculation of all received packets.
char payload_parity_[kMaxPacketSize];
size_t payload_parity_len_;
- bool entropy_parity_;
+ // The effective encryption level, which is the lowest encryption level of
+ // the data and FEC in the group.
+ EncryptionLevel effective_encryption_level_;
DISALLOW_COPY_AND_ASSIGN(QuicFecGroup);
};
diff --git a/chromium/net/quic/quic_fec_group_test.cc b/chromium/net/quic/quic_fec_group_test.cc
index 3de40b1bc7c..b9420dd11d5 100644
--- a/chromium/net/quic/quic_fec_group_test.cc
+++ b/chromium/net/quic/quic_fec_group_test.cc
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "net/quic/quic_fec_group.h"
+
#include <algorithm>
#include <vector>
+#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "net/quic/quic_fec_group.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
@@ -35,8 +37,6 @@ const bool kEntropyFlag[] = {
true,
};
-const bool kTestFecPacketEntropy = false;
-
} // namespace
class QuicFecGroupTest : public ::testing::Test {
@@ -44,7 +44,6 @@ class QuicFecGroupTest : public ::testing::Test {
void RunTest(size_t num_packets, size_t lost_packet, bool out_of_order) {
size_t max_len = strlen(kData[0]);
scoped_ptr<char[]> redundancy(new char[max_len]);
- bool entropy_redundancy = false;
for (size_t packet = 0; packet < num_packets; ++packet) {
for (size_t i = 0; i < max_len; i++) {
if (packet == 0) {
@@ -56,7 +55,6 @@ class QuicFecGroupTest : public ::testing::Test {
uint8 byte = i > strlen(kData[packet]) ? 0x00 : kData[packet][i];
redundancy[i] = redundancy[i] ^ byte;
}
- entropy_redundancy = (entropy_redundancy != kEntropyFlag[packet]);
}
QuicFecGroup group;
@@ -71,17 +69,19 @@ class QuicFecGroupTest : public ::testing::Test {
QuicFecData fec;
fec.fec_group = 0;
fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0]));
- ASSERT_TRUE(group.UpdateFec(num_packets, entropy_redundancy, fec));
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, num_packets,
+ fec));
} else {
QuicPacketHeader header;
header.packet_sequence_number = packet;
header.entropy_flag = kEntropyFlag[packet];
- ASSERT_TRUE(group.Update(header, kData[packet]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header,
+ kData[packet]));
}
ASSERT_TRUE(group.CanRevive() == (packet == num_packets - 1));
}
} else {
- // Update the FEC state for each non-lost packet.
+ // Update the FEC state for each non-lost packet.
for (size_t packet = 0; packet < num_packets; packet++) {
if (packet == lost_packet) {
continue;
@@ -90,7 +90,8 @@ class QuicFecGroupTest : public ::testing::Test {
QuicPacketHeader header;
header.packet_sequence_number = packet;
header.entropy_flag = kEntropyFlag[packet];
- ASSERT_TRUE(group.Update(header, kData[packet]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header,
+ kData[packet]));
ASSERT_FALSE(group.CanRevive());
}
@@ -100,7 +101,8 @@ class QuicFecGroupTest : public ::testing::Test {
fec.fec_group = 0;
fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0]));
- ASSERT_TRUE(group.UpdateFec(num_packets, entropy_redundancy, fec));
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, num_packets,
+ fec));
}
QuicPacketHeader header;
char recovered[kMaxPacketSize];
@@ -112,7 +114,8 @@ class QuicFecGroupTest : public ::testing::Test {
EXPECT_EQ(lost_packet, header.packet_sequence_number)
<< "Failed to revive packet " << lost_packet << " out of "
<< num_packets;
- EXPECT_EQ(kEntropyFlag[lost_packet], header.entropy_flag);
+ // Revived packets have an unknown entropy.
+ EXPECT_FALSE(header.entropy_flag);
ASSERT_GE(len, strlen(kData[lost_packet])) << "Incorrect length";
for (size_t i = 0; i < strlen(kData[lost_packet]); i++) {
EXPECT_EQ(kData[lost_packet][i], recovered[i]);
@@ -150,14 +153,14 @@ TEST_F(QuicFecGroupTest, UpdateFecIfReceivedPacketIsNotCovered) {
QuicPacketHeader header;
header.packet_sequence_number = 3;
- group.Update(header, data1);
+ group.Update(ENCRYPTION_FORWARD_SECURE, header, data1);
QuicFecData fec;
fec.fec_group = 1;
fec.redundancy = redundancy;
header.packet_sequence_number = 2;
- ASSERT_FALSE(group.UpdateFec(2, kTestFecPacketEntropy, fec));
+ ASSERT_FALSE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 2, fec));
}
TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) {
@@ -165,7 +168,7 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) {
header.packet_sequence_number = 3;
QuicFecGroup group;
- ASSERT_TRUE(group.Update(header, kData[0]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
EXPECT_FALSE(group.ProtectsPacketsBefore(1));
EXPECT_FALSE(group.ProtectsPacketsBefore(2));
@@ -180,13 +183,13 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithSeveralPackets) {
header.packet_sequence_number = 3;
QuicFecGroup group;
- ASSERT_TRUE(group.Update(header, kData[0]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
header.packet_sequence_number = 7;
- ASSERT_TRUE(group.Update(header, kData[0]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
header.packet_sequence_number = 5;
- ASSERT_TRUE(group.Update(header, kData[0]));
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
EXPECT_FALSE(group.ProtectsPacketsBefore(1));
EXPECT_FALSE(group.ProtectsPacketsBefore(2));
@@ -206,7 +209,7 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) {
fec.redundancy = kData[0];
QuicFecGroup group;
- ASSERT_TRUE(group.UpdateFec(3, kTestFecPacketEntropy, fec));
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 3, fec));
EXPECT_FALSE(group.ProtectsPacketsBefore(1));
EXPECT_FALSE(group.ProtectsPacketsBefore(2));
@@ -216,4 +219,24 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) {
EXPECT_TRUE(group.ProtectsPacketsBefore(50));
}
+TEST_F(QuicFecGroupTest, EffectiveEncryptionLevel) {
+ QuicFecGroup group;
+ EXPECT_EQ(NUM_ENCRYPTION_LEVELS, group.effective_encryption_level());
+
+ QuicPacketHeader header;
+ header.packet_sequence_number = 5;
+ ASSERT_TRUE(group.Update(ENCRYPTION_INITIAL, header, kData[0]));
+ EXPECT_EQ(ENCRYPTION_INITIAL, group.effective_encryption_level());
+
+ QuicFecData fec;
+ fec.fec_group = 0;
+ fec.redundancy = kData[0];
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 7, fec));
+ EXPECT_EQ(ENCRYPTION_INITIAL, group.effective_encryption_level());
+
+ header.packet_sequence_number = 3;
+ ASSERT_TRUE(group.Update(ENCRYPTION_NONE, header, kData[0]));
+ EXPECT_EQ(ENCRYPTION_NONE, group.effective_encryption_level());
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_flags.cc b/chromium/net/quic/quic_flags.cc
new file mode 100644
index 00000000000..10eaeb36847
--- /dev/null
+++ b/chromium/net/quic/quic_flags.cc
@@ -0,0 +1,48 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_flags.h"
+
+// TODO(rtenneti): Remove this.
+// Do not flip this flag until the flakiness of the
+// net/tools/quic/end_to_end_test is fixed.
+// If true, then QUIC connections will track the retransmission history of a
+// packet so that an ack of a previous transmission will ack the data of all
+// other transmissions.
+bool FLAGS_track_retransmission_history = false;
+
+// Do not remove this flag until the Finch-trials described in b/11706275
+// are complete.
+// If true, QUIC connections will support the use of a pacing algorithm when
+// sending packets, in an attempt to reduce packet loss. The client must also
+// request pacing for the server to enable it.
+bool FLAGS_enable_quic_pacing = true;
+
+// Do not remove this flag until b/11792453 is marked as Fixed.
+// If true, turns on stream flow control in QUIC.
+// If this is disabled, all in flight QUIC connections talking QUIC_VERSION_17
+// or higher will timeout. New connections will be fine.
+// If disabling this flag, also disable enable_quic_connection_flow_control_2.
+bool FLAGS_enable_quic_stream_flow_control_2 = true;
+
+// Do not remove this flag until b/11792453 is marked as Fixed.
+// If true, turns on connection level flow control in QUIC.
+// If this is disabled, all in flight QUIC connections talking QUIC_VERSION_19
+// or higher will timeout. New connections will be fine.
+bool FLAGS_enable_quic_connection_flow_control_2 = true;
+
+bool FLAGS_quic_allow_oversized_packets_for_test = false;
+
+// When true, the use time based loss detection instead of nack.
+bool FLAGS_quic_use_time_loss_detection = false;
+
+// If true, allow peer port migration of established QUIC connections.
+bool FLAGS_quic_allow_port_migration = true;
+
+// If true, it will return as soon as an error is detected while validating
+// CHLO.
+bool FLAGS_use_early_return_when_verifying_chlo = true;
+
+// If true, QUIC crypto reject message will include the reasons for rejection.
+bool FLAGS_send_quic_crypto_reject_reason = false;
diff --git a/chromium/net/quic/quic_flags.h b/chromium/net/quic/quic_flags.h
new file mode 100644
index 00000000000..c650738cfe7
--- /dev/null
+++ b/chromium/net/quic/quic_flags.h
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_FLAGS_H_
+#define NET_QUIC_QUIC_FLAGS_H_
+
+#include "net/base/net_export.h"
+
+NET_EXPORT_PRIVATE extern bool FLAGS_track_retransmission_history;
+NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_pacing;
+NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_stream_flow_control_2;
+NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_connection_flow_control_2;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_oversized_packets_for_test;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_time_loss_detection;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_port_migration;
+NET_EXPORT_PRIVATE extern bool FLAGS_use_early_return_when_verifying_chlo;
+NET_EXPORT_PRIVATE extern bool FLAGS_send_quic_crypto_reject_reason;
+
+#endif // NET_QUIC_QUIC_FLAGS_H_
diff --git a/chromium/net/quic/quic_flow_controller.cc b/chromium/net/quic/quic_flow_controller.cc
new file mode 100644
index 00000000000..107b4d9510a
--- /dev/null
+++ b/chromium/net/quic/quic_flow_controller.cc
@@ -0,0 +1,203 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_flow_controller.h"
+
+#include "base/basictypes.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
+
+QuicFlowController::QuicFlowController(QuicConnection* connection,
+ QuicStreamId id,
+ bool is_server,
+ uint64 send_window_offset,
+ uint64 receive_window_offset,
+ uint64 max_receive_window)
+ : connection_(connection),
+ id_(id),
+ is_enabled_(true),
+ is_server_(is_server),
+ bytes_consumed_(0),
+ highest_received_byte_offset_(0),
+ bytes_sent_(0),
+ send_window_offset_(send_window_offset),
+ receive_window_offset_(receive_window_offset),
+ max_receive_window_(max_receive_window),
+ last_blocked_send_window_offset_(0) {
+ DVLOG(1) << ENDPOINT << "Created flow controller for stream " << id_
+ << ", setting initial receive window offset to: "
+ << receive_window_offset_
+ << ", max receive window to: "
+ << max_receive_window_
+ << ", setting send window offset to: " << send_window_offset_;
+ if (connection_->version() < QUIC_VERSION_17) {
+ DVLOG(1) << ENDPOINT << "Disabling QuicFlowController for stream " << id_
+ << ", QUIC version " << connection_->version();
+ Disable();
+ }
+}
+
+void QuicFlowController::AddBytesConsumed(uint64 bytes_consumed) {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ bytes_consumed_ += bytes_consumed;
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed: " << bytes_consumed_;
+
+ MaybeSendWindowUpdate();
+}
+
+bool QuicFlowController::UpdateHighestReceivedOffset(uint64 new_offset) {
+ if (!IsEnabled()) {
+ return false;
+ }
+
+ // Only update if offset has increased.
+ if (new_offset <= highest_received_byte_offset_) {
+ return false;
+ }
+
+ DVLOG(1) << ENDPOINT << "Stream " << id_
+ << " highest byte offset increased from: "
+ << highest_received_byte_offset_ << " to " << new_offset;
+ highest_received_byte_offset_ = new_offset;
+ return true;
+}
+
+void QuicFlowController::AddBytesSent(uint64 bytes_sent) {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ if (bytes_sent_ + bytes_sent > send_window_offset_) {
+ LOG(DFATAL) << ENDPOINT << "Stream " << id_ << " Trying to send an extra "
+ << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_
+ << ", and send_window_offset_ = " << send_window_offset_;
+ bytes_sent_ = send_window_offset_;
+
+ // This is an error on our side, close the connection as soon as possible.
+ connection_->SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA);
+ return;
+ }
+
+ bytes_sent_ += bytes_sent;
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent: " << bytes_sent_;
+}
+
+bool QuicFlowController::FlowControlViolation() {
+ if (!IsEnabled()) {
+ return false;
+ }
+
+ if (highest_received_byte_offset_ > receive_window_offset_) {
+ LOG(ERROR) << ENDPOINT << "Flow control violation on stream "
+ << id_ << ", receive window offset: "
+ << receive_window_offset_
+ << ", highest received byte offset: "
+ << highest_received_byte_offset_;
+ return true;
+ }
+ return false;
+}
+
+void QuicFlowController::MaybeSendWindowUpdate() {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ // Send WindowUpdate to increase receive window if
+ // (receive window offset - consumed bytes) < (max window / 2).
+ // This is behaviour copied from SPDY.
+ DCHECK_LT(bytes_consumed_, receive_window_offset_);
+ size_t consumed_window = receive_window_offset_ - bytes_consumed_;
+ size_t threshold = (max_receive_window_ / 2);
+
+ if (consumed_window < threshold) {
+ // Update our receive window.
+ receive_window_offset_ += (max_receive_window_ - consumed_window);
+
+ DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_
+ << ", consumed bytes: " << bytes_consumed_
+ << ", consumed window: " << consumed_window
+ << ", and threshold: " << threshold
+ << ", and max recvw: " << max_receive_window_
+ << ". New receive window offset is: " << receive_window_offset_;
+
+ // Inform the peer of our new receive window.
+ connection_->SendWindowUpdate(id_, receive_window_offset_);
+ }
+}
+
+void QuicFlowController::MaybeSendBlocked() {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ if (SendWindowSize() == 0 &&
+ last_blocked_send_window_offset_ < send_window_offset_) {
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " is flow control blocked. "
+ << "Send window: " << SendWindowSize()
+ << ", bytes sent: " << bytes_sent_
+ << ", send limit: " << send_window_offset_;
+ // The entire send_window has been consumed, we are now flow control
+ // blocked.
+ connection_->SendBlocked(id_);
+
+ // Keep track of when we last sent a BLOCKED frame so that we only send one
+ // at a given send offset.
+ last_blocked_send_window_offset_ = send_window_offset_;
+ }
+}
+
+bool QuicFlowController::UpdateSendWindowOffset(uint64 new_send_window_offset) {
+ if (!IsEnabled()) {
+ return false;
+ }
+
+ // Only update if send window has increased.
+ if (new_send_window_offset <= send_window_offset_) {
+ return false;
+ }
+
+ DVLOG(1) << ENDPOINT << "UpdateSendWindowOffset for stream " << id_
+ << " with new offset " << new_send_window_offset
+ << " , current offset: " << send_window_offset_;
+
+ send_window_offset_ = new_send_window_offset;
+ return true;
+}
+
+void QuicFlowController::Disable() {
+ is_enabled_ = false;
+}
+
+bool QuicFlowController::IsEnabled() const {
+ bool connection_flow_control_enabled =
+ (id_ == kConnectionLevelId &&
+ FLAGS_enable_quic_connection_flow_control_2);
+ bool stream_flow_control_enabled =
+ (id_ != kConnectionLevelId &&
+ FLAGS_enable_quic_stream_flow_control_2);
+ return (connection_flow_control_enabled || stream_flow_control_enabled) &&
+ is_enabled_;
+}
+
+bool QuicFlowController::IsBlocked() const {
+ return IsEnabled() && SendWindowSize() == 0;
+}
+
+uint64 QuicFlowController::SendWindowSize() const {
+ if (bytes_sent_ > send_window_offset_) {
+ return 0;
+ }
+ return send_window_offset_ - bytes_sent_;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_flow_controller.h b/chromium/net/quic/quic_flow_controller.h
new file mode 100644
index 00000000000..e5f5494b7f1
--- /dev/null
+++ b/chromium/net/quic/quic_flow_controller.h
@@ -0,0 +1,130 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_FLOW_CONTROLLER_H_
+#define NET_QUIC_QUIC_FLOW_CONTROLLER_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+namespace test {
+class QuicFlowControllerPeer;
+} // namespace test
+
+class QuicConnection;
+
+const QuicStreamId kConnectionLevelId = 0;
+
+// QuicFlowController allows a QUIC stream or connection to perform flow
+// control. The stream/connection owns a QuicFlowController which keeps track of
+// bytes sent/received, can tell the owner if it is flow control blocked, and
+// can send WINDOW_UPDATE or BLOCKED frames when needed.
+class NET_EXPORT_PRIVATE QuicFlowController {
+ public:
+ QuicFlowController(QuicConnection* connection,
+ QuicStreamId id,
+ bool is_server,
+ uint64 send_window_offset,
+ uint64 receive_window_offset,
+ uint64 max_receive_window);
+ ~QuicFlowController() {}
+
+ // Called when we see a new highest received byte offset from the peer, either
+ // via a data frame or a RST.
+ // Returns true if this call changes highest_received_byte_offset_, and false
+ // in the case where |new_offset| is <= highest_received_byte_offset_.
+ bool UpdateHighestReceivedOffset(uint64 new_offset);
+
+ // Called when bytes received from the peer are consumed locally. This may
+ // trigger the sending of a WINDOW_UPDATE frame using |connection|.
+ void AddBytesConsumed(uint64 bytes_consumed);
+
+ // Called when bytes are sent to the peer.
+ void AddBytesSent(uint64 bytes_sent);
+
+ // Set a new send window offset.
+ // Returns true if this changes send_window_offset_, and false in the case
+ // where |new_send_window| is <= send_window_offset_.
+ bool UpdateSendWindowOffset(uint64 new_send_window_offset);
+
+ // Returns the current available send window.
+ uint64 SendWindowSize() const;
+
+ // Send a BLOCKED frame if appropriate.
+ void MaybeSendBlocked();
+
+ // Disable flow control.
+ void Disable();
+
+ // Returns true if flow control is enabled.
+ bool IsEnabled() const;
+
+ // Returns true if flow control send limits have been reached.
+ bool IsBlocked() const;
+
+ // Returns true if flow control receive limits have been violated by the peer.
+ bool FlowControlViolation();
+
+ uint64 bytes_consumed() const { return bytes_consumed_; }
+
+ uint64 highest_received_byte_offset() const {
+ return highest_received_byte_offset_;
+ }
+
+ private:
+ friend class test::QuicFlowControllerPeer;
+
+ // Send a WINDOW_UPDATE frame if appropriate.
+ void MaybeSendWindowUpdate();
+
+ // The parent connection, used to send connection close on flow control
+ // violation, and WINDOW_UPDATE and BLOCKED frames when appropriate.
+ // Not owned.
+ QuicConnection* connection_;
+
+ // ID of stream this flow controller belongs to. This can be 0 if this is a
+ // connection level flow controller.
+ QuicStreamId id_;
+
+ // True if flow control is enabled.
+ bool is_enabled_;
+
+ // True if this is owned by a server.
+ bool is_server_;
+
+ // Track number of bytes received from the peer, which have been consumed
+ // locally.
+ uint64 bytes_consumed_;
+
+ // The highest byte offset we have seen from the peer. This could be the
+ // highest offset in a data frame, or a final value in a RST.
+ uint64 highest_received_byte_offset_;
+
+ // Tracks number of bytes sent to the peer.
+ uint64 bytes_sent_;
+
+ // The absolute offset in the outgoing byte stream. If this offset is reached
+ // then we become flow control blocked until we receive a WINDOW_UPDATE.
+ uint64 send_window_offset_;
+
+ // The absolute offset in the incoming byte stream. The peer should never send
+ // us bytes which are beyond this offset.
+ uint64 receive_window_offset_;
+
+ // Largest size the receive window can grow to.
+ uint64 max_receive_window_;
+
+ // Keep track of the last time we sent a BLOCKED frame. We should only send
+ // another when the number of bytes we have sent has changed.
+ uint64 last_blocked_send_window_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicFlowController);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_FLOW_CONTROLLER_H_
diff --git a/chromium/net/quic/quic_flow_controller_test.cc b/chromium/net/quic/quic_flow_controller_test.cc
new file mode 100644
index 00000000000..26781acb65f
--- /dev/null
+++ b/chromium/net/quic/quic_flow_controller_test.cc
@@ -0,0 +1,224 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_flow_controller.h"
+
+#include "base/strings/stringprintf.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using base::StringPrintf;
+
+namespace net {
+namespace test {
+
+using ::testing::_;
+
+class QuicFlowControllerTest : public ::testing::Test {
+ public:
+ QuicFlowControllerTest()
+ : stream_id_(1234),
+ send_window_(kInitialSessionFlowControlWindowForTest),
+ receive_window_(kInitialSessionFlowControlWindowForTest),
+ max_receive_window_(kInitialSessionFlowControlWindowForTest),
+ connection_(false),
+ old_flag_(&FLAGS_enable_quic_stream_flow_control_2, true) {
+ }
+
+ void Initialize() {
+ flow_controller_.reset(new QuicFlowController(
+ &connection_, stream_id_, false, send_window_,
+ receive_window_, max_receive_window_));
+ }
+
+ protected:
+ QuicStreamId stream_id_;
+ uint64 send_window_;
+ uint64 receive_window_;
+ uint64 max_receive_window_;
+ scoped_ptr<QuicFlowController> flow_controller_;
+ MockConnection connection_;
+ ValueRestore<bool> old_flag_;
+};
+
+TEST_F(QuicFlowControllerTest, SendingBytes) {
+ Initialize();
+
+ EXPECT_TRUE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Send some bytes, but not enough to block.
+ flow_controller_->AddBytesSent(send_window_ / 2);
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_ / 2, flow_controller_->SendWindowSize());
+
+ // Send enough bytes to block.
+ flow_controller_->AddBytesSent(send_window_ / 2);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // BLOCKED frame should get sent.
+ EXPECT_CALL(connection_, SendBlocked(stream_id_)).Times(1);
+ flow_controller_->MaybeSendBlocked();
+
+ // Update the send window, and verify this has unblocked.
+ EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_));
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Updating with a smaller offset doesn't change anything.
+ EXPECT_FALSE(flow_controller_->UpdateSendWindowOffset(send_window_ / 10));
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Try to send more bytes, violating flow control.
+ EXPECT_CALL(connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA));
+ EXPECT_DFATAL(
+ flow_controller_->AddBytesSent(send_window_ * 10),
+ StringPrintf("Trying to send an extra %d bytes",
+ static_cast<int>(send_window_ * 10)));
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+}
+
+TEST_F(QuicFlowControllerTest, ReceivingBytes) {
+ Initialize();
+
+ EXPECT_TRUE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Receive some bytes, updating highest received offset, but not enough to
+ // fill flow control receive window.
+ EXPECT_TRUE(
+ flow_controller_->UpdateHighestReceivedOffset(1 + receive_window_ / 2));
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ((receive_window_ / 2) - 1,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Consume enough bytes to send a WINDOW_UPDATE frame.
+ EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, _)).Times(1);
+
+ flow_controller_->AddBytesConsumed(1 + receive_window_ / 2);
+
+ // Result is that once again we have a fully open receive window.
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+}
+
+TEST_F(QuicFlowControllerTest,
+ DisabledWhenQuicVersionDoesNotSupportFlowControl) {
+ // Only support version 16: no flow control.
+ QuicConnectionPeer::SetSupportedVersions(&connection_,
+ SupportedVersions(QUIC_VERSION_16));
+
+ Initialize();
+
+ MockConnection connection(false);
+
+ // Should not be enabled, and should not report as blocked.
+ EXPECT_FALSE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+
+ // Any attempts to add/remove bytes should have no effect.
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ EXPECT_EQ(send_window_,
+ QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get()));
+ EXPECT_EQ(receive_window_, QuicFlowControllerPeer::ReceiveWindowOffset(
+ flow_controller_.get()));
+ flow_controller_->AddBytesSent(123);
+ flow_controller_->AddBytesConsumed(456);
+ flow_controller_->UpdateHighestReceivedOffset(789);
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ EXPECT_EQ(send_window_,
+ QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get()));
+ EXPECT_EQ(receive_window_, QuicFlowControllerPeer::ReceiveWindowOffset(
+ flow_controller_.get()));
+
+ // Any attempt to change offset should have no effect.
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ EXPECT_EQ(send_window_,
+ QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get()));
+ flow_controller_->UpdateSendWindowOffset(send_window_ + 12345);
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ EXPECT_EQ(send_window_,
+ QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get()));
+
+ // The connection should never send WINDOW_UPDATE or BLOCKED frames, even if
+ // the internal state implies that it should.
+
+ // If the flow controller was enabled, then a send window size of 0 would
+ // trigger a BLOCKED frame to be sent.
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ EXPECT_CALL(connection_, SendBlocked(_)).Times(0);
+ flow_controller_->MaybeSendBlocked();
+
+ // If the flow controller was enabled, then a WINDOW_UPDATE would be sent if
+ // (receive window) < (max receive window / 2)
+ QuicFlowControllerPeer::SetReceiveWindowOffset(flow_controller_.get(),
+ max_receive_window_ / 10);
+ EXPECT_TRUE(QuicFlowControllerPeer::ReceiveWindowSize(
+ flow_controller_.get()) < (max_receive_window_ / 2));
+ EXPECT_CALL(connection_, SendWindowUpdate(_, _)).Times(0);
+ flow_controller_->AddBytesConsumed(0);
+
+ // Should not be enabled, and should not report as blocked.
+ EXPECT_FALSE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+}
+
+TEST_F(QuicFlowControllerTest, OnlySendBlockedFrameOncePerOffset) {
+ Initialize();
+
+ // Test that we don't send duplicate BLOCKED frames. We should only send one
+ // BLOCKED frame at a given send window offset.
+ EXPECT_TRUE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Send enough bytes to block.
+ flow_controller_->AddBytesSent(send_window_);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // Expect that 2 BLOCKED frames should get sent in total.
+ EXPECT_CALL(connection_, SendBlocked(stream_id_)).Times(2);
+
+ // BLOCKED frame should get sent.
+ flow_controller_->MaybeSendBlocked();
+
+ // BLOCKED frame should not get sent again until our send offset changes.
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+
+ // Update the send window, then send enough bytes to block again.
+ EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_));
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ flow_controller_->AddBytesSent(send_window_);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // BLOCKED frame should get sent as send offset has changed.
+ flow_controller_->MaybeSendBlocked();
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_framer.cc b/chromium/net/quic/quic_framer.cc
index cc02882ddc9..fd077bc21da 100644
--- a/chromium/net/quic/quic_framer.cc
+++ b/chromium/net/quic/quic_framer.cc
@@ -5,10 +5,16 @@
#include "net/quic/quic_framer.h"
#include "base/containers/hash_tables.h"
+#include "base/stl_util.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_data_reader.h"
#include "net/quic/quic_data_writer.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_socket_address_coder.h"
using base::StringPiece;
using std::make_pair;
@@ -32,8 +38,8 @@ const QuicPacketSequenceNumber k2ByteSequenceNumberMask =
const QuicPacketSequenceNumber k1ByteSequenceNumberMask =
GG_UINT64_C(0x00000000000000FF);
-const QuicGuid k1ByteGuidMask = GG_UINT64_C(0x00000000000000FF);
-const QuicGuid k4ByteGuidMask = GG_UINT64_C(0x00000000FFFFFFFF);
+const QuicConnectionId k1ByteConnectionIdMask = GG_UINT64_C(0x00000000000000FF);
+const QuicConnectionId k4ByteConnectionIdMask = GG_UINT64_C(0x00000000FFFFFFFF);
// Number of bits the sequence number length bits are shifted from the right
// edge of the public header.
@@ -49,6 +55,8 @@ const uint8 kPublicHeaderSequenceNumberShift = 4;
// ResetStream : 0b 00000001 (0x01)
// ConnectionClose : 0b 00000010 (0x02)
// GoAway : 0b 00000011 (0x03)
+// WindowUpdate : 0b 00000100 (0x04)
+// Blocked : 0b 00000101 (0x05)
//
// Special Frame Types encode both a Frame Type and corresponding flags
// all in the Frame Type byte. Currently defined Special Frame Types are:
@@ -108,8 +116,45 @@ QuicPacketSequenceNumber ClosestTo(QuicPacketSequenceNumber target,
return (Delta(target, a) < Delta(target, b)) ? a : b;
}
+QuicSequenceNumberLength ReadSequenceNumberLength(uint8 flags) {
+ switch (flags & PACKET_FLAGS_6BYTE_SEQUENCE) {
+ case PACKET_FLAGS_6BYTE_SEQUENCE:
+ return PACKET_6BYTE_SEQUENCE_NUMBER;
+ case PACKET_FLAGS_4BYTE_SEQUENCE:
+ return PACKET_4BYTE_SEQUENCE_NUMBER;
+ case PACKET_FLAGS_2BYTE_SEQUENCE:
+ return PACKET_2BYTE_SEQUENCE_NUMBER;
+ case PACKET_FLAGS_1BYTE_SEQUENCE:
+ return PACKET_1BYTE_SEQUENCE_NUMBER;
+ default:
+ LOG(DFATAL) << "Unreachable case statement.";
+ return PACKET_6BYTE_SEQUENCE_NUMBER;
+ }
+}
+
+bool CanTruncate(
+ QuicVersion version, const QuicFrame& frame, size_t free_bytes) {
+ if ((frame.type == ACK_FRAME || frame.type == CONNECTION_CLOSE_FRAME) &&
+ free_bytes >=
+ QuicFramer::GetMinAckFrameSize(version,
+ PACKET_6BYTE_SEQUENCE_NUMBER,
+ PACKET_6BYTE_SEQUENCE_NUMBER)) {
+ return true;
+ }
+ return false;
+}
+
} // namespace
+bool QuicFramerVisitorInterface::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ return true;
+}
+
+bool QuicFramerVisitorInterface::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ return true;
+}
+
QuicFramer::QuicFramer(const QuicVersionVector& supported_versions,
QuicTime creation_time,
bool is_server)
@@ -118,10 +163,13 @@ QuicFramer::QuicFramer(const QuicVersionVector& supported_versions,
entropy_calculator_(NULL),
error_(QUIC_NO_ERROR),
last_sequence_number_(0),
- last_serialized_guid_(0),
+ last_serialized_connection_id_(0),
supported_versions_(supported_versions),
+ decrypter_level_(ENCRYPTION_NONE),
+ alternative_decrypter_level_(ENCRYPTION_NONE),
alternative_decrypter_latch_(false),
is_server_(is_server),
+ validate_flags_(true),
creation_time_(creation_time) {
DCHECK(!supported_versions.empty());
quic_version_ = supported_versions_[0];
@@ -136,10 +184,13 @@ QuicFramer::~QuicFramer() {}
size_t QuicFramer::GetMinStreamFrameSize(QuicVersion version,
QuicStreamId stream_id,
QuicStreamOffset offset,
- bool last_frame_in_packet) {
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group) {
+ bool no_stream_frame_length = last_frame_in_packet &&
+ is_in_fec_group == NOT_IN_FEC_GROUP;
return kQuicFrameTypeSize + GetStreamIdSize(stream_id) +
GetStreamOffsetSize(offset) +
- (last_frame_in_packet ? 0 : kQuicStreamPayloadLengthSize);
+ (no_stream_frame_length ? 0 : kQuicStreamPayloadLengthSize);
}
// static
@@ -147,14 +198,25 @@ size_t QuicFramer::GetMinAckFrameSize(
QuicVersion version,
QuicSequenceNumberLength sequence_number_length,
QuicSequenceNumberLength largest_observed_length) {
- return kQuicFrameTypeSize + kQuicEntropyHashSize +
- sequence_number_length + kQuicEntropyHashSize +
+ size_t len = kQuicFrameTypeSize + kQuicEntropyHashSize +
largest_observed_length + kQuicDeltaTimeLargestObservedSize;
+ if (version <= QUIC_VERSION_15) {
+ len += sequence_number_length + kQuicEntropyHashSize;
+ }
+ return len;
}
// static
-size_t QuicFramer::GetMinRstStreamFrameSize() {
- return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicErrorCodeSize +
+size_t QuicFramer::GetStopWaitingFrameSize(
+ QuicSequenceNumberLength sequence_number_length) {
+ return kQuicFrameTypeSize + kQuicEntropyHashSize +
+ sequence_number_length;
+}
+
+// static
+size_t QuicFramer::GetMinRstStreamFrameSize(QuicVersion quic_version) {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize +
+ kQuicMaxStreamOffsetSize + kQuicErrorCodeSize +
kQuicErrorDetailsLengthSize;
}
@@ -170,6 +232,16 @@ size_t QuicFramer::GetMinGoAwayFrameSize() {
}
// static
+size_t QuicFramer::GetWindowUpdateFrameSize() {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
+}
+
+// static
+size_t QuicFramer::GetBlockedFrameSize() {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize;
+}
+
+// static
size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) {
// Sizes are 1 through 4 bytes.
for (int i = 1; i <= 4; ++i) {
@@ -202,22 +274,10 @@ size_t QuicFramer::GetStreamOffsetSize(QuicStreamOffset offset) {
// static
size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) {
- return kPublicFlagsSize + PACKET_8BYTE_GUID +
+ return kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID +
number_versions * kQuicVersionSize;
}
-// static
-bool QuicFramer::CanTruncate(
- QuicVersion version, const QuicFrame& frame, size_t free_bytes) {
- if ((frame.type == ACK_FRAME || frame.type == CONNECTION_CLOSE_FRAME) &&
- free_bytes >= GetMinAckFrameSize(version,
- PACKET_6BYTE_SEQUENCE_NUMBER,
- PACKET_6BYTE_SEQUENCE_NUMBER)) {
- return true;
- }
- return false;
-}
-
bool QuicFramer::IsSupportedVersion(const QuicVersion version) const {
for (size_t i = 0; i < supported_versions_.size(); ++i) {
if (version == supported_versions_[i]) {
@@ -232,57 +292,46 @@ size_t QuicFramer::GetSerializedFrameLength(
size_t free_bytes,
bool first_frame,
bool last_frame,
+ InFecGroup is_in_fec_group,
QuicSequenceNumberLength sequence_number_length) {
if (frame.type == PADDING_FRAME) {
// PADDING implies end of packet.
return free_bytes;
}
size_t frame_len =
- ComputeFrameLength(frame, last_frame, sequence_number_length);
- if (frame_len > free_bytes) {
- // Only truncate the first frame in a packet, so if subsequent ones go
- // over, stop including more frames.
- if (!first_frame) {
- return 0;
- }
- if (CanTruncate(quic_version_, frame, free_bytes)) {
- // Truncate the frame so the packet will not exceed kMaxPacketSize.
- // Note that we may not use every byte of the writer in this case.
- DVLOG(1) << "Truncating large frame";
- return free_bytes;
- }
+ ComputeFrameLength(frame, last_frame, is_in_fec_group,
+ sequence_number_length);
+ if (frame_len <= free_bytes) {
+ // Frame fits within packet. Note that acks may be truncated.
+ return frame_len;
+ }
+ // Only truncate the first frame in a packet, so if subsequent ones go
+ // over, stop including more frames.
+ if (!first_frame) {
+ return 0;
+ }
+ if (CanTruncate(quic_version_, frame, free_bytes)) {
+ // Truncate the frame so the packet will not exceed kMaxPacketSize.
+ // Note that we may not use every byte of the writer in this case.
+ DVLOG(1) << "Truncating large frame, free bytes: " << free_bytes;
+ return free_bytes;
}
+ if (!FLAGS_quic_allow_oversized_packets_for_test) {
+ return 0;
+ }
+ LOG(DFATAL) << "Packet size too small to fit frame.";
return frame_len;
}
-QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) { }
+QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) {}
-QuicFramer::AckFrameInfo::~AckFrameInfo() { }
+QuicFramer::AckFrameInfo::~AckFrameInfo() {}
QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash(
const QuicPacketHeader& header) const {
return header.entropy_flag << (header.packet_sequence_number % 8);
}
-// Test only.
-SerializedPacket QuicFramer::BuildUnsizedDataPacket(
- const QuicPacketHeader& header,
- const QuicFrames& frames) {
- const size_t max_plaintext_size = GetMaxPlaintextSize(kMaxPacketSize);
- size_t packet_size = GetPacketHeaderSize(header);
- for (size_t i = 0; i < frames.size(); ++i) {
- DCHECK_LE(packet_size, max_plaintext_size);
- bool first_frame = i == 0;
- bool last_frame = i == frames.size() - 1;
- const size_t frame_size = GetSerializedFrameLength(
- frames[i], max_plaintext_size - packet_size, first_frame, last_frame,
- header.public_header.sequence_number_length);
- DCHECK(frame_size);
- packet_size += frame_size;
- }
- return BuildDataPacket(header, frames, packet_size);
-}
-
SerializedPacket QuicFramer::BuildDataPacket(
const QuicPacketHeader& header,
const QuicFrames& frames,
@@ -291,14 +340,19 @@ SerializedPacket QuicFramer::BuildDataPacket(
const SerializedPacket kNoPacket(
0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL);
if (!AppendPacketHeader(header, &writer)) {
+ LOG(DFATAL) << "AppendPacketHeader failed";
return kNoPacket;
}
for (size_t i = 0; i < frames.size(); ++i) {
const QuicFrame& frame = frames[i];
- const bool last_frame_in_packet = i == (frames.size() - 1);
- if (!AppendTypeByte(frame, last_frame_in_packet, &writer)) {
+ // Determine if we should write stream frame length in header.
+ const bool no_stream_frame_length =
+ (header.is_in_fec_group == NOT_IN_FEC_GROUP) &&
+ (i == frames.size() - 1);
+ if (!AppendTypeByte(frame, no_stream_frame_length, &writer)) {
+ LOG(DFATAL) << "AppendTypeByte failed";
return kNoPacket;
}
@@ -307,41 +361,80 @@ SerializedPacket QuicFramer::BuildDataPacket(
writer.WritePadding();
break;
case STREAM_FRAME:
- if (!AppendStreamFramePayload(
- *frame.stream_frame, last_frame_in_packet, &writer)) {
+ if (!AppendStreamFrame(
+ *frame.stream_frame, no_stream_frame_length, &writer)) {
+ LOG(DFATAL) << "AppendStreamFrame failed";
return kNoPacket;
}
break;
case ACK_FRAME:
- if (!AppendAckFramePayloadAndTypeByte(
+ if (!AppendAckFrameAndTypeByte(
header, *frame.ack_frame, &writer)) {
+ LOG(DFATAL) << "AppendAckFrameAndTypeByte failed";
return kNoPacket;
}
break;
case CONGESTION_FEEDBACK_FRAME:
- if (!AppendQuicCongestionFeedbackFramePayload(
+ if (!AppendCongestionFeedbackFrame(
*frame.congestion_feedback_frame, &writer)) {
+ LOG(DFATAL) << "AppendCongestionFeedbackFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case STOP_WAITING_FRAME:
+ if (quic_version_ <= QUIC_VERSION_15) {
+ LOG(DFATAL) << "Attempt to add a StopWaitingFrame in "
+ << QuicVersionToString(quic_version_);
+ return kNoPacket;
+ }
+ if (!AppendStopWaitingFrame(
+ header, *frame.stop_waiting_frame, &writer)) {
+ LOG(DFATAL) << "AppendStopWaitingFrame failed";
return kNoPacket;
}
break;
+ case PING_FRAME:
+ if (quic_version_ <= QUIC_VERSION_17) {
+ LOG(DFATAL) << "Attempt to add a PingFrame in "
+ << QuicVersionToString(quic_version_);
+ return kNoPacket;
+ }
+ // Ping has no payload.
+ break;
case RST_STREAM_FRAME:
- if (!AppendRstStreamFramePayload(*frame.rst_stream_frame, &writer)) {
+ if (!AppendRstStreamFrame(*frame.rst_stream_frame, &writer)) {
+ LOG(DFATAL) << "AppendRstStreamFrame failed";
return kNoPacket;
}
break;
case CONNECTION_CLOSE_FRAME:
- if (!AppendConnectionCloseFramePayload(
+ if (!AppendConnectionCloseFrame(
*frame.connection_close_frame, &writer)) {
+ LOG(DFATAL) << "AppendConnectionCloseFrame failed";
return kNoPacket;
}
break;
case GOAWAY_FRAME:
- if (!AppendGoAwayFramePayload(*frame.goaway_frame, &writer)) {
+ if (!AppendGoAwayFrame(*frame.goaway_frame, &writer)) {
+ LOG(DFATAL) << "AppendGoAwayFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case WINDOW_UPDATE_FRAME:
+ if (!AppendWindowUpdateFrame(*frame.window_update_frame, &writer)) {
+ LOG(DFATAL) << "AppendWindowUpdateFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case BLOCKED_FRAME:
+ if (!AppendBlockedFrame(*frame.blocked_frame, &writer)) {
+ LOG(DFATAL) << "AppendBlockedFrame failed";
return kNoPacket;
}
break;
default:
RaiseError(QUIC_INVALID_FRAME_DATA);
+ LOG(DFATAL) << "QUIC_INVALID_FRAME_DATA";
return kNoPacket;
}
}
@@ -352,7 +445,7 @@ SerializedPacket QuicFramer::BuildDataPacket(
// length, even though they're typically slightly shorter.
DCHECK_LE(len, packet_size);
QuicPacket* packet = QuicPacket::NewDataPacket(
- writer.take(), len, true, header.public_header.guid_length,
+ writer.take(), len, true, header.public_header.connection_id_length,
header.public_header.version_flag,
header.public_header.sequence_number_length);
@@ -377,10 +470,12 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header,
const SerializedPacket kNoPacket(
0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL);
if (!AppendPacketHeader(header, &writer)) {
+ LOG(DFATAL) << "AppendPacketHeader failed";
return kNoPacket;
}
if (!writer.WriteBytes(fec.redundancy.data(), fec.redundancy.length())) {
+ LOG(DFATAL) << "Failed to add FEC";
return kNoPacket;
}
@@ -388,7 +483,7 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header,
header.packet_sequence_number,
header.public_header.sequence_number_length,
QuicPacket::NewFecPacket(writer.take(), len, true,
- header.public_header.guid_length,
+ header.public_header.connection_id_length,
header.public_header.version_flag,
header.public_header.sequence_number_length),
GetPacketEntropyHash(header), NULL);
@@ -398,27 +493,37 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header,
QuicEncryptedPacket* QuicFramer::BuildPublicResetPacket(
const QuicPublicResetPacket& packet) {
DCHECK(packet.public_header.reset_flag);
- size_t len = GetPublicResetPacketSize();
+
+ CryptoHandshakeMessage reset;
+ reset.set_tag(kPRST);
+ reset.SetValue(kRNON, packet.nonce_proof);
+ reset.SetValue(kRSEQ, packet.rejected_sequence_number);
+ if (!packet.client_address.address().empty()) {
+ // packet.client_address is non-empty.
+ QuicSocketAddressCoder address_coder(packet.client_address);
+ string serialized_address = address_coder.Encode();
+ if (serialized_address.empty()) {
+ return NULL;
+ }
+ reset.SetStringPiece(kCADR, serialized_address);
+ }
+ const QuicData& reset_serialized = reset.GetSerialized();
+
+ size_t len =
+ kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID + reset_serialized.length();
QuicDataWriter writer(len);
uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_RST |
- PACKET_PUBLIC_FLAGS_8BYTE_GUID |
- PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE);
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID);
if (!writer.WriteUInt8(flags)) {
return NULL;
}
- if (!writer.WriteUInt64(packet.public_header.guid)) {
- return NULL;
- }
-
- if (!writer.WriteUInt64(packet.nonce_proof)) {
+ if (!writer.WriteUInt64(packet.public_header.connection_id)) {
return NULL;
}
- if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER,
- packet.rejected_sequence_number,
- &writer)) {
+ if (!writer.WriteBytes(reset_serialized.data(), reset_serialized.length())) {
return NULL;
}
@@ -433,13 +538,12 @@ QuicEncryptedPacket* QuicFramer::BuildVersionNegotiationPacket(
QuicDataWriter writer(len);
uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_VERSION |
- PACKET_PUBLIC_FLAGS_8BYTE_GUID |
- PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE);
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID);
if (!writer.WriteUInt8(flags)) {
return NULL;
}
- if (!writer.WriteUInt64(header.guid)) {
+ if (!writer.WriteUInt64(header.connection_id)) {
return NULL;
}
@@ -466,6 +570,12 @@ bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
return RaiseError(QUIC_INVALID_PACKET_HEADER);
}
+ if (!visitor_->OnUnauthenticatedPublicHeader(public_header)) {
+ // The visitor suppresses further processing of the packet.
+ reader_.reset(NULL);
+ return true;
+ }
+
if (is_server_ && public_header.version_flag &&
public_header.versions[0] != quic_version_) {
if (!visitor_->OnProtocolVersionMismatch(public_header.versions[0])) {
@@ -548,16 +658,39 @@ bool QuicFramer::ProcessDataPacket(
bool QuicFramer::ProcessPublicResetPacket(
const QuicPacketPublicHeader& public_header) {
QuicPublicResetPacket packet(public_header);
- if (!reader_->ReadUInt64(&packet.nonce_proof)) {
+
+ scoped_ptr<CryptoHandshakeMessage> reset(
+ CryptoFramer::ParseMessage(reader_->ReadRemainingPayload()));
+ if (!reset.get()) {
+ set_detailed_error("Unable to read reset message.");
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+ if (reset->tag() != kPRST) {
+ set_detailed_error("Incorrect message tag.");
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+
+ if (reset->GetUint64(kRNON, &packet.nonce_proof) != QUIC_NO_ERROR) {
set_detailed_error("Unable to read nonce proof.");
return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
}
// TODO(satyamshekhar): validate nonce to protect against DoS.
- if (!reader_->ReadUInt48(&packet.rejected_sequence_number)) {
+ if (reset->GetUint64(kRSEQ, &packet.rejected_sequence_number) !=
+ QUIC_NO_ERROR) {
set_detailed_error("Unable to read rejected sequence number.");
return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
}
+
+ StringPiece address;
+ if (reset->GetStringPiece(kCADR, &address)) {
+ QuicSocketAddressCoder address_coder;
+ if (address_coder.Decode(address.data(), address.length())) {
+ packet.client_address = IPEndPoint(address_coder.ip(),
+ address_coder.port());
+ }
+ }
+
visitor_->OnPublicResetPacket(packet);
return true;
}
@@ -593,6 +726,7 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header,
bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header,
QuicDataWriter* writer) {
+ DVLOG(1) << "Appending header: " << header;
DCHECK(header.fec_group > 0 || header.is_in_fec_group == NOT_IN_FEC_GROUP);
uint8 public_flags = 0;
if (header.public_header.reset_flag) {
@@ -606,38 +740,44 @@ bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header,
GetSequenceNumberFlags(header.public_header.sequence_number_length)
<< kPublicHeaderSequenceNumberShift;
- switch (header.public_header.guid_length) {
- case PACKET_0BYTE_GUID:
- if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_0BYTE_GUID)) {
+ switch (header.public_header.connection_id_length) {
+ case PACKET_0BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID)) {
return false;
}
break;
- case PACKET_1BYTE_GUID:
- if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_1BYTE_GUID)) {
+ case PACKET_1BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID)) {
return false;
}
- if (!writer->WriteUInt8(header.public_header.guid & k1ByteGuidMask)) {
+ if (!writer->WriteUInt8(
+ header.public_header.connection_id & k1ByteConnectionIdMask)) {
return false;
}
break;
- case PACKET_4BYTE_GUID:
- if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_4BYTE_GUID)) {
+ case PACKET_4BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID)) {
return false;
}
- if (!writer->WriteUInt32(header.public_header.guid & k4ByteGuidMask)) {
+ if (!writer->WriteUInt32(
+ header.public_header.connection_id & k4ByteConnectionIdMask)) {
return false;
}
break;
- case PACKET_8BYTE_GUID:
- if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_8BYTE_GUID)) {
+ case PACKET_8BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID)) {
return false;
}
- if (!writer->WriteUInt64(header.public_header.guid)) {
+ if (!writer->WriteUInt64(header.public_header.connection_id)) {
return false;
}
break;
}
- last_serialized_guid_ = header.public_header.guid;
+ last_serialized_connection_id_ = header.public_header.connection_id;
if (header.public_header.version_flag) {
DCHECK(!is_server_);
@@ -717,7 +857,8 @@ bool QuicFramer::ProcessPublicHeader(
public_header->version_flag =
(public_flags & PACKET_PUBLIC_FLAGS_VERSION) != 0;
- if (!public_header->version_flag && public_flags > PACKET_PUBLIC_FLAGS_MAX) {
+ if (validate_flags_ &&
+ !public_header->version_flag && public_flags > PACKET_PUBLIC_FLAGS_MAX) {
set_detailed_error("Illegal public flags value.");
return false;
}
@@ -727,46 +868,49 @@ bool QuicFramer::ProcessPublicHeader(
return false;
}
- switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_GUID) {
- case PACKET_PUBLIC_FLAGS_8BYTE_GUID:
- if (!reader_->ReadUInt64(&public_header->guid)) {
- set_detailed_error("Unable to read GUID.");
+ switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID) {
+ case PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID:
+ if (!reader_->ReadUInt64(&public_header->connection_id)) {
+ set_detailed_error("Unable to read ConnectionId.");
return false;
}
- public_header->guid_length = PACKET_8BYTE_GUID;
+ public_header->connection_id_length = PACKET_8BYTE_CONNECTION_ID;
break;
- case PACKET_PUBLIC_FLAGS_4BYTE_GUID:
- // If the guid is truncated, expect to read the last serialized guid.
- if (!reader_->ReadBytes(&public_header->guid, PACKET_4BYTE_GUID)) {
- set_detailed_error("Unable to read GUID.");
+ case PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID:
+ // If the connection_id is truncated, expect to read the last serialized
+ // connection_id.
+ if (!reader_->ReadBytes(&public_header->connection_id,
+ PACKET_4BYTE_CONNECTION_ID)) {
+ set_detailed_error("Unable to read ConnectionId.");
return false;
}
- if ((public_header->guid & k4ByteGuidMask) !=
- (last_serialized_guid_ & k4ByteGuidMask)) {
- set_detailed_error(
- "Truncated 4 byte GUID does not match previous guid.");
+ if ((public_header->connection_id & k4ByteConnectionIdMask) !=
+ (last_serialized_connection_id_ & k4ByteConnectionIdMask)) {
+ set_detailed_error("Truncated 4 byte ConnectionId does not match "
+ "previous connection_id.");
return false;
}
- public_header->guid_length = PACKET_4BYTE_GUID;
- public_header->guid = last_serialized_guid_;
+ public_header->connection_id_length = PACKET_4BYTE_CONNECTION_ID;
+ public_header->connection_id = last_serialized_connection_id_;
break;
- case PACKET_PUBLIC_FLAGS_1BYTE_GUID:
- if (!reader_->ReadBytes(&public_header->guid, PACKET_1BYTE_GUID)) {
- set_detailed_error("Unable to read GUID.");
+ case PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID:
+ if (!reader_->ReadBytes(&public_header->connection_id,
+ PACKET_1BYTE_CONNECTION_ID)) {
+ set_detailed_error("Unable to read ConnectionId.");
return false;
}
- if ((public_header->guid & k1ByteGuidMask) !=
- (last_serialized_guid_ & k1ByteGuidMask)) {
- set_detailed_error(
- "Truncated 1 byte GUID does not match previous guid.");
+ if ((public_header->connection_id & k1ByteConnectionIdMask) !=
+ (last_serialized_connection_id_ & k1ByteConnectionIdMask)) {
+ set_detailed_error("Truncated 1 byte ConnectionId does not match "
+ "previous connection_id.");
return false;
}
- public_header->guid_length = PACKET_1BYTE_GUID;
- public_header->guid = last_serialized_guid_;
+ public_header->connection_id_length = PACKET_1BYTE_CONNECTION_ID;
+ public_header->connection_id = last_serialized_connection_id_;
break;
- case PACKET_PUBLIC_FLAGS_0BYTE_GUID:
- public_header->guid_length = PACKET_0BYTE_GUID;
- public_header->guid = last_serialized_guid_;
+ case PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID:
+ public_header->connection_id_length = PACKET_0BYTE_CONNECTION_ID;
+ public_header->connection_id = last_serialized_connection_id_;
break;
}
@@ -797,40 +941,6 @@ bool QuicFramer::ProcessPublicHeader(
}
// static
-bool QuicFramer::ReadGuidFromPacket(const QuicEncryptedPacket& packet,
- QuicGuid* guid) {
- QuicDataReader reader(packet.data(), packet.length());
- uint8 public_flags;
- if (!reader.ReadBytes(&public_flags, 1)) {
- return false;
- }
- // Ensure it's an 8 byte guid.
- if ((public_flags & PACKET_PUBLIC_FLAGS_8BYTE_GUID) !=
- PACKET_PUBLIC_FLAGS_8BYTE_GUID) {
- return false;
- }
-
- return reader.ReadUInt64(guid);
-}
-
-// static
-QuicSequenceNumberLength QuicFramer::ReadSequenceNumberLength(uint8 flags) {
- switch (flags & PACKET_FLAGS_6BYTE_SEQUENCE) {
- case PACKET_FLAGS_6BYTE_SEQUENCE:
- return PACKET_6BYTE_SEQUENCE_NUMBER;
- case PACKET_FLAGS_4BYTE_SEQUENCE:
- return PACKET_4BYTE_SEQUENCE_NUMBER;
- case PACKET_FLAGS_2BYTE_SEQUENCE:
- return PACKET_2BYTE_SEQUENCE_NUMBER;
- case PACKET_FLAGS_1BYTE_SEQUENCE:
- return PACKET_1BYTE_SEQUENCE_NUMBER;
- default:
- LOG(DFATAL) << "Unreachable case statement.";
- return PACKET_6BYTE_SEQUENCE_NUMBER;
- }
-}
-
-// static
QuicSequenceNumberLength QuicFramer::GetMinSequenceNumberLength(
QuicPacketSequenceNumber sequence_number) {
if (sequence_number < 1 << (PACKET_1BYTE_SEQUENCE_NUMBER * 8)) {
@@ -1082,6 +1192,65 @@ bool QuicFramer::ProcessFrameData(const QuicPacketHeader& header) {
continue;
}
+ case WINDOW_UPDATE_FRAME: {
+ QuicWindowUpdateFrame window_update_frame;
+ if (!ProcessWindowUpdateFrame(&window_update_frame)) {
+ return RaiseError(QUIC_INVALID_WINDOW_UPDATE_DATA);
+ }
+ if (!visitor_->OnWindowUpdateFrame(window_update_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case BLOCKED_FRAME: {
+ QuicBlockedFrame blocked_frame;
+ if (!ProcessBlockedFrame(&blocked_frame)) {
+ return RaiseError(QUIC_INVALID_BLOCKED_DATA);
+ }
+ if (!visitor_->OnBlockedFrame(blocked_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case STOP_WAITING_FRAME: {
+ if (quic_version_ <= QUIC_VERSION_15) {
+ LOG(DFATAL) << "Trying to read a StopWaiting in "
+ << QuicVersionToString(quic_version_);
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ }
+ QuicStopWaitingFrame stop_waiting_frame;
+ if (!ProcessStopWaitingFrame(header, &stop_waiting_frame)) {
+ return RaiseError(QUIC_INVALID_STOP_WAITING_DATA);
+ }
+ if (!visitor_->OnStopWaitingFrame(stop_waiting_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+ case PING_FRAME: {
+ if (quic_version_ <= QUIC_VERSION_17) {
+ LOG(DFATAL) << "Trying to read a Ping in "
+ << QuicVersionToString(quic_version_);
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ }
+ // Ping has no payload.
+ QuicPingFrame ping_frame;
+ if (!visitor_->OnPingFrame(ping_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
default:
set_detailed_error("Illegal frame type.");
DLOG(WARNING) << "Illegal frame type: "
@@ -1152,8 +1321,10 @@ bool QuicFramer::ProcessStreamFrame(uint8 frame_type,
bool QuicFramer::ProcessAckFrame(const QuicPacketHeader& header,
uint8 frame_type,
QuicAckFrame* frame) {
- if (!ProcessSentInfo(header, &frame->sent_info)) {
- return false;
+ if (quic_version_ <= QUIC_VERSION_15) {
+ if (!ProcessStopWaitingFrame(header, &frame->sent_info)) {
+ return false;
+ }
}
if (!ProcessReceivedInfo(frame_type, &frame->received_info)) {
return false;
@@ -1232,12 +1403,30 @@ bool QuicFramer::ProcessReceivedInfo(uint8 frame_type,
last_sequence_number -= (range_length + 1);
}
+ // Parse the revived packets list.
+ uint8 num_revived_packets;
+ if (!reader_->ReadBytes(&num_revived_packets, 1)) {
+ set_detailed_error("Unable to read num revived packets.");
+ return false;
+ }
+
+ for (size_t i = 0; i < num_revived_packets; ++i) {
+ QuicPacketSequenceNumber revived_packet = 0;
+ if (!reader_->ReadBytes(&revived_packet,
+ largest_observed_sequence_number_length)) {
+ set_detailed_error("Unable to read revived packet.");
+ return false;
+ }
+
+ received_info->revived_packets.insert(revived_packet);
+ }
+
return true;
}
-bool QuicFramer::ProcessSentInfo(const QuicPacketHeader& header,
- SentPacketInfo* sent_info) {
- if (!reader_->ReadBytes(&sent_info->entropy_hash, 1)) {
+bool QuicFramer::ProcessStopWaitingFrame(const QuicPacketHeader& header,
+ QuicStopWaitingFrame* stop_waiting) {
+ if (!reader_->ReadBytes(&stop_waiting->entropy_hash, 1)) {
set_detailed_error("Unable to read entropy hash for sent packets.");
return false;
}
@@ -1249,7 +1438,7 @@ bool QuicFramer::ProcessSentInfo(const QuicPacketHeader& header,
return false;
}
DCHECK_GE(header.packet_sequence_number, least_unacked_delta);
- sent_info->least_unacked =
+ stop_waiting->least_unacked =
header.packet_sequence_number - least_unacked_delta;
return true;
@@ -1269,12 +1458,6 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame(
case kInterArrival: {
CongestionFeedbackMessageInterArrival* inter_arrival =
&frame->inter_arrival;
- if (!reader_->ReadUInt16(
- &inter_arrival->accumulated_number_of_lost_packets)) {
- set_detailed_error(
- "Unable to read accumulated number of lost packets.");
- return false;
- }
uint8 num_received_packets;
if (!reader_->ReadBytes(&num_received_packets, 1)) {
set_detailed_error("Unable to read num received packets.");
@@ -1333,11 +1516,6 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame(
}
case kTCP: {
CongestionFeedbackMessageTCP* tcp = &frame->tcp;
- if (!reader_->ReadUInt16(&tcp->accumulated_number_of_lost_packets)) {
- set_detailed_error(
- "Unable to read accumulated number of lost packets.");
- return false;
- }
// TODO(ianswett): Remove receive window, since it's constant.
uint16 receive_window = 0;
if (!reader_->ReadUInt16(&receive_window)) {
@@ -1364,6 +1542,11 @@ bool QuicFramer::ProcessRstStreamFrame(QuicRstStreamFrame* frame) {
return false;
}
+ if (!reader_->ReadUInt64(&frame->byte_offset)) {
+ set_detailed_error("Unable to read rst stream sent byte offset.");
+ return false;
+ }
+
uint32 error_code;
if (!reader_->ReadUInt32(&error_code)) {
set_detailed_error("Unable to read rst stream error code.");
@@ -1444,26 +1627,54 @@ bool QuicFramer::ProcessGoAwayFrame(QuicGoAwayFrame* frame) {
return true;
}
+bool QuicFramer::ProcessWindowUpdateFrame(QuicWindowUpdateFrame* frame) {
+ if (!reader_->ReadUInt32(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ if (!reader_->ReadUInt64(&frame->byte_offset)) {
+ set_detailed_error("Unable to read window byte_offset.");
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicFramer::ProcessBlockedFrame(QuicBlockedFrame* frame) {
+ if (!reader_->ReadUInt32(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ return true;
+}
+
// static
StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket(
const QuicEncryptedPacket& encrypted,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool includes_version,
QuicSequenceNumberLength sequence_number_length) {
- return StringPiece(encrypted.data() + kStartOfHashData,
- GetStartOfEncryptedData(
- guid_length, includes_version, sequence_number_length)
- - kStartOfHashData);
+ return StringPiece(
+ encrypted.data() + kStartOfHashData, GetStartOfEncryptedData(
+ connection_id_length, includes_version, sequence_number_length)
+ - kStartOfHashData);
}
-void QuicFramer::SetDecrypter(QuicDecrypter* decrypter) {
+void QuicFramer::SetDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level) {
DCHECK(alternative_decrypter_.get() == NULL);
+ DCHECK_GE(level, decrypter_level_);
decrypter_.reset(decrypter);
+ decrypter_level_ = level;
}
void QuicFramer::SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
bool latch_once_used) {
alternative_decrypter_.reset(decrypter);
+ alternative_decrypter_level_ = level;
alternative_decrypter_latch_ = latch_once_used;
}
@@ -1489,18 +1700,6 @@ const QuicEncrypter* QuicFramer::encrypter(EncryptionLevel level) const {
return encrypter_[level].get();
}
-void QuicFramer::SwapCryptersForTest(QuicFramer* other) {
- for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) {
- encrypter_[i].swap(other->encrypter_[i]);
- }
- decrypter_.swap(other->decrypter_);
- alternative_decrypter_.swap(other->alternative_decrypter_);
-
- const bool other_latch = other->alternative_decrypter_latch_;
- other->alternative_decrypter_latch_ = alternative_decrypter_latch_;
- alternative_decrypter_latch_ = other_latch;
-}
-
QuicEncryptedPacket* QuicFramer::EncryptPacket(
EncryptionLevel level,
QuicPacketSequenceNumber packet_sequence_number,
@@ -1550,32 +1749,42 @@ bool QuicFramer::DecryptPayload(const QuicPacketHeader& header,
header.packet_sequence_number,
GetAssociatedDataFromEncryptedPacket(
packet,
- header.public_header.guid_length,
+ header.public_header.connection_id_length,
header.public_header.version_flag,
header.public_header.sequence_number_length),
encrypted));
- if (decrypted_.get() == NULL && alternative_decrypter_.get() != NULL) {
+ if (decrypted_.get() != NULL) {
+ visitor_->OnDecryptedPacket(decrypter_level_);
+ } else if (alternative_decrypter_.get() != NULL) {
decrypted_.reset(alternative_decrypter_->DecryptPacket(
header.packet_sequence_number,
GetAssociatedDataFromEncryptedPacket(
packet,
- header.public_header.guid_length,
+ header.public_header.connection_id_length,
header.public_header.version_flag,
header.public_header.sequence_number_length),
encrypted));
if (decrypted_.get() != NULL) {
+ visitor_->OnDecryptedPacket(alternative_decrypter_level_);
if (alternative_decrypter_latch_) {
// Switch to the alternative decrypter and latch so that we cannot
// switch back.
decrypter_.reset(alternative_decrypter_.release());
+ decrypter_level_ = alternative_decrypter_level_;
+ alternative_decrypter_level_ = ENCRYPTION_NONE;
} else {
// Switch the alternative decrypter so that we use it first next time.
decrypter_.swap(alternative_decrypter_);
+ EncryptionLevel level = alternative_decrypter_level_;
+ alternative_decrypter_level_ = decrypter_level_;
+ decrypter_level_ = level;
}
}
}
if (decrypted_.get() == NULL) {
+ DLOG(WARNING) << "DecryptPacket failed for sequence_number:"
+ << header.packet_sequence_number;
return false;
}
@@ -1592,24 +1801,31 @@ size_t QuicFramer::GetAckFrameSize(
QuicSequenceNumberLength missing_sequence_number_length =
GetMinSequenceNumberLength(ack_info.max_delta);
- return GetMinAckFrameSize(quic_version_,
- sequence_number_length,
- largest_observed_length) +
- (ack_info.nack_ranges.empty() ? 0 : kNumberOfMissingPacketsSize) +
- ack_info.nack_ranges.size() *
- (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER);
+ size_t ack_size = GetMinAckFrameSize(quic_version_,
+ sequence_number_length,
+ largest_observed_length);
+ if (!ack_info.nack_ranges.empty()) {
+ ack_size += kNumberOfNackRangesSize + kNumberOfRevivedPacketsSize;
+ ack_size += min(ack_info.nack_ranges.size(), kMaxNackRanges) *
+ (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER);
+ ack_size += min(ack.received_info.revived_packets.size(),
+ kMaxRevivedPackets) * largest_observed_length;
+ }
+ return ack_size;
}
size_t QuicFramer::ComputeFrameLength(
const QuicFrame& frame,
bool last_frame_in_packet,
+ InFecGroup is_in_fec_group,
QuicSequenceNumberLength sequence_number_length) {
switch (frame.type) {
case STREAM_FRAME:
return GetMinStreamFrameSize(quic_version_,
frame.stream_frame->stream_id,
frame.stream_frame->offset,
- last_frame_in_packet) +
+ last_frame_in_packet,
+ is_in_fec_group) +
frame.stream_frame->data.TotalBufferSize();
case ACK_FRAME: {
return GetAckFrameSize(*frame.ack_frame, sequence_number_length);
@@ -1624,7 +1840,6 @@ size_t QuicFramer::ComputeFrameLength(
case kInterArrival: {
const CongestionFeedbackMessageInterArrival& inter_arrival =
congestion_feedback.inter_arrival;
- len += 2;
len += 1; // Number received packets.
if (inter_arrival.received_packet_times.size() > 0) {
len += PACKET_6BYTE_SEQUENCE_NUMBER; // Smallest received.
@@ -1636,10 +1851,10 @@ size_t QuicFramer::ComputeFrameLength(
break;
}
case kFixRate:
- len += 4;
+ len += 4; // Bitrate.
break;
case kTCP:
- len += 4;
+ len += 2; // Receive window.
break;
default:
set_detailed_error("Illegal feedback type.");
@@ -1648,14 +1863,23 @@ size_t QuicFramer::ComputeFrameLength(
}
return len;
}
+ case STOP_WAITING_FRAME:
+ return GetStopWaitingFrameSize(sequence_number_length);
+ case PING_FRAME:
+ // Ping has no payload.
+ return kQuicFrameTypeSize;
case RST_STREAM_FRAME:
- return GetMinRstStreamFrameSize() +
+ return GetMinRstStreamFrameSize(quic_version_) +
frame.rst_stream_frame->error_details.size();
case CONNECTION_CLOSE_FRAME:
return GetMinConnectionCloseFrameSize() +
frame.connection_close_frame->error_details.size();
case GOAWAY_FRAME:
return GetMinGoAwayFrameSize() + frame.goaway_frame->reason_phrase.size();
+ case WINDOW_UPDATE_FRAME:
+ return GetWindowUpdateFrameSize();
+ case BLOCKED_FRAME:
+ return GetBlockedFrameSize();
case PADDING_FRAME:
DCHECK(false);
return 0;
@@ -1670,7 +1894,7 @@ size_t QuicFramer::ComputeFrameLength(
}
bool QuicFramer::AppendTypeByte(const QuicFrame& frame,
- bool last_frame_in_packet,
+ bool no_stream_frame_length,
QuicDataWriter* writer) {
uint8 type_byte = 0;
switch (frame.type) {
@@ -1683,7 +1907,7 @@ bool QuicFramer::AppendTypeByte(const QuicFrame& frame,
// Data Length bit.
type_byte <<= kQuicStreamDataLengthShift;
- type_byte |= last_frame_in_packet ? 0 : kQuicStreamDataLengthMask;
+ type_byte |= no_stream_frame_length ? 0: kQuicStreamDataLengthMask;
// Offset 3 bits.
type_byte <<= kQuicStreamOffsetShift;
@@ -1741,63 +1965,44 @@ bool QuicFramer::AppendPacketSequenceNumber(
packet_sequence_number & k6ByteSequenceNumberMask);
break;
default:
- NOTREACHED() << "sequence_number_length: " << sequence_number_length;
+ DCHECK(false) << "sequence_number_length: " << sequence_number_length;
return false;
}
}
-bool QuicFramer::AppendStreamFramePayload(
+bool QuicFramer::AppendStreamFrame(
const QuicStreamFrame& frame,
- bool last_frame_in_packet,
+ bool no_stream_frame_length,
QuicDataWriter* writer) {
if (!writer->WriteBytes(&frame.stream_id, GetStreamIdSize(frame.stream_id))) {
+ LOG(DFATAL) << "Writing stream id size failed.";
return false;
}
if (!writer->WriteBytes(&frame.offset, GetStreamOffsetSize(frame.offset))) {
+ LOG(DFATAL) << "Writing offset size failed.";
return false;
}
- if (!last_frame_in_packet) {
+ if (!no_stream_frame_length) {
if (!writer->WriteUInt16(frame.data.TotalBufferSize())) {
+ LOG(DFATAL) << "Writing stream frame length failed";
return false;
}
}
if (!writer->WriteIOVector(frame.data)) {
+ LOG(DFATAL) << "Writing frame data failed.";
return false;
}
return true;
}
// static
-bool QuicFramer::HasVersionFlag(const QuicEncryptedPacket& packet) {
- return packet.length() > 0 &&
- (packet.data()[0] & PACKET_PUBLIC_FLAGS_VERSION) != 0;
-}
-
-// static
-QuicPacketSequenceNumber QuicFramer::CalculateLargestObserved(
- const SequenceNumberSet& missing_packets,
- SequenceNumberSet::const_iterator largest_written) {
- SequenceNumberSet::const_iterator it = largest_written;
- QuicPacketSequenceNumber previous_missing = *it;
- ++it;
-
- // See if the next thing is a gap in the missing packets: if it's a
- // non-missing packet we can return it.
- if (it != missing_packets.end() && previous_missing + 1 != *it) {
- return *it - 1;
- }
-
- // Otherwise return the largest missing packet, as indirectly observed.
- return *largest_written;
-}
-
void QuicFramer::set_version(const QuicVersion version) {
- DCHECK(IsSupportedVersion(version));
+ DCHECK(IsSupportedVersion(version)) << QuicVersionToString(version);
quic_version_ = version;
}
-bool QuicFramer::AppendAckFramePayloadAndTypeByte(
+bool QuicFramer::AppendAckFrameAndTypeByte(
const QuicPacketHeader& header,
const QuicAckFrame& frame,
QuicDataWriter* writer) {
@@ -1810,18 +2015,17 @@ bool QuicFramer::AppendAckFramePayloadAndTypeByte(
GetMinSequenceNumberLength(ack_info.max_delta);
// Determine whether we need to truncate ranges.
size_t available_range_bytes = writer->capacity() - writer->length() -
+ kNumberOfRevivedPacketsSize - kNumberOfNackRangesSize -
GetMinAckFrameSize(quic_version_,
header.public_header.sequence_number_length,
largest_observed_length);
size_t max_num_ranges = available_range_bytes /
(missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER);
- max_num_ranges =
- min(static_cast<size_t>(numeric_limits<uint8>::max()), max_num_ranges);
+ max_num_ranges = min(kMaxNackRanges, max_num_ranges);
bool truncated = ack_info.nack_ranges.size() > max_num_ranges;
DVLOG_IF(1, truncated) << "Truncating ack from "
<< ack_info.nack_ranges.size() << " ranges to "
<< max_num_ranges;
-
// Write out the type byte by setting the low order bits and doing shifts
// to make room for the next bit flags to be set.
// Whether there are any nacks.
@@ -1845,18 +2049,10 @@ bool QuicFramer::AppendAckFramePayloadAndTypeByte(
return false;
}
- // TODO(satyamshekhar): Decide how often we really should send this
- // entropy_hash update.
- if (!writer->WriteUInt8(frame.sent_info.entropy_hash)) {
- return false;
- }
-
- DCHECK_GE(header.packet_sequence_number, frame.sent_info.least_unacked);
- const QuicPacketSequenceNumber least_unacked_delta =
- header.packet_sequence_number - frame.sent_info.least_unacked;
- if (!AppendPacketSequenceNumber(header.public_header.sequence_number_length,
- least_unacked_delta, writer)) {
- return false;
+ if (quic_version_ <= QUIC_VERSION_15) {
+ if (!AppendStopWaitingFrame(header, frame.sent_info, writer)) {
+ return false;
+ }
}
const ReceivedPacketInfo& received_info = frame.received_info;
@@ -1927,12 +2123,33 @@ bool QuicFramer::AppendAckFramePayloadAndTypeByte(
last_sequence_written = ack_iter->first - 1;
++num_ranges_written;
}
-
DCHECK_EQ(num_missing_ranges, num_ranges_written);
+
+ // Append revived packets.
+ // If not all the revived packets fit, only mention the ones that do.
+ uint8 num_revived_packets = min(received_info.revived_packets.size(),
+ kMaxRevivedPackets);
+ num_revived_packets = min(
+ static_cast<size_t>(num_revived_packets),
+ (writer->capacity() - writer->length()) / largest_observed_length);
+ if (!writer->WriteBytes(&num_revived_packets, 1)) {
+ return false;
+ }
+
+ SequenceNumberSet::const_iterator iter =
+ received_info.revived_packets.begin();
+ for (int i = 0; i < num_revived_packets; ++i, ++iter) {
+ LOG_IF(DFATAL, !ContainsKey(received_info.missing_packets, *iter));
+ if (!AppendPacketSequenceNumber(largest_observed_length,
+ *iter, writer)) {
+ return false;
+ }
+ }
+
return true;
}
-bool QuicFramer::AppendQuicCongestionFeedbackFramePayload(
+bool QuicFramer::AppendCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame,
QuicDataWriter* writer) {
if (!writer->WriteBytes(&frame.type, 1)) {
@@ -1943,10 +2160,6 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload(
case kInterArrival: {
const CongestionFeedbackMessageInterArrival& inter_arrival =
frame.inter_arrival;
- if (!writer->WriteUInt16(
- inter_arrival.accumulated_number_of_lost_packets)) {
- return false;
- }
DCHECK_GE(numeric_limits<uint8>::max(),
inter_arrival.received_packet_times.size());
if (inter_arrival.received_packet_times.size() >
@@ -2007,9 +2220,6 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload(
DCHECK_LE(tcp.receive_window, 1u << 20);
// Simple bit packing, don't send the 4 least significant bits.
uint16 receive_window = static_cast<uint16>(tcp.receive_window >> 4);
- if (!writer->WriteUInt16(tcp.accumulated_number_of_lost_packets)) {
- return false;
- }
if (!writer->WriteUInt16(receive_window)) {
return false;
}
@@ -2022,13 +2232,48 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload(
return true;
}
-bool QuicFramer::AppendRstStreamFramePayload(
+bool QuicFramer::AppendStopWaitingFrame(
+ const QuicPacketHeader& header,
+ const QuicStopWaitingFrame& frame,
+ QuicDataWriter* writer) {
+ DCHECK_GE(header.packet_sequence_number, frame.least_unacked);
+ const QuicPacketSequenceNumber least_unacked_delta =
+ header.packet_sequence_number - frame.least_unacked;
+ const QuicPacketSequenceNumber length_shift =
+ header.public_header.sequence_number_length * 8;
+ if (!writer->WriteUInt8(frame.entropy_hash)) {
+ LOG(DFATAL) << " hash failed";
+ return false;
+ }
+
+ if (least_unacked_delta >> length_shift > 0) {
+ LOG(DFATAL) << "sequence_number_length "
+ << header.public_header.sequence_number_length
+ << " is too small for least_unacked_delta: "
+ << least_unacked_delta;
+ return false;
+ }
+ if (!AppendPacketSequenceNumber(header.public_header.sequence_number_length,
+ least_unacked_delta, writer)) {
+ LOG(DFATAL) << " seq failed: "
+ << header.public_header.sequence_number_length;
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicFramer::AppendRstStreamFrame(
const QuicRstStreamFrame& frame,
QuicDataWriter* writer) {
if (!writer->WriteUInt32(frame.stream_id)) {
return false;
}
+ if (!writer->WriteUInt64(frame.byte_offset)) {
+ return false;
+ }
+
uint32 error_code = static_cast<uint32>(frame.error_code);
if (!writer->WriteUInt32(error_code)) {
return false;
@@ -2040,7 +2285,7 @@ bool QuicFramer::AppendRstStreamFramePayload(
return true;
}
-bool QuicFramer::AppendConnectionCloseFramePayload(
+bool QuicFramer::AppendConnectionCloseFrame(
const QuicConnectionCloseFrame& frame,
QuicDataWriter* writer) {
uint32 error_code = static_cast<uint32>(frame.error_code);
@@ -2053,8 +2298,8 @@ bool QuicFramer::AppendConnectionCloseFramePayload(
return true;
}
-bool QuicFramer::AppendGoAwayFramePayload(const QuicGoAwayFrame& frame,
- QuicDataWriter* writer) {
+bool QuicFramer::AppendGoAwayFrame(const QuicGoAwayFrame& frame,
+ QuicDataWriter* writer) {
uint32 error_code = static_cast<uint32>(frame.error_code);
if (!writer->WriteUInt32(error_code)) {
return false;
@@ -2069,8 +2314,29 @@ bool QuicFramer::AppendGoAwayFramePayload(const QuicGoAwayFrame& frame,
return true;
}
+bool QuicFramer::AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer) {
+ uint32 stream_id = static_cast<uint32>(frame.stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ if (!writer->WriteUInt64(frame.byte_offset)) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ uint32 stream_id = static_cast<uint32>(frame.stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ return true;
+}
+
bool QuicFramer::RaiseError(QuicErrorCode error) {
- DVLOG(1) << detailed_error_;
+ DVLOG(1) << "Error detail: " << detailed_error_;
set_error(error);
visitor_->OnError(this);
reader_.reset(NULL);
diff --git a/chromium/net/quic/quic_framer.h b/chromium/net/quic/quic_framer.h
index 132375200be..25518109a41 100644
--- a/chromium/net/quic/quic_framer.h
+++ b/chromium/net/quic/quic_framer.h
@@ -46,7 +46,15 @@ const size_t kQuicEntropyHashSize = 1;
// sequence number in ack frames.
const size_t kQuicDeltaTimeLargestObservedSize = 2;
// Size in bytes reserved for the number of missing packets in ack frames.
-const size_t kNumberOfMissingPacketsSize = 1;
+const size_t kNumberOfNackRangesSize = 1;
+// Maximum number of missing packet ranges that can fit within an ack frame.
+const size_t kMaxNackRanges =
+ (1 << (kNumberOfNackRangesSize * 8)) - 1;
+// Size in bytes reserved for the number of revived packets in ack frames.
+const size_t kNumberOfRevivedPacketsSize = 1;
+// Maximum number of revived packets that can fit within an ack frame.
+const size_t kMaxRevivedPackets =
+ (1 << (kNumberOfRevivedPacketsSize * 8)) - 1;
// This class receives callbacks from the framer when packets
// are processed.
@@ -82,11 +90,20 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface {
// before it has been processed.
virtual void OnRevivedPacket() = 0;
+ // Called when the public header has been parsed, but has not been
+ // authenticated. If it returns false, framing for this packet will cease.
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) = 0;
+
// Called when the unauthenticated portion of the header has been parsed.
// If OnUnauthenticatedHeader returns false, framing for this packet will
// cease.
virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) = 0;
+ // Called when a packet has been decrypted. |level| is the encryption level
+ // of the packet.
+ virtual void OnDecryptedPacket(EncryptionLevel level) = 0;
+
// Called when the complete header of a packet had been parsed.
// If OnPacketHeader returns false, framing for this packet will cease.
virtual bool OnPacketHeader(const QuicPacketHeader& header) = 0;
@@ -106,6 +123,12 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface {
virtual bool OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) = 0;
+ // Called when a StopWaitingFrame has been parsed.
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) = 0;
+
+ // Called when a PingFrame has been parsed.
+ virtual bool OnPingFrame(const QuicPingFrame& frame) = 0;
+
// Called when a RstStreamFrame has been parsed.
virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) = 0;
@@ -116,6 +139,12 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface {
// Called when a GoAwayFrame has been parsed.
virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) = 0;
+ // Called when a WindowUpdateFrame has been parsed.
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) = 0;
+
+ // Called when a BlockedFrame has been parsed.
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) = 0;
+
// Called when FEC data has been parsed.
virtual void OnFecData(const QuicFecData& fec) = 0;
@@ -167,16 +196,6 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Returns true if |version| is a supported protocol version.
bool IsSupportedVersion(const QuicVersion version) const;
- // Returns true if the version flag is set in the public flags.
- static bool HasVersionFlag(const QuicEncryptedPacket& packet);
-
- // Calculates the largest observed packet to advertise in the case an Ack
- // Frame was truncated. last_written in this case is the iterator for the
- // last missing packet which fit in the outgoing ack.
- static QuicPacketSequenceNumber CalculateLargestObserved(
- const SequenceNumberSet& missing_packets,
- SequenceNumberSet::const_iterator last_written);
-
// Set callbacks to be called from the framer. A visitor must be set, or
// else the framer will likely crash. It is acceptable for the visitor
// to do nothing. If this is called multiple times, only the last visitor
@@ -239,22 +258,27 @@ class NET_EXPORT_PRIVATE QuicFramer {
static size_t GetMinStreamFrameSize(QuicVersion version,
QuicStreamId stream_id,
QuicStreamOffset offset,
- bool last_frame_in_packet);
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group);
// Size in bytes of all ack frame fields without the missing packets.
static size_t GetMinAckFrameSize(
QuicVersion version,
QuicSequenceNumberLength sequence_number_length,
QuicSequenceNumberLength largest_observed_length);
+ // Size in bytes of a stop waiting frame.
+ static size_t GetStopWaitingFrameSize(
+ QuicSequenceNumberLength sequence_number_length);
// Size in bytes of all reset stream frame without the error details.
- static size_t GetMinRstStreamFrameSize();
+ static size_t GetMinRstStreamFrameSize(QuicVersion quic_version);
// Size in bytes of all connection close frame fields without the error
// details and the missing packets from the enclosed ack frame.
static size_t GetMinConnectionCloseFrameSize();
// Size in bytes of all GoAway frame fields without the reason phrase.
static size_t GetMinGoAwayFrameSize();
- // The maximum number of nacks which can be transmitted in a single ack packet
- // without exceeding kDefaultMaxPacketSize.
- static size_t GetMaxUnackedPackets(QuicPacketHeader header);
+ // Size in bytes of all WindowUpdate frame fields.
+ static size_t GetWindowUpdateFrameSize();
+ // Size in bytes of all Blocked frame fields.
+ static size_t GetBlockedFrameSize();
// Size in bytes required to serialize the stream id.
static size_t GetStreamIdSize(QuicStreamId stream_id);
// Size in bytes required to serialize the stream offset.
@@ -262,36 +286,26 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Size in bytes required for a serialized version negotiation packet
static size_t GetVersionNegotiationPacketSize(size_t number_versions);
-
- static bool CanTruncate(
- QuicVersion version, const QuicFrame& frame, size_t free_bytes);
-
// Returns the number of bytes added to the packet for the specified frame,
// and 0 if the frame doesn't fit. Includes the header size for the first
// frame.
size_t GetSerializedFrameLength(
const QuicFrame& frame,
size_t free_bytes,
- bool first_frame,
- bool last_frame,
+ bool first_frame_in_packet,
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group,
QuicSequenceNumberLength sequence_number_length);
// Returns the associated data from the encrypted packet |encrypted| as a
// stringpiece.
static base::StringPiece GetAssociatedDataFromEncryptedPacket(
const QuicEncryptedPacket& encrypted,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool includes_version,
QuicSequenceNumberLength sequence_number_length);
// Returns a SerializedPacket whose |packet| member is owned by the caller,
- // and is populated with the fields in |header| and |frames|, or is NULL if
- // the packet could not be created.
- // TODO(ianswett): Used for testing only.
- SerializedPacket BuildUnsizedDataPacket(const QuicPacketHeader& header,
- const QuicFrames& frames);
-
- // Returns a SerializedPacket whose |packet| member is owned by the caller,
// is created from the first |num_frames| frames, or is NULL if the packet
// could not be created. The packet must be of size |packet_size|.
SerializedPacket BuildDataPacket(const QuicPacketHeader& header,
@@ -315,16 +329,18 @@ class NET_EXPORT_PRIVATE QuicFramer {
// SetDecrypter sets the primary decrypter, replacing any that already exists,
// and takes ownership. If an alternative decrypter is in place then the
// function DCHECKs. This is intended for cases where one knows that future
- // packets will be using the new decrypter and the previous decrypter is not
- // obsolete.
- void SetDecrypter(QuicDecrypter* decrypter);
+ // packets will be using the new decrypter and the previous decrypter is now
+ // obsolete. |level| indicates the encryption level of the new decrypter.
+ void SetDecrypter(QuicDecrypter* decrypter, EncryptionLevel level);
// SetAlternativeDecrypter sets a decrypter that may be used to decrypt
- // future packets and takes ownership of it. If |latch_once_used| is true,
- // then the first time that the decrypter is successful it will replace the
- // primary decrypter. Otherwise both decrypters will remain active and the
- // primary decrypter will be the one last used.
+ // future packets and takes ownership of it. |level| indicates the encryption
+ // level of the decrypter. If |latch_once_used| is true, then the first time
+ // that the decrypter is successful it will replace the primary decrypter.
+ // Otherwise both decrypters will remain active and the primary decrypter
+ // will be the one last used.
void SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
bool latch_once_used);
const QuicDecrypter* decrypter() const;
@@ -335,10 +351,6 @@ class NET_EXPORT_PRIVATE QuicFramer {
void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter);
const QuicEncrypter* encrypter(EncryptionLevel level) const;
- // SwapCryptersForTest exchanges the state of the crypters with |other|. To
- // be used in tests only.
- void SwapCryptersForTest(QuicFramer* other);
-
// Returns a new encrypted packet, owned by the caller.
QuicEncryptedPacket* EncryptPacket(EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
@@ -350,17 +362,19 @@ class NET_EXPORT_PRIVATE QuicFramer {
const std::string& detailed_error() { return detailed_error_; }
- // Read the full 8 byte guid from a packet header.
- // Return true on success, else false.
- static bool ReadGuidFromPacket(const QuicEncryptedPacket& packet,
- QuicGuid* guid);
-
- static QuicSequenceNumberLength ReadSequenceNumberLength(uint8 flags);
-
// The minimum sequence number length required to represent |sequence_number|.
static QuicSequenceNumberLength GetMinSequenceNumberLength(
QuicPacketSequenceNumber sequence_number);
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ supported_versions_ = versions;
+ quic_version_ = versions[0];
+ }
+
+ void set_validate_flags(bool value) { validate_flags_ = value; }
+
+ bool is_server() const { return is_server_; }
+
private:
friend class test::QuicFramerPeer;
@@ -400,13 +414,15 @@ class NET_EXPORT_PRIVATE QuicFramer {
uint8 frame_type,
QuicAckFrame* frame);
bool ProcessReceivedInfo(uint8 frame_type, ReceivedPacketInfo* received_info);
- bool ProcessSentInfo(const QuicPacketHeader& public_header,
- SentPacketInfo* sent_info);
+ bool ProcessStopWaitingFrame(const QuicPacketHeader& public_header,
+ QuicStopWaitingFrame* stop_waiting);
bool ProcessQuicCongestionFeedbackFrame(
QuicCongestionFeedbackFrame* congestion_feedback);
bool ProcessRstStreamFrame(QuicRstStreamFrame* frame);
bool ProcessConnectionCloseFrame(QuicConnectionCloseFrame* frame);
bool ProcessGoAwayFrame(QuicGoAwayFrame* frame);
+ bool ProcessWindowUpdateFrame(QuicWindowUpdateFrame* frame);
+ bool ProcessBlockedFrame(QuicBlockedFrame* frame);
bool DecryptPayload(const QuicPacketHeader& header,
const QuicEncryptedPacket& packet);
@@ -424,6 +440,7 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Computes the wire size in bytes of the payload of |frame|.
size_t ComputeFrameLength(const QuicFrame& frame,
bool last_frame_in_packet,
+ InFecGroup is_in_fec_group,
QuicSequenceNumberLength sequence_number_length);
static bool AppendPacketSequenceNumber(
@@ -436,27 +453,34 @@ class NET_EXPORT_PRIVATE QuicFramer {
static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame);
+ // The Append* methods attempt to write the provided header or frame using the
+ // |writer|, and return true if successful.
bool AppendPacketHeader(const QuicPacketHeader& header,
QuicDataWriter* writer);
bool AppendTypeByte(const QuicFrame& frame,
bool last_frame_in_packet,
QuicDataWriter* writer);
- bool AppendStreamFramePayload(const QuicStreamFrame& frame,
- bool last_frame_in_packet,
- QuicDataWriter* builder);
- bool AppendAckFramePayloadAndTypeByte(const QuicPacketHeader& header,
- const QuicAckFrame& frame,
- QuicDataWriter* builder);
- bool AppendQuicCongestionFeedbackFramePayload(
- const QuicCongestionFeedbackFrame& frame,
- QuicDataWriter* builder);
- bool AppendRstStreamFramePayload(const QuicRstStreamFrame& frame,
- QuicDataWriter* builder);
- bool AppendConnectionCloseFramePayload(
- const QuicConnectionCloseFrame& frame,
- QuicDataWriter* builder);
- bool AppendGoAwayFramePayload(const QuicGoAwayFrame& frame,
- QuicDataWriter* writer);
+ bool AppendStreamFrame(const QuicStreamFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* builder);
+ bool AppendAckFrameAndTypeByte(const QuicPacketHeader& header,
+ const QuicAckFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendCongestionFeedbackFrame(const QuicCongestionFeedbackFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendStopWaitingFrame(const QuicPacketHeader& header,
+ const QuicStopWaitingFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendRstStreamFrame(const QuicRstStreamFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendConnectionCloseFrame(const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendGoAwayFrame(const QuicGoAwayFrame& frame, QuicDataWriter* writer);
+ bool AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer);
+
bool RaiseError(QuicErrorCode error);
void set_error(QuicErrorCode error) {
@@ -476,7 +500,7 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Updated by ProcessPacketHeader when it succeeds.
QuicPacketSequenceNumber last_sequence_number_;
// Updated by WritePacketHeader.
- QuicGuid last_serialized_guid_;
+ QuicConnectionId last_serialized_connection_id_;
// Buffer containing decrypted payload data during parsing.
scoped_ptr<QuicData> decrypted_;
// Version of the protocol being used.
@@ -490,7 +514,11 @@ class NET_EXPORT_PRIVATE QuicFramer {
scoped_ptr<QuicDecrypter> decrypter_;
// Alternative decrypter that can also be used to decrypt packets.
scoped_ptr<QuicDecrypter> alternative_decrypter_;
- // alternative_decrypter_latch_is true if, when |alternative_decrypter_|
+ // The encryption level of |decrypter_|.
+ EncryptionLevel decrypter_level_;
+ // The encryption level of |alternative_decrypter_|.
+ EncryptionLevel alternative_decrypter_level_;
+ // |alternative_decrypter_latch_| is true if, when |alternative_decrypter_|
// successfully decrypts a packet, we should install it as the only
// decrypter.
bool alternative_decrypter_latch_;
@@ -499,6 +527,8 @@ class NET_EXPORT_PRIVATE QuicFramer {
// Tracks if the framer is being used by the entity that received the
// connection or the entity that initiated it.
bool is_server_;
+ // If false, skip validation that the public flags are set to legal values.
+ bool validate_flags_;
// The time this frames was created. Time written to the wire will be
// written as a delta from this value.
QuicTime creation_time_;
diff --git a/chromium/net/quic/quic_framer_test.cc b/chromium/net/quic/quic_framer_test.cc
index eef4a5894c1..fd206e18620 100644
--- a/chromium/net/quic/quic_framer_test.cc
+++ b/chromium/net/quic/quic_framer_test.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "net/quic/quic_framer.h"
+
#include <algorithm>
#include <map>
#include <string>
@@ -14,11 +16,11 @@
#include "base/stl_util.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
-#include "net/quic/quic_framer.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_framer_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
using base::hash_set;
using base::StringPiece;
@@ -37,46 +39,54 @@ namespace test {
const QuicPacketSequenceNumber kEpoch = GG_UINT64_C(1) << 48;
const QuicPacketSequenceNumber kMask = kEpoch - 1;
-// Index into the guid offset in the header.
-const size_t kGuidOffset = kPublicFlagsSize;
+// Index into the connection_id offset in the header.
+const size_t kConnectionIdOffset = kPublicFlagsSize;
// Index into the version string in the header. (if present).
-const size_t kVersionOffset = kGuidOffset + PACKET_8BYTE_GUID;
+const size_t kVersionOffset = kConnectionIdOffset + PACKET_8BYTE_CONNECTION_ID;
+
+// Size in bytes of the stream frame fields for an arbitrary StreamID and
+// offset and the last frame in a packet.
+size_t GetMinStreamFrameSize(QuicVersion version) {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
+}
// Index into the sequence number offset in the header.
-size_t GetSequenceNumberOffset(QuicGuidLength guid_length,
+size_t GetSequenceNumberOffset(QuicConnectionIdLength connection_id_length,
bool include_version) {
- return kGuidOffset + guid_length +
+ return kConnectionIdOffset + connection_id_length +
(include_version ? kQuicVersionSize : 0);
}
size_t GetSequenceNumberOffset(bool include_version) {
- return GetSequenceNumberOffset(PACKET_8BYTE_GUID, include_version);
+ return GetSequenceNumberOffset(PACKET_8BYTE_CONNECTION_ID, include_version);
}
// Index into the private flags offset in the data packet header.
-size_t GetPrivateFlagsOffset(QuicGuidLength guid_length, bool include_version) {
- return GetSequenceNumberOffset(guid_length, include_version) +
+size_t GetPrivateFlagsOffset(QuicConnectionIdLength connection_id_length,
+ bool include_version) {
+ return GetSequenceNumberOffset(connection_id_length, include_version) +
PACKET_6BYTE_SEQUENCE_NUMBER;
}
size_t GetPrivateFlagsOffset(bool include_version) {
- return GetPrivateFlagsOffset(PACKET_8BYTE_GUID, include_version);
+ return GetPrivateFlagsOffset(PACKET_8BYTE_CONNECTION_ID, include_version);
}
size_t GetPrivateFlagsOffset(bool include_version,
QuicSequenceNumberLength sequence_number_length) {
- return GetSequenceNumberOffset(PACKET_8BYTE_GUID, include_version) +
+ return GetSequenceNumberOffset(PACKET_8BYTE_CONNECTION_ID, include_version) +
sequence_number_length;
}
// Index into the fec group offset in the header.
-size_t GetFecGroupOffset(QuicGuidLength guid_length, bool include_version) {
- return GetPrivateFlagsOffset(guid_length, include_version) +
+size_t GetFecGroupOffset(QuicConnectionIdLength connection_id_length,
+ bool include_version) {
+ return GetPrivateFlagsOffset(connection_id_length, include_version) +
kPrivateFlagsSize;
}
size_t GetFecGroupOffset(bool include_version) {
- return GetPrivateFlagsOffset(PACKET_8BYTE_GUID, include_version) +
+ return GetPrivateFlagsOffset(PACKET_8BYTE_CONNECTION_ID, include_version) +
kPrivateFlagsSize;
}
@@ -86,14 +96,10 @@ size_t GetFecGroupOffset(bool include_version,
kPrivateFlagsSize;
}
-// Index into the nonce proof of the public reset packet.
-// Public resets always have full guids.
-const size_t kPublicResetPacketNonceProofOffset =
- kGuidOffset + PACKET_8BYTE_GUID;
-
-// Index into the rejected sequence number of the public reset packet.
-const size_t kPublicResetPacketRejectedSequenceNumberOffset =
- kPublicResetPacketNonceProofOffset + kPublicResetNonceSize;
+// Index into the message tag of the public reset packet.
+// Public resets always have full connection_ids.
+const size_t kPublicResetPacketMessageTagOffset =
+ kConnectionIdOffset + PACKET_8BYTE_CONNECTION_ID;
class TestEncrypter : public QuicEncrypter {
public:
@@ -188,20 +194,23 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
fec_count_(0),
complete_packets_(0),
revived_packets_(0),
- accept_packet_(true) {
+ accept_packet_(true),
+ accept_public_header_(true) {
}
virtual ~TestQuicVisitor() {
STLDeleteElements(&stream_frames_);
STLDeleteElements(&ack_frames_);
STLDeleteElements(&congestion_feedback_frames_);
+ STLDeleteElements(&stop_waiting_frames_);
+ STLDeleteElements(&ping_frames_);
STLDeleteElements(&fec_data_);
}
virtual void OnError(QuicFramer* f) OVERRIDE {
- DLOG(INFO) << "QuicFramer Error: " << QuicUtils::ErrorToString(f->error())
- << " (" << f->error() << ")";
- error_count_++;
+ DVLOG(1) << "QuicFramer Error: " << QuicUtils::ErrorToString(f->error())
+ << " (" << f->error() << ")";
+ ++error_count_;
}
virtual void OnPacket() OVERRIDE {}
@@ -217,19 +226,19 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
}
virtual void OnRevivedPacket() OVERRIDE {
- revived_packets_++;
+ ++revived_packets_;
}
virtual bool OnProtocolVersionMismatch(QuicVersion version) OVERRIDE {
- DLOG(INFO) << "QuicFramer Version Mismatch, version: " << version;
- version_mismatch_++;
+ DVLOG(1) << "QuicFramer Version Mismatch, version: " << version;
+ ++version_mismatch_;
return true;
}
- virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE {
- packet_count_++;
- header_.reset(new QuicPacketHeader(header));
- return accept_packet_;
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE {
+ public_header_.reset(new QuicPacketPublicHeader(header));
+ return accept_public_header_;
}
virtual bool OnUnauthenticatedHeader(
@@ -237,8 +246,16 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
return true;
}
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {}
+
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE {
+ ++packet_count_;
+ header_.reset(new QuicPacketHeader(header));
+ return accept_packet_;
+ }
+
virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE {
- frame_count_++;
+ ++frame_count_;
stream_frames_.push_back(new QuicStreamFrame(frame));
return true;
}
@@ -248,26 +265,38 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
}
virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE {
- frame_count_++;
+ ++frame_count_;
ack_frames_.push_back(new QuicAckFrame(frame));
return true;
}
virtual bool OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) OVERRIDE {
- frame_count_++;
+ ++frame_count_;
congestion_feedback_frames_.push_back(
new QuicCongestionFeedbackFrame(frame));
return true;
}
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE {
+ ++frame_count_;
+ stop_waiting_frames_.push_back(new QuicStopWaitingFrame(frame));
+ return true;
+ }
+
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE {
+ ++frame_count_;
+ ping_frames_.push_back(new QuicPingFrame(frame));
+ return true;
+ }
+
virtual void OnFecData(const QuicFecData& fec) OVERRIDE {
- fec_count_++;
+ ++fec_count_;
fec_data_.push_back(new QuicFecData(fec));
}
virtual void OnPacketComplete() OVERRIDE {
- complete_packets_++;
+ ++complete_packets_;
}
virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE {
@@ -286,6 +315,17 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
return true;
}
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame)
+ OVERRIDE {
+ window_update_frame_ = frame;
+ return true;
+ }
+
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+ blocked_frame_ = frame;
+ return true;
+ }
+
// Counters from the visitor_ callbacks.
int error_count_;
int version_mismatch_;
@@ -295,18 +335,24 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
int complete_packets_;
int revived_packets_;
bool accept_packet_;
+ bool accept_public_header_;
scoped_ptr<QuicPacketHeader> header_;
+ scoped_ptr<QuicPacketPublicHeader> public_header_;
scoped_ptr<QuicPublicResetPacket> public_reset_packet_;
scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
vector<QuicStreamFrame*> stream_frames_;
vector<QuicAckFrame*> ack_frames_;
vector<QuicCongestionFeedbackFrame*> congestion_feedback_frames_;
+ vector<QuicStopWaitingFrame*> stop_waiting_frames_;
+ vector<QuicPingFrame*> ping_frames_;
vector<QuicFecData*> fec_data_;
string fec_protected_payload_;
QuicRstStreamFrame rst_stream_frame_;
QuicConnectionCloseFrame connection_close_frame_;
QuicGoAwayFrame goaway_frame_;
+ QuicWindowUpdateFrame window_update_frame_;
+ QuicBlockedFrame blocked_frame_;
};
class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
@@ -318,7 +364,7 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
framer_(QuicSupportedVersions(), start_, true) {
version_ = GetParam();
framer_.set_version(version_);
- framer_.SetDecrypter(decrypter_);
+ framer_.SetDecrypter(decrypter_, ENCRYPTION_NONE);
framer_.SetEncrypter(ENCRYPTION_NONE, encrypter_);
framer_.set_visitor(&visitor_);
framer_.set_received_entropy_calculator(&entropy_calculator_);
@@ -369,18 +415,18 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
return false;
}
if (QuicFramer::GetAssociatedDataFromEncryptedPacket(
- encrypted, PACKET_8BYTE_GUID,
+ encrypted, PACKET_8BYTE_CONNECTION_ID,
includes_version, PACKET_6BYTE_SEQUENCE_NUMBER) !=
decrypter_->associated_data_) {
LOG(ERROR) << "Decrypted incorrect associated data. expected "
<< QuicFramer::GetAssociatedDataFromEncryptedPacket(
- encrypted, PACKET_8BYTE_GUID,
+ encrypted, PACKET_8BYTE_CONNECTION_ID,
includes_version, PACKET_6BYTE_SEQUENCE_NUMBER)
<< " actual: " << decrypter_->associated_data_;
return false;
}
StringPiece ciphertext(encrypted.AsStringPiece().substr(
- GetStartOfEncryptedData(PACKET_8BYTE_GUID, includes_version,
+ GetStartOfEncryptedData(PACKET_8BYTE_CONNECTION_ID, includes_version,
PACKET_6BYTE_SEQUENCE_NUMBER)));
if (ciphertext != decrypter_->ciphertext_) {
LOG(ERROR) << "Decrypted incorrect ciphertext data. expected "
@@ -428,7 +474,7 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, include_version,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, include_version,
PACKET_6BYTE_SEQUENCE_NUMBER,
NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_STREAM_DATA);
@@ -448,6 +494,11 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
<< " wire_sequence_number: " << wire_sequence_number;
}
+ QuicPacket* BuildDataPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames) {
+ return BuildUnsizedDataPacket(&framer_, header, frames).packet;
+ }
+
test::TestEncrypter* encrypter_;
test::TestDecrypter* decrypter_;
QuicVersion version_;
@@ -572,9 +623,9 @@ TEST_P(QuicFramerTest, EmptyPacket) {
TEST_P(QuicFramerTest, LargePacket) {
unsigned char packet[kMaxPacketSize + 1] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -585,10 +636,10 @@ TEST_P(QuicFramerTest, LargePacket) {
};
memset(packet + GetPacketHeaderSize(
- PACKET_8BYTE_GUID, !kIncludeVersion,
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), 0,
kMaxPacketSize - GetPacketHeaderSize(
- PACKET_8BYTE_GUID, !kIncludeVersion,
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP) + 1);
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
@@ -597,16 +648,16 @@ TEST_P(QuicFramerTest, LargePacket) {
ASSERT_TRUE(visitor_.header_.get());
// Make sure we've parsed the packet header, so we can send an error.
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
// Make sure the correct error is propagated.
EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error());
}
TEST_P(QuicFramerTest, PacketHeader) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -621,7 +672,7 @@ TEST_P(QuicFramerTest, PacketHeader) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -634,14 +685,14 @@ TEST_P(QuicFramerTest, PacketHeader) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else if (i < GetPrivateFlagsOffset(!kIncludeVersion)) {
expected_error = "Unable to read sequence number.";
} else if (i < GetFecGroupOffset(!kIncludeVersion)) {
@@ -653,14 +704,14 @@ TEST_P(QuicFramerTest, PacketHeader) {
}
}
-TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) {
- QuicFramerPeer::SetLastSerializedGuid(&framer_,
- GG_UINT64_C(0xFEDCBA9876543210));
+TEST_P(QuicFramerTest, PacketHeaderWith4ByteConnectionId) {
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ &framer_, GG_UINT64_C(0xFEDCBA9876543210));
unsigned char packet[] = {
- // public flags (4 byte guid)
+ // public flags (4 byte connection_id)
0x38,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
@@ -674,7 +725,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -687,19 +738,20 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_4BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_4BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
- } else if (i < GetSequenceNumberOffset(PACKET_4BYTE_GUID,
+ } else if (i < GetSequenceNumberOffset(PACKET_4BYTE_CONNECTION_ID,
!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
- } else if (i < GetPrivateFlagsOffset(PACKET_4BYTE_GUID,
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(PACKET_4BYTE_CONNECTION_ID,
!kIncludeVersion)) {
expected_error = "Unable to read sequence number.";
- } else if (i < GetFecGroupOffset(PACKET_4BYTE_GUID, !kIncludeVersion)) {
+ } else if (i < GetFecGroupOffset(PACKET_4BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
expected_error = "Unable to read private flags.";
} else {
expected_error = "Unable to read first fec protected packet offset.";
@@ -708,14 +760,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) {
}
}
-TEST_P(QuicFramerTest, PacketHeader1ByteGuid) {
- QuicFramerPeer::SetLastSerializedGuid(&framer_,
- GG_UINT64_C(0xFEDCBA9876543210));
+TEST_P(QuicFramerTest, PacketHeader1ByteConnectionId) {
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ &framer_, GG_UINT64_C(0xFEDCBA9876543210));
unsigned char packet[] = {
- // public flags (1 byte guid)
+ // public flags (1 byte connection_id)
0x34,
- // guid
+ // connection_id
0x10,
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
@@ -729,7 +781,7 @@ TEST_P(QuicFramerTest, PacketHeader1ByteGuid) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -742,18 +794,20 @@ TEST_P(QuicFramerTest, PacketHeader1ByteGuid) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_1BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_1BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
- } else if (i < GetSequenceNumberOffset(PACKET_1BYTE_GUID,
+ } else if (i < GetSequenceNumberOffset(PACKET_1BYTE_CONNECTION_ID,
!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
- } else if (i < GetPrivateFlagsOffset(PACKET_1BYTE_GUID, !kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(PACKET_1BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
expected_error = "Unable to read sequence number.";
- } else if (i < GetFecGroupOffset(PACKET_1BYTE_GUID, !kIncludeVersion)) {
+ } else if (i < GetFecGroupOffset(PACKET_1BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
expected_error = "Unable to read private flags.";
} else {
expected_error = "Unable to read first fec protected packet offset.";
@@ -762,14 +816,14 @@ TEST_P(QuicFramerTest, PacketHeader1ByteGuid) {
}
}
-TEST_P(QuicFramerTest, PacketHeaderWith0ByteGuid) {
- QuicFramerPeer::SetLastSerializedGuid(&framer_,
- GG_UINT64_C(0xFEDCBA9876543210));
+TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) {
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ &framer_, GG_UINT64_C(0xFEDCBA9876543210));
unsigned char packet[] = {
- // public flags (0 byte guid)
+ // public flags (0 byte connection_id)
0x30,
- // guid
+ // connection_id
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
@@ -782,7 +836,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteGuid) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -795,18 +849,20 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteGuid) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_0BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
- } else if (i < GetSequenceNumberOffset(PACKET_0BYTE_GUID,
+ } else if (i < GetSequenceNumberOffset(PACKET_0BYTE_CONNECTION_ID,
!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
- } else if (i < GetPrivateFlagsOffset(PACKET_0BYTE_GUID, !kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(PACKET_0BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
expected_error = "Unable to read sequence number.";
- } else if (i < GetFecGroupOffset(PACKET_0BYTE_GUID, !kIncludeVersion)) {
+ } else if (i < GetFecGroupOffset(PACKET_0BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
expected_error = "Unable to read private flags.";
} else {
expected_error = "Unable to read first fec protected packet offset.";
@@ -819,7 +875,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) {
unsigned char packet[] = {
// public flags (version)
0x3D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -836,7 +892,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_TRUE(visitor_.header_->public_header.version_flag);
EXPECT_EQ(GetParam(), visitor_.header_->public_header.versions[0]);
@@ -850,14 +906,14 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_8BYTE_GUID, kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < kVersionOffset) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else if (i < GetSequenceNumberOffset(kIncludeVersion)) {
expected_error = "Unable to read protocol version.";
} else if (i < GetPrivateFlagsOffset(kIncludeVersion)) {
@@ -876,9 +932,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) {
GG_UINT64_C(0x123456789ABA));
unsigned char packet[] = {
- // public flags (8 byte guid and 4 byte sequence number)
+ // public flags (8 byte connection_id and 4 byte sequence number)
0x2C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -892,7 +948,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -905,14 +961,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_4BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else if (i < GetPrivateFlagsOffset(!kIncludeVersion,
PACKET_4BYTE_SEQUENCE_NUMBER)) {
expected_error = "Unable to read sequence number.";
@@ -931,9 +987,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) {
GG_UINT64_C(0x123456789ABA));
unsigned char packet[] = {
- // public flags (8 byte guid and 2 byte sequence number)
+ // public flags (8 byte connection_id and 2 byte sequence number)
0x1C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -947,7 +1003,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -960,14 +1016,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_2BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else if (i < GetPrivateFlagsOffset(!kIncludeVersion,
PACKET_2BYTE_SEQUENCE_NUMBER)) {
expected_error = "Unable to read sequence number.";
@@ -986,9 +1042,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) {
GG_UINT64_C(0x123456789ABA));
unsigned char packet[] = {
- // public flags (8 byte guid and 1 byte sequence number)
+ // public flags (8 byte connection_id and 1 byte sequence number)
0x0C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1002,7 +1058,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) {
EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_FALSE(visitor_.header_->fec_flag);
@@ -1015,14 +1071,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) {
// Now test framing boundaries
for (size_t i = 0;
- i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
++i) {
string expected_error;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else if (i < GetPrivateFlagsOffset(!kIncludeVersion,
PACKET_1BYTE_SEQUENCE_NUMBER)) {
expected_error = "Unable to read sequence number.";
@@ -1038,9 +1094,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) {
TEST_P(QuicFramerTest, InvalidPublicFlag) {
unsigned char packet[] = {
- // public flags, unknown flag at bit 6
- 0x40,
- // guid
+ // public flags: all flags set but the public reset flag and version flag.
+ 0xFC,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1057,13 +1113,18 @@ TEST_P(QuicFramerTest, InvalidPublicFlag) {
arraysize(packet),
"Illegal public flags value.",
QUIC_INVALID_PACKET_HEADER);
+
+ // Now turn off validation.
+ framer_.set_validate_flags(false);
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
};
TEST_P(QuicFramerTest, InvalidPublicFlagWithMatchingVersions) {
unsigned char packet[] = {
- // public flags (8 byte guid and version flag and an unknown flag)
+ // public flags (8 byte connection_id and version flag and an unknown flag)
0x4D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -1086,9 +1147,9 @@ TEST_P(QuicFramerTest, InvalidPublicFlagWithMatchingVersions) {
TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) {
unsigned char packet[] = {
- // public flags (8 byte guid, version flag and an unknown flag)
+ // public flags (8 byte connection_id, version flag and an unknown flag)
0x7D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -1113,9 +1174,9 @@ TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) {
TEST_P(QuicFramerTest, InvalidPrivateFlag) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1136,9 +1197,9 @@ TEST_P(QuicFramerTest, InvalidPrivateFlag) {
TEST_P(QuicFramerTest, InvalidFECGroupOffset) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1158,9 +1219,9 @@ TEST_P(QuicFramerTest, InvalidFECGroupOffset) {
TEST_P(QuicFramerTest, PaddingFrame) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1198,16 +1259,16 @@ TEST_P(QuicFramerTest, PaddingFrame) {
// A packet with no frames is not acceptable.
CheckProcessingFails(
packet,
- GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
"Packet has no frames.", QUIC_MISSING_PAYLOAD);
}
TEST_P(QuicFramerTest, StreamFrame) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1253,9 +1314,9 @@ TEST_P(QuicFramerTest, StreamFrame) {
TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1302,9 +1363,9 @@ TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) {
TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1351,9 +1412,9 @@ TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) {
TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1400,9 +1461,9 @@ TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) {
TEST_P(QuicFramerTest, StreamFrameWithVersion) {
unsigned char packet[] = {
- // public flags (version, 8 byte guid)
+ // public flags (version, 8 byte connection_id)
0x3D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -1433,8 +1494,8 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) {
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
- EXPECT_TRUE(visitor_.header_.get()->public_header.version_flag);
- EXPECT_EQ(GetParam(), visitor_.header_.get()->public_header.versions[0]);
+ EXPECT_TRUE(visitor_.header_->public_header.version_flag);
+ EXPECT_EQ(GetParam(), visitor_.header_->public_header.versions[0]);
EXPECT_TRUE(CheckDecryption(encrypted, kIncludeVersion));
ASSERT_EQ(1u, visitor_.stream_frames_.size());
@@ -1454,9 +1515,9 @@ TEST_P(QuicFramerTest, RejectPacket) {
visitor_.accept_packet_ = false;
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1491,6 +1552,25 @@ TEST_P(QuicFramerTest, RejectPacket) {
EXPECT_EQ(0u, visitor_.ack_frames_.size());
}
+TEST_P(QuicFramerTest, RejectPublicHeader) {
+ visitor_.accept_public_header_ = false;
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.public_header_.get());
+ ASSERT_FALSE(visitor_.header_.get());
+}
+
TEST_P(QuicFramerTest, RevivedStreamFrame) {
unsigned char payload[] = {
// frame type (stream frame with fin)
@@ -1509,7 +1589,7 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) {
};
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = true;
@@ -1526,7 +1606,7 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) {
ASSERT_EQ(1, visitor_.revived_packets_);
ASSERT_TRUE(visitor_.header_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.header_->public_header.guid);
+ visitor_.header_->public_header.connection_id);
EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
EXPECT_FALSE(visitor_.header_->public_header.version_flag);
EXPECT_TRUE(visitor_.header_->fec_flag);
@@ -1549,9 +1629,9 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) {
TEST_P(QuicFramerTest, StreamFrameInFecGroup) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1586,8 +1666,10 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) {
EXPECT_EQ(IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
EXPECT_EQ(GG_UINT64_C(0x341256789ABA),
visitor_.header_->fec_group);
- const size_t fec_offset = GetStartOfFecProtectedData(
- PACKET_8BYTE_GUID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER);
+ const size_t fec_offset =
+ GetStartOfFecProtectedData(PACKET_8BYTE_CONNECTION_ID,
+ !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER);
EXPECT_EQ(
string(AsChars(packet) + fec_offset, arraysize(packet) - fec_offset),
visitor_.fec_protected_payload_);
@@ -1601,11 +1683,15 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) {
CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]);
}
-TEST_P(QuicFramerTest, AckFrame) {
+TEST_P(QuicFramerTest, AckFrame15) {
+ if (framer_.version() != QUIC_VERSION_15) {
+ return;
+ }
+
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1635,6 +1721,8 @@ TEST_P(QuicFramerTest, AckFrame) {
0x01,
// 0 more missing packets in range.
0x00,
+ // Number of revived packets.
+ 0x00,
};
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
@@ -1667,11 +1755,13 @@ TEST_P(QuicFramerTest, AckFrame) {
const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
kQuicDeltaTimeLargestObservedSize;
const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
- kNumberOfMissingPacketsSize;
+ kNumberOfNackRangesSize;
const size_t kMissingPacketsRange = kMissingPacketsOffset +
PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
// Now test framing boundaries
- const size_t ack_frame_size = kMissingPacketsRange +
+ const size_t ack_frame_size = kRevivedPacketsLength +
PACKET_1BYTE_SEQUENCE_NUMBER;
for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
string expected_error;
@@ -1689,22 +1779,396 @@ TEST_P(QuicFramerTest, AckFrame) {
expected_error = "Unable to read num missing packet ranges.";
} else if (i < kMissingPacketsRange) {
expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else {
+ expected_error = "Unable to read num revived packets.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, AckFrame) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame.received_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed);
+ ASSERT_EQ(1u, frame.received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.received_info.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+
+ const size_t kReceivedEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries
+ const size_t ack_frame_size = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else {
+ expected_error = "Unable to read num revived packets.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, AckFrameRevivedPackets) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x01,
+ // Revived packet sequence number.
+ 0xBE, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame.received_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed);
+ ASSERT_EQ(1u, frame.received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.received_info.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+
+ const size_t kReceivedEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketSequenceNumberLength = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries
+ const size_t ack_frame_size = kRevivedPacketSequenceNumberLength +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kReceivedEntropyOffset) {
+ expected_error = "Unable to read least unacked delta.";
+ } else if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else if (i < kRevivedPacketSequenceNumberLength) {
+ expected_error = "Unable to read num revived packets.";
} else {
+ expected_error = "Unable to read revived packet.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, AckFrameRevivedPackets15) {
+ if (framer_.version() != QUIC_VERSION_15) {
+ return;
+ }
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0xAB,
+ // least packet sequence number awaiting an ack, delta from sequence number.
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x01,
+ // Revived packet sequence number.
+ 0xBE, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xAB, frame.sent_info.entropy_hash);
+ EXPECT_EQ(0xBA, frame.received_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed);
+ ASSERT_EQ(1u, frame.received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.received_info.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked);
+
+ const size_t kSentEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize;
+ const size_t kReceivedEntropyOffset = kLeastUnackedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketSequenceNumberLength = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries
+ const size_t ack_frame_size = kRevivedPacketSequenceNumberLength +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kLeastUnackedOffset) {
+ expected_error = "Unable to read entropy hash for sent packets.";
+ } else if (i < kReceivedEntropyOffset) {
+ expected_error = "Unable to read least unacked delta.";
+ } else if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
expected_error = "Unable to read missing sequence number range.";
+ } else if (i < kRevivedPacketSequenceNumberLength) {
+ expected_error = "Unable to read num revived packets.";
+ } else {
+ expected_error = "Unable to read revived packet.";
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_ACK_DATA);
}
}
TEST_P(QuicFramerTest, AckFrameNoNacks) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (no nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x4C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame* frame = visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame->received_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF),
+ frame->received_info.largest_observed);
+ ASSERT_EQ(0u, frame->received_info.missing_packets.size());
+
+ // Verify that the packet re-serializes identically.
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, AckFrameNoNacks15) {
+ if (framer_.version() > QUIC_VERSION_15) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1750,8 +2214,7 @@ TEST_P(QuicFramerTest, AckFrameNoNacks) {
// Verify that the packet re-serializes identically.
QuicFrames frames;
frames.push_back(QuicFrame(frame));
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1760,10 +2223,87 @@ TEST_P(QuicFramerTest, AckFrameNoNacks) {
}
TEST_P(QuicFramerTest, AckFrame500Nacks) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges
+ 0x02,
+ // missing packet delta
+ 0x01,
+ // 243 more missing packets in range.
+ // The ranges are listed in this order so the re-constructed packet matches.
+ 0xF3,
+ // No gap between ranges
+ 0x00,
+ // 255 more missing packets in range.
+ 0xFF,
+ // No revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame* frame = visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame->received_info.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF),
+ frame->received_info.largest_observed);
+ EXPECT_EQ(0u, frame->received_info.revived_packets.size());
+ ASSERT_EQ(500u, frame->received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator first_missing_iter =
+ frame->received_info.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE) - 499, *first_missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ frame->received_info.missing_packets.rbegin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *last_missing_iter);
+
+ // Verify that the packet re-serializes identically.
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, AckFrame500Nacks15) {
+ if (framer_.version() != QUIC_VERSION_15) {
+ return;
+ }
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1798,6 +2338,8 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
0x00,
// 255 more missing packets in range.
0xFF,
+ // No revived packets.
+ 0x00,
};
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
@@ -1814,6 +2356,7 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
EXPECT_EQ(0xBA, frame->received_info.entropy_hash);
EXPECT_EQ(GG_UINT64_C(0x0123456789ABF),
frame->received_info.largest_observed);
+ EXPECT_EQ(0u, frame->received_info.revived_packets.size());
ASSERT_EQ(500u, frame->received_info.missing_packets.size());
SequenceNumberSet::const_iterator first_missing_iter =
frame->received_info.missing_packets.begin();
@@ -1826,8 +2369,7 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
// Verify that the packet re-serializes identically.
QuicFrames frames;
frames.push_back(QuicFrame(frame));
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -1837,9 +2379,9 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1852,8 +2394,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
0x20,
// congestion feedback type (tcp)
0x00,
- // ack_frame.feedback.tcp.accumulated_number_of_lost_packets
- 0x01, 0x02,
// ack_frame.feedback.tcp.receive_window
0x03, 0x04,
};
@@ -1870,23 +2410,19 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
const QuicCongestionFeedbackFrame& frame =
*visitor_.congestion_feedback_frames_[0];
ASSERT_EQ(kTCP, frame.type);
- EXPECT_EQ(0x0201,
- frame.tcp.accumulated_number_of_lost_packets);
EXPECT_EQ(0x4030u, frame.tcp.receive_window);
// Now test framing boundaries
- for (size_t i = kQuicFrameTypeSize; i < 6; ++i) {
+ for (size_t i = kQuicFrameTypeSize; i < 4; ++i) {
string expected_error;
if (i < 2) {
expected_error = "Unable to read congestion feedback type.";
} else if (i < 4) {
- expected_error = "Unable to read accumulated number of lost packets.";
- } else if (i < 6) {
expected_error = "Unable to read receive window.";
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
}
@@ -1894,9 +2430,9 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -1909,8 +2445,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
0x20,
// congestion feedback type (inter arrival)
0x01,
- // accumulated_number_of_lost_packets
- 0x02, 0x03,
// num received packets
0x03,
// lowest sequence number
@@ -1941,8 +2475,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
const QuicCongestionFeedbackFrame& frame =
*visitor_.congestion_feedback_frames_[0];
ASSERT_EQ(kInterArrival, frame.type);
- EXPECT_EQ(0x0302, frame.inter_arrival.
- accumulated_number_of_lost_packets);
ASSERT_EQ(3u, frame.inter_arrival.received_packet_times.size());
TimeMap::const_iterator iter =
frame.inter_arrival.received_packet_times.begin();
@@ -1959,30 +2491,28 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
iter->second.Subtract(start_).ToMicroseconds());
// Now test framing boundaries
- for (size_t i = kQuicFrameTypeSize; i < 31; ++i) {
+ for (size_t i = kQuicFrameTypeSize; i < 29; ++i) {
string expected_error;
if (i < 2) {
expected_error = "Unable to read congestion feedback type.";
- } else if (i < 4) {
- expected_error = "Unable to read accumulated number of lost packets.";
- } else if (i < 5) {
+ } else if (i < 3) {
expected_error = "Unable to read num received packets.";
- } else if (i < 11) {
+ } else if (i < 9) {
expected_error = "Unable to read smallest received.";
- } else if (i < 19) {
+ } else if (i < 17) {
expected_error = "Unable to read time received.";
- } else if (i < 21) {
+ } else if (i < 19) {
expected_error = "Unable to read sequence delta in received packets.";
- } else if (i < 25) {
+ } else if (i < 23) {
expected_error = "Unable to read time delta in received packets.";
- } else if (i < 27) {
+ } else if (i < 25) {
expected_error = "Unable to read sequence delta in received packets.";
- } else if (i < 31) {
+ } else if (i < 29) {
expected_error = "Unable to read time delta in received packets.";
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
}
@@ -1990,9 +2520,9 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
TEST_P(QuicFramerTest, CongestionFeedbackFrameFixRate) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2034,7 +2564,7 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameFixRate) {
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
}
@@ -2042,9 +2572,9 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameFixRate) {
TEST_P(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2065,11 +2595,68 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) {
EXPECT_EQ(QUIC_INVALID_CONGESTION_FEEDBACK_DATA, framer_.error());
}
-TEST_P(QuicFramerTest, RstStreamFrame) {
+TEST_P(QuicFramerTest, StopWaitingFrame) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x06,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0xAB,
+ // least packet sequence number awaiting an ack, delta from sequence number.
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.stop_waiting_frames_.size());
+ const QuicStopWaitingFrame& frame = *visitor_.stop_waiting_frames_[0];
+ EXPECT_EQ(0xAB, frame.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.least_unacked);
+
+ const size_t kSentEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize;
+ const size_t frame_size = 7;
+ for (size_t i = kQuicFrameTypeSize; i < frame_size; ++i) {
+ string expected_error;
+ if (i < kLeastUnackedOffset) {
+ expected_error = "Unable to read entropy hash for sent packets.";
+ } else {
+ expected_error = "Unable to read least unacked delta.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_STOP_WAITING_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, RstStreamFrameQuic) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2082,6 +2669,11 @@ TEST_P(QuicFramerTest, RstStreamFrame) {
0x01,
// stream id
0x04, 0x03, 0x02, 0x01,
+
+ // sent byte offset
+ 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08,
+
// error code
0x01, 0x00, 0x00, 0x00,
@@ -2104,21 +2696,27 @@ TEST_P(QuicFramerTest, RstStreamFrame) {
EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id);
EXPECT_EQ(0x01, visitor_.rst_stream_frame_.error_code);
EXPECT_EQ("because I can", visitor_.rst_stream_frame_.error_details);
+ EXPECT_EQ(GG_UINT64_C(0x0807060504030201),
+ visitor_.rst_stream_frame_.byte_offset);
// Now test framing boundaries
- for (size_t i = kQuicFrameTypeSize; i < 24; ++i) {
+ for (size_t i = kQuicFrameTypeSize;
+ i < QuicFramer::GetMinRstStreamFrameSize(version_); ++i) {
string expected_error;
if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) {
expected_error = "Unable to read stream_id.";
} else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize +
- kQuicErrorCodeSize) {
+ + kQuicMaxStreamOffsetSize) {
+ expected_error = "Unable to read rst stream sent byte offset.";
+ } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize +
+ + kQuicMaxStreamOffsetSize + kQuicErrorCodeSize) {
expected_error = "Unable to read rst stream error code.";
} else {
expected_error = "Unable to read rst stream error details.";
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_RST_STREAM_DATA);
}
@@ -2126,9 +2724,9 @@ TEST_P(QuicFramerTest, RstStreamFrame) {
TEST_P(QuicFramerTest, ConnectionCloseFrame) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2176,7 +2774,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) {
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_CONNECTION_CLOSE_DATA);
}
@@ -2184,9 +2782,9 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) {
TEST_P(QuicFramerTest, GoAwayFrame) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2237,25 +2835,164 @@ TEST_P(QuicFramerTest, GoAwayFrame) {
}
CheckProcessingFails(
packet,
- i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
expected_error, QUIC_INVALID_GOAWAY_DATA);
}
}
+TEST_P(QuicFramerTest, WindowUpdateFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (window update frame)
+ 0x04,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // byte offset
+ 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(GG_UINT64_C(0x01020304),
+ visitor_.window_update_frame_.stream_id);
+ EXPECT_EQ(GG_UINT64_C(0x0c0b0a0908070605),
+ visitor_.window_update_frame_.byte_offset);
+
+ // Now test framing boundaries
+ for (size_t i = kQuicFrameTypeSize;
+ i < QuicFramer::GetWindowUpdateFrameSize(); ++i) {
+ string expected_error;
+ if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) {
+ expected_error = "Unable to read stream_id.";
+ } else {
+ expected_error = "Unable to read window byte_offset.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_WINDOW_UPDATE_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, BlockedFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (blocked frame)
+ 0x05,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(GG_UINT64_C(0x01020304),
+ visitor_.blocked_frame_.stream_id);
+
+ // Now test framing boundaries
+ for (size_t i = kQuicFrameTypeSize; i < QuicFramer::GetBlockedFrameSize();
+ ++i) {
+ string expected_error = "Unable to read stream_id.";
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_BLOCKED_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, PingFrame) {
+ if (version_ <= QUIC_VERSION_17) {
+ return;
+ }
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (ping frame)
+ 0x07,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(1u, visitor_.ping_frames_.size());
+
+ // No need to check the PING frame boundaries because it has no payload.
+}
+
TEST_P(QuicFramerTest, PublicResetPacket) {
unsigned char packet[] = {
- // public flags (public reset, 8 byte guid)
- 0x3E,
- // guid
+ // public flags (public reset, 8 byte connection_id)
+ 0x0E,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
// nonce proof
0x89, 0x67, 0x45, 0x23,
0x01, 0xEF, 0xCD, 0xAB,
// rejected sequence number
0xBC, 0x9A, 0x78, 0x56,
- 0x34, 0x12,
+ 0x34, 0x12, 0x00, 0x00,
};
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
@@ -2263,32 +3000,136 @@ TEST_P(QuicFramerTest, PublicResetPacket) {
ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.public_reset_packet_.get());
EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
- visitor_.public_reset_packet_->public_header.guid);
+ visitor_.public_reset_packet_->public_header.connection_id);
EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag);
EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag);
EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789),
visitor_.public_reset_packet_->nonce_proof);
EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
visitor_.public_reset_packet_->rejected_sequence_number);
+ EXPECT_TRUE(
+ visitor_.public_reset_packet_->client_address.address().empty());
// Now test framing boundaries
- for (size_t i = 0; i < GetPublicResetPacketSize(); ++i) {
+ for (size_t i = 0; i < arraysize(packet); ++i) {
string expected_error;
- DLOG(INFO) << "iteration: " << i;
- if (i < kGuidOffset) {
+ DVLOG(1) << "iteration: " << i;
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
CheckProcessingFails(packet, i, expected_error,
QUIC_INVALID_PACKET_HEADER);
- } else if (i < kPublicResetPacketNonceProofOffset) {
- expected_error = "Unable to read GUID.";
+ } else if (i < kPublicResetPacketMessageTagOffset) {
+ expected_error = "Unable to read ConnectionId.";
CheckProcessingFails(packet, i, expected_error,
QUIC_INVALID_PACKET_HEADER);
- } else if (i < kPublicResetPacketRejectedSequenceNumberOffset) {
- expected_error = "Unable to read nonce proof.";
+ } else {
+ expected_error = "Unable to read reset message.";
CheckProcessingFails(packet, i, expected_error,
QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketWithTrailingJunk) {
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte connection_id)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // trailing junk
+ 'j', 'u', 'n', 'k',
+ };
+
+ string expected_error = "Unable to read reset message.";
+ CheckProcessingFails(packet, arraysize(packet), expected_error,
+ QUIC_INVALID_PUBLIC_RST_PACKET);
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketWithClientAddress) {
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte connection_id)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (3) + padding
+ 0x03, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // tag kCADR
+ 'C', 'A', 'D', 'R',
+ // end offset 24
+ 0x18, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // client address: 4.31.198.44:443
+ 0x02, 0x00,
+ 0x04, 0x1F, 0xC6, 0x2C,
+ 0xBB, 0x01,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.public_reset_packet_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.public_reset_packet_->public_header.connection_id);
+ EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag);
+ EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789),
+ visitor_.public_reset_packet_->nonce_proof);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.public_reset_packet_->rejected_sequence_number);
+ EXPECT_EQ("4.31.198.44",
+ IPAddressToString(visitor_.public_reset_packet_->
+ client_address.address()));
+ EXPECT_EQ(443, visitor_.public_reset_packet_->client_address.port());
+
+ // Now test framing boundaries
+ for (size_t i = 0; i < arraysize(packet); ++i) {
+ string expected_error;
+ DVLOG(1) << "iteration: " << i;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PACKET_HEADER);
+ } else if (i < kPublicResetPacketMessageTagOffset) {
+ expected_error = "Unable to read ConnectionId.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PACKET_HEADER);
} else {
- expected_error = "Unable to read rejected sequence number.";
+ expected_error = "Unable to read reset message.";
CheckProcessingFails(packet, i, expected_error,
QUIC_INVALID_PUBLIC_RST_PACKET);
}
@@ -2297,9 +3138,9 @@ TEST_P(QuicFramerTest, PublicResetPacket) {
TEST_P(QuicFramerTest, VersionNegotiationPacket) {
unsigned char packet[] = {
- // public flags (version, 8 byte guid)
+ // public flags (version, 8 byte connection_id)
0x3D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -2316,13 +3157,13 @@ TEST_P(QuicFramerTest, VersionNegotiationPacket) {
EXPECT_EQ(2u, visitor_.version_negotiation_packet_->versions.size());
EXPECT_EQ(GetParam(), visitor_.version_negotiation_packet_->versions[0]);
- for (size_t i = 0; i <= kPublicFlagsSize + PACKET_8BYTE_GUID; ++i) {
+ for (size_t i = 0; i <= kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID; ++i) {
string expected_error;
QuicErrorCode error_code = QUIC_INVALID_PACKET_HEADER;
- if (i < kGuidOffset) {
+ if (i < kConnectionIdOffset) {
expected_error = "Unable to read public flags.";
} else if (i < kVersionOffset) {
- expected_error = "Unable to read GUID.";
+ expected_error = "Unable to read ConnectionId.";
} else {
expected_error = "Unable to read supported version in negotiation.";
error_code = QUIC_INVALID_VERSION_NEGOTIATION_PACKET;
@@ -2333,9 +3174,9 @@ TEST_P(QuicFramerTest, VersionNegotiationPacket) {
TEST_P(QuicFramerTest, FecPacket) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2370,7 +3211,7 @@ TEST_P(QuicFramerTest, FecPacket) {
TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2384,9 +3225,9 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
frames.push_back(QuicFrame(&padding_frame));
unsigned char packet[kMaxPacketSize] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2401,12 +3242,11 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
};
uint64 header_size =
- GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2417,7 +3257,7 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2432,9 +3272,9 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) {
frames.push_back(QuicFrame(&padding_frame));
unsigned char packet[kMaxPacketSize] = {
- // public flags (8 byte guid and 4 byte sequence number)
+ // public flags (8 byte connection_id and 4 byte sequence number)
0x2C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2448,12 +3288,11 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) {
};
uint64 header_size =
- GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_4BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2464,7 +3303,7 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) {
TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2479,9 +3318,9 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) {
frames.push_back(QuicFrame(&padding_frame));
unsigned char packet[kMaxPacketSize] = {
- // public flags (8 byte guid and 2 byte sequence number)
+ // public flags (8 byte connection_id and 2 byte sequence number)
0x1C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2495,12 +3334,11 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) {
};
uint64 header_size =
- GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_2BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2511,7 +3349,7 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) {
TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2526,9 +3364,9 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) {
frames.push_back(QuicFrame(&padding_frame));
unsigned char packet[kMaxPacketSize] = {
- // public flags (8 byte guid and 1 byte sequence number)
+ // public flags (8 byte connection_id and 1 byte sequence number)
0x0C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2542,12 +3380,11 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) {
};
uint64 header_size =
- GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2558,7 +3395,7 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) {
TEST_P(QuicFramerTest, BuildStreamFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2576,9 +3413,9 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) {
frames.push_back(QuicFrame(&stream_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2600,8 +3437,62 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) {
'r', 'l', 'd', '!',
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildStreamFramePacketInFecGroup) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC);
+ header.is_in_fec_group = IN_FEC_GROUP;
+ header.fec_group = GG_UINT64_C(0x77123456789ABC);
+
+ QuicStreamFrame stream_frame;
+ stream_frame.stream_id = 0x01020304;
+ stream_frame.fin = true;
+ stream_frame.offset = GG_UINT64_C(0xBA98FEDC32107654);
+ stream_frame.data = MakeIOVector("hello world!");
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&stream_frame));
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy, is_in_fec_group)
+ 0x03,
+ // FEC group
+ 0x00,
+ // frame type (stream frame with fin and data length field)
+ 0xFF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length (since packet is in an FEC group)
+ 0x0C, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2611,7 +3502,7 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) {
TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = true;
header.fec_flag = false;
@@ -2629,9 +3520,9 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) {
frames.push_back(QuicFrame(&stream_frame));
unsigned char packet[] = {
- // public flags (version, 8 byte guid)
+ // public flags (version, 8 byte connection_id)
0x3D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -2656,8 +3547,7 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) {
};
QuicFramerPeer::SetIsServer(&framer_, false);
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2667,14 +3557,14 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) {
TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) {
QuicPacketPublicHeader header;
- header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.reset_flag = false;
header.version_flag = true;
unsigned char packet[] = {
- // public flags (version, 8 byte guid)
- 0x3D,
- // guid
+ // public flags (version, 8 byte connection_id)
+ 0x0D,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -2692,8 +3582,74 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) {
}
TEST_P(QuicFramerTest, BuildAckFramePacket) {
+ if (version_ <= QUIC_VERSION_15) {
+ return;
+ }
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ ack_frame.received_info.entropy_hash = 0x43;
+ ack_frame.received_info.largest_observed = GG_UINT64_C(0x770123456789ABF);
+ ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ ack_frame.received_info.missing_packets.insert(
+ GG_UINT64_C(0x770123456789ABE));
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0x43,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildAckFramePacket15) {
+ if (version_ != QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2714,9 +3670,9 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) {
frames.push_back(QuicFrame(&ack_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2746,10 +3702,128 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) {
0x01,
// 0 more missing packets in range.
0x00,
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+// TODO(jri): Add test for tuncated packets in which the original ack frame had
+// revived packets. (In both the large and small packet cases below).
+TEST_P(QuicFramerTest, BuildTruncatedAckFrameLargePacket) {
+ if (version_ <= QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ // This entropy hash is different from what shows up in the packet below,
+ // since entropy is recomputed by the framer on ack truncation (by
+ // TestEntropyCalculator for this test.)
+ ack_frame.received_info.entropy_hash = 0x43;
+ ack_frame.received_info.largest_observed = 2 * 300;
+ ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ for (size_t i = 1; i < 2 * 300; i += 2) {
+ ack_frame.received_info.missing_packets.insert(i);
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, is truncated, 2 byte largest observed, 1 byte delta)
+ 0x74,
+ // entropy hash of all received packets, set to 1 by TestEntropyCalculator
+ // since ack is truncated.
+ 0x01,
+ // 2-byte largest observed packet sequence number.
+ // Expected to be 510 (0x1FE), since only 255 nack ranges can fit.
+ 0xFE, 0x01,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges (limited to 255 by size of this field).
+ 0xFF,
+ // {missing packet delta, further missing packets in range}
+ // 6 nack ranges x 42 + 3 nack ranges
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ // 0 revived packets.
+ 0x00,
};
scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ framer_.BuildDataPacket(header, frames, kMaxPacketSize).packet);
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2757,9 +3831,79 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) {
AsChars(packet), arraysize(packet));
}
+
+TEST_P(QuicFramerTest, BuildTruncatedAckFrameSmallPacket) {
+ if (version_ <= QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ // This entropy hash is different from what shows up in the packet below,
+ // since entropy is recomputed by the framer on ack truncation (by
+ // TestEntropyCalculator for this test.)
+ ack_frame.received_info.entropy_hash = 0x43;
+ ack_frame.received_info.largest_observed = 2 * 300;
+ ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ for (size_t i = 1; i < 2 * 300; i += 2) {
+ ack_frame.received_info.missing_packets.insert(i);
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, is truncated, 2 byte largest observed, 1 byte delta)
+ 0x74,
+ // entropy hash of all received packets, set to 1 by TestEntropyCalculator
+ // since ack is truncated.
+ 0x01,
+ // 2-byte largest observed packet sequence number.
+ // Expected to be 12 (0x0C), since only 6 nack ranges can fit.
+ 0x0C, 0x00,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges (limited to 6 by packet size of 37).
+ 0x06,
+ // {missing packet delta, further missing packets in range}
+ // 6 nack ranges
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(
+ framer_.BuildDataPacket(header, frames, 37u).packet);
+ ASSERT_TRUE(data != NULL);
+ // Expect 1 byte unused since at least 2 bytes are needed to fit more nacks.
+ EXPECT_EQ(36u, data->length());
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2769,16 +3913,15 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
QuicCongestionFeedbackFrame congestion_feedback_frame;
congestion_feedback_frame.type = kTCP;
- congestion_feedback_frame.tcp.accumulated_number_of_lost_packets = 0x0201;
congestion_feedback_frame.tcp.receive_window = 0x4030;
QuicFrames frames;
frames.push_back(QuicFrame(&congestion_feedback_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2791,14 +3934,11 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
0x20,
// congestion feedback type (TCP)
0x00,
- // accumulated number of lost packets
- 0x01, 0x02,
// TCP receive window
0x03, 0x04,
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2808,7 +3948,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2818,7 +3958,6 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
QuicCongestionFeedbackFrame frame;
frame.type = kInterArrival;
- frame.inter_arrival.accumulated_number_of_lost_packets = 0x0302;
frame.inter_arrival.received_packet_times.insert(
make_pair(GG_UINT64_C(0x0123456789ABA),
start_.Add(QuicTime::Delta::FromMicroseconds(
@@ -2835,9 +3974,9 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
frames.push_back(QuicFrame(&frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2850,8 +3989,6 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
0x20,
// congestion feedback type (inter arrival)
0x01,
- // accumulated_number_of_lost_packets
- 0x02, 0x03,
// num received packets
0x03,
// lowest sequence number
@@ -2870,8 +4007,56 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
0x02, 0x00, 0x00, 0x00,
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildStopWaitingPacket) {
+ if (version_ <= QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicStopWaitingFrame stop_waiting_frame;
+ stop_waiting_frame.entropy_hash = 0x14;
+ stop_waiting_frame.least_unacked = GG_UINT64_C(0x770123456789AA0);
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&stop_waiting_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (stop waiting frame)
+ 0x06,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0x14,
+ // least packet sequence number awaiting an ack, delta from sequence number.
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2881,7 +4066,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2898,9 +4083,9 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) {
frames.push_back(QuicFrame(&congestion_feedback_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2917,8 +4102,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) {
0x01, 0x02, 0x03, 0x04,
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -2928,7 +4112,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) {
TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2943,14 +4127,16 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) {
QuicFrames frames;
frames.push_back(QuicFrame(&congestion_feedback_frame));
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data;
+ EXPECT_DFATAL(
+ data.reset(BuildDataPacket(header, frames)),
+ "AppendCongestionFeedbackFrame failed");
ASSERT_TRUE(data == NULL);
}
-TEST_P(QuicFramerTest, BuildRstFramePacket) {
+TEST_P(QuicFramerTest, BuildRstFramePacketQuic) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -2962,11 +4148,12 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) {
rst_frame.stream_id = 0x01020304;
rst_frame.error_code = static_cast<QuicRstStreamErrorCode>(0x05060708);
rst_frame.error_details = "because I can";
+ rst_frame.byte_offset = 0x0807060504030201;
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -2979,6 +4166,9 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) {
0x01,
// stream id
0x04, 0x03, 0x02, 0x01,
+ // sent byte offset
+ 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08,
// error code
0x08, 0x07, 0x06, 0x05,
// error details length
@@ -2993,8 +4183,7 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) {
QuicFrames frames;
frames.push_back(QuicFrame(&rst_frame));
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -3004,7 +4193,7 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) {
TEST_P(QuicFramerTest, BuildCloseFramePacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -3020,9 +4209,9 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) {
frames.push_back(QuicFrame(&close_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3044,8 +4233,7 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) {
'n',
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -3055,7 +4243,7 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) {
TEST_P(QuicFramerTest, BuildGoAwayPacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -3072,9 +4260,9 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) {
frames.push_back(QuicFrame(&goaway_frame));
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3098,8 +4286,7 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) {
'n',
};
- scoped_ptr<QuicPacket> data(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
ASSERT_TRUE(data != NULL);
test::CompareCharArraysWithHexError("constructed packet",
@@ -3107,25 +4294,224 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) {
AsChars(packet), arraysize(packet));
}
+TEST_P(QuicFramerTest, BuildWindowUpdatePacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicWindowUpdateFrame window_update_frame;
+ window_update_frame.stream_id = 0x01020304;
+ window_update_frame.byte_offset = 0x1122334455667788;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&window_update_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags(entropy)
+ 0x01,
+
+ // frame type (window update frame)
+ 0x04,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // byte offset
+ 0x88, 0x77, 0x66, 0x55,
+ 0x44, 0x33, 0x22, 0x11,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildBlockedPacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicBlockedFrame blocked_frame;
+ blocked_frame.stream_id = 0x01020304;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&blocked_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags(entropy)
+ 0x01,
+
+ // frame type (blocked frame)
+ 0x05,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPingPacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicPingFrame ping_frame;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ping_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags(entropy)
+ 0x01,
+
+ // frame type (ping frame)
+ 0x07,
+ };
+
+ if (version_ > QUIC_VERSION_17) {
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+ } else {
+ string expected_error =
+ "Attempt to add a PingFrame in " + QuicVersionToString(version_);
+ EXPECT_DFATAL(BuildDataPacket(header, frames),
+ expected_error);
+ return;
+ }
+}
+
TEST_P(QuicFramerTest, BuildPublicResetPacket) {
QuicPublicResetPacket reset_packet;
- reset_packet.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ reset_packet.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
reset_packet.public_header.reset_flag = true;
reset_packet.public_header.version_flag = false;
reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC);
reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789);
unsigned char packet[] = {
- // public flags (public reset, 8 byte GUID)
- 0x3E,
- // guid
+ // public flags (public reset, 8 byte ConnectionId)
+ 0x0E,
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
// nonce proof
0x89, 0x67, 0x45, 0x23,
0x01, 0xEF, 0xCD, 0xAB,
// rejected sequence number
0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ };
+
+ scoped_ptr<QuicEncryptedPacket> data(
+ framer_.BuildPublicResetPacket(reset_packet));
+ ASSERT_TRUE(data != NULL);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPublicResetPacketWithClientAddress) {
+ QuicPublicResetPacket reset_packet;
+ reset_packet.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ reset_packet.public_header.reset_flag = true;
+ reset_packet.public_header.version_flag = false;
+ reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC);
+ reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789);
+ reset_packet.client_address = IPEndPoint(Loopback4(), 0x1234);
+
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte ConnectionId)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (3) + padding
+ 0x03, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // tag kCADR
+ 'C', 'A', 'D', 'R',
+ // end offset 24
+ 0x18, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // client address
+ 0x02, 0x00,
+ 0x7F, 0x00, 0x00, 0x01,
0x34, 0x12,
};
@@ -3140,7 +4526,7 @@ TEST_P(QuicFramerTest, BuildPublicResetPacket) {
TEST_P(QuicFramerTest, BuildFecPacket) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = true;
@@ -3154,9 +4540,9 @@ TEST_P(QuicFramerTest, BuildFecPacket) {
fec_data.redundancy = "abcdefghijklmnop";
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3186,9 +4572,9 @@ TEST_P(QuicFramerTest, BuildFecPacket) {
TEST_P(QuicFramerTest, EncryptPacket) {
QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC);
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3208,7 +4594,7 @@ TEST_P(QuicFramerTest, EncryptPacket) {
scoped_ptr<QuicPacket> raw(
QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false,
- PACKET_8BYTE_GUID, !kIncludeVersion,
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER));
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw));
@@ -3220,9 +4606,9 @@ TEST_P(QuicFramerTest, EncryptPacket) {
TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) {
QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC);
unsigned char packet[] = {
- // public flags (version, 8 byte guid)
+ // public flags (version, 8 byte connection_id)
0x3D,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// version tag
@@ -3244,7 +4630,7 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) {
scoped_ptr<QuicPacket> raw(
QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false,
- PACKET_8BYTE_GUID, kIncludeVersion,
+ PACKET_8BYTE_CONNECTION_ID, kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER));
scoped_ptr<QuicEncryptedPacket> encrypted(
framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw));
@@ -3253,31 +4639,98 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) {
EXPECT_TRUE(CheckEncryption(sequence_number, raw.get()));
}
-// TODO(rch): re-enable after https://codereview.chromium.org/11820005/
-// lands. Currently this is causing valgrind problems, but it should be
-// fixed in the followup CL.
-TEST_P(QuicFramerTest, DISABLED_CalculateLargestReceived) {
- SequenceNumberSet missing;
- missing.insert(1);
- missing.insert(5);
- missing.insert(7);
+TEST_P(QuicFramerTest, AckTruncationLargePacket) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ // Create a packet with just the ack.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(300, 0u);
+ QuicFrame frame;
+ frame.type = ACK_FRAME;
+ frame.ack_frame = &ack_frame;
+ QuicFrames frames;
+ frames.push_back(frame);
+
+ // Build an ack packet with truncation due to limit in number of nack ranges.
+ scoped_ptr<QuicPacket> raw_ack_packet(
+ framer_.BuildDataPacket(header, frames, kMaxPacketSize).packet);
+ ASSERT_TRUE(raw_ack_packet != NULL);
+ scoped_ptr<QuicEncryptedPacket> ack_packet(
+ framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
+ *raw_ack_packet));
+ // Now make sure we can turn our ack packet back into an ack frame.
+ ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
+ EXPECT_TRUE(processed_ack_frame.received_info.is_truncated);
+ EXPECT_EQ(510u, processed_ack_frame.received_info.largest_observed);
+ ASSERT_EQ(255u, processed_ack_frame.received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ processed_ack_frame.received_info.missing_packets.begin();
+ EXPECT_EQ(1u, *missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ processed_ack_frame.received_info.missing_packets.rbegin();
+ EXPECT_EQ(509u, *last_missing_iter);
+}
+
+TEST_P(QuicFramerTest, AckTruncationSmallPacket) {
+ if (framer_.version() <= QUIC_VERSION_15) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
- // These two we just walk to the next gap, and return the largest seen.
- EXPECT_EQ(4u, QuicFramer::CalculateLargestObserved(missing, missing.find(1)));
- EXPECT_EQ(6u, QuicFramer::CalculateLargestObserved(missing, missing.find(5)));
+ // Create a packet with just the ack.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(300, 0u);
+ QuicFrame frame;
+ frame.type = ACK_FRAME;
+ frame.ack_frame = &ack_frame;
+ QuicFrames frames;
+ frames.push_back(frame);
- missing.insert(2);
- // For 1, we can't go forward as 2 would be implicitly acked so we return the
- // largest missing packet.
- EXPECT_EQ(1u, QuicFramer::CalculateLargestObserved(missing, missing.find(1)));
- // For 2, we've seen 3 and 4, so can admit to a largest observed.
- EXPECT_EQ(4u, QuicFramer::CalculateLargestObserved(missing, missing.find(2)));
+ // Build an ack packet with truncation due to limit in number of nack ranges.
+ scoped_ptr<QuicPacket> raw_ack_packet(
+ framer_.BuildDataPacket(header, frames, 500).packet);
+ ASSERT_TRUE(raw_ack_packet != NULL);
+ scoped_ptr<QuicEncryptedPacket> ack_packet(
+ framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
+ *raw_ack_packet));
+ // Now make sure we can turn our ack packet back into an ack frame.
+ ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
+ EXPECT_TRUE(processed_ack_frame.received_info.is_truncated);
+ EXPECT_EQ(476u, processed_ack_frame.received_info.largest_observed);
+ ASSERT_EQ(238u, processed_ack_frame.received_info.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ processed_ack_frame.received_info.missing_packets.begin();
+ EXPECT_EQ(1u, *missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ processed_ack_frame.received_info.missing_packets.rbegin();
+ EXPECT_EQ(475u, *last_missing_iter);
}
-// TODO(rch) enable after landing the revised truncation CL.
-TEST_P(QuicFramerTest, DISABLED_Truncation) {
+TEST_P(QuicFramerTest, Truncation15) {
+ if (framer_.version() > QUIC_VERSION_15) {
+ return;
+ }
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -3287,31 +4740,31 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) {
QuicAckFrame ack_frame;
ack_frame.received_info.largest_observed = 601;
- ack_frame.sent_info.least_unacked = 0;
+ ack_frame.sent_info.least_unacked = header.packet_sequence_number - 1;
for (uint64 i = 1; i < ack_frame.received_info.largest_observed; i += 2) {
ack_frame.received_info.missing_packets.insert(i);
}
- // Create a packet with just the ack
+ // Create a packet with just the ack.
QuicFrame frame;
frame.type = ACK_FRAME;
frame.ack_frame = &ack_frame;
QuicFrames frames;
frames.push_back(frame);
- scoped_ptr<QuicPacket> raw_ack_packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames));
ASSERT_TRUE(raw_ack_packet != NULL);
scoped_ptr<QuicEncryptedPacket> ack_packet(
framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
*raw_ack_packet));
- // Now make sure we can turn our ack packet back into an ack frame
+ // Now make sure we can turn our ack packet back into an ack frame.
ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
ASSERT_EQ(1u, visitor_.ack_frames_.size());
const QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
- EXPECT_EQ(0u, processed_ack_frame.sent_info.least_unacked);
+ EXPECT_EQ(header.packet_sequence_number - 1,
+ processed_ack_frame.sent_info.least_unacked);
EXPECT_TRUE(processed_ack_frame.received_info.is_truncated);
EXPECT_EQ(510u, processed_ack_frame.received_info.largest_observed);
ASSERT_EQ(255u, processed_ack_frame.received_info.missing_packets.size());
@@ -3325,7 +4778,7 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) {
TEST_P(QuicFramerTest, CleanTruncation) {
QuicPacketHeader header;
- header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
header.public_header.reset_flag = false;
header.public_header.version_flag = false;
header.fec_flag = false;
@@ -3335,27 +4788,26 @@ TEST_P(QuicFramerTest, CleanTruncation) {
QuicAckFrame ack_frame;
ack_frame.received_info.largest_observed = 201;
- ack_frame.sent_info.least_unacked = 0;
+ ack_frame.sent_info.least_unacked = header.packet_sequence_number - 2;
for (uint64 i = 1; i < ack_frame.received_info.largest_observed; ++i) {
ack_frame.received_info.missing_packets.insert(i);
}
- // Create a packet with just the ack
+ // Create a packet with just the ack.
QuicFrame frame;
frame.type = ACK_FRAME;
frame.ack_frame = &ack_frame;
QuicFrames frames;
frames.push_back(frame);
- scoped_ptr<QuicPacket> raw_ack_packet(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ scoped_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames));
ASSERT_TRUE(raw_ack_packet != NULL);
scoped_ptr<QuicEncryptedPacket> ack_packet(
framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
*raw_ack_packet));
- // Now make sure we can turn our ack packet back into an ack frame
+ // Now make sure we can turn our ack packet back into an ack frame.
ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
// Test for clean truncation of the ack by comparing the length of the
@@ -3366,8 +4818,7 @@ TEST_P(QuicFramerTest, CleanTruncation) {
frames.push_back(frame);
size_t original_raw_length = raw_ack_packet->length();
- raw_ack_packet.reset(
- framer_.BuildUnsizedDataPacket(header, frames).packet);
+ raw_ack_packet.reset(BuildDataPacket(header, frames));
ASSERT_TRUE(raw_ack_packet != NULL);
EXPECT_EQ(original_raw_length, raw_ack_packet->length());
ASSERT_TRUE(raw_ack_packet != NULL);
@@ -3375,9 +4826,9 @@ TEST_P(QuicFramerTest, CleanTruncation) {
TEST_P(QuicFramerTest, EntropyFlagTest) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3410,9 +4861,9 @@ TEST_P(QuicFramerTest, EntropyFlagTest) {
TEST_P(QuicFramerTest, FecEntropyTest) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3447,9 +4898,9 @@ TEST_P(QuicFramerTest, FecEntropyTest) {
TEST_P(QuicFramerTest, StopPacketProcessing) {
unsigned char packet[] = {
- // public flags (8 byte guid)
+ // public flags (8 byte connection_id)
0x3C,
- // guid
+ // connection_id
0x10, 0x32, 0x54, 0x76,
0x98, 0xBA, 0xDC, 0xFE,
// packet sequence number
@@ -3498,6 +4949,7 @@ TEST_P(QuicFramerTest, StopPacketProcessing) {
EXPECT_CALL(visitor, OnStreamFrame(_)).WillOnce(Return(false));
EXPECT_CALL(visitor, OnAckFrame(_)).Times(0);
EXPECT_CALL(visitor, OnPacketComplete());
+ EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)).WillOnce(Return(true));
QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
EXPECT_TRUE(framer_.ProcessPacket(encrypted));
diff --git a/chromium/net/quic/quic_headers_stream.cc b/chromium/net/quic/quic_headers_stream.cc
new file mode 100644
index 00000000000..c158e085055
--- /dev/null
+++ b/chromium/net/quic/quic_headers_stream.cc
@@ -0,0 +1,266 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_headers_stream.h"
+
+#include "net/quic/quic_session.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const QuicStreamId kInvalidStreamId = 0;
+
+} // namespace
+
+// A SpdyFramer visitor which passed SYN_STREAM and SYN_REPLY frames to
+// the QuicDataStream, and closes the connection if any unexpected frames
+// are received.
+class QuicHeadersStream::SpdyFramerVisitor
+ : public SpdyFramerVisitorInterface,
+ public SpdyFramerDebugVisitorInterface {
+ public:
+ explicit SpdyFramerVisitor(QuicHeadersStream* stream) : stream_(stream) {}
+
+ // SpdyFramerVisitorInterface implementation
+ virtual void OnSynStream(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ bool fin,
+ bool unidirectional) OVERRIDE {
+ if (!stream_->IsConnected()) {
+ return;
+ }
+
+ if (associated_stream_id != 0) {
+ CloseConnection("associated_stream_id != 0");
+ return;
+ }
+
+ if (unidirectional != 0) {
+ CloseConnection("unidirectional != 0");
+ return;
+ }
+
+ stream_->OnSynStream(stream_id, priority, fin);
+ }
+
+ virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {
+ if (!stream_->IsConnected()) {
+ return;
+ }
+
+ stream_->OnSynReply(stream_id, fin);
+ }
+
+ virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len) OVERRIDE {
+ if (!stream_->IsConnected()) {
+ return false;
+ }
+ stream_->OnControlFrameHeaderData(stream_id, header_data, len);
+ return true;
+ }
+
+ virtual void OnStreamFrameData(SpdyStreamId stream_id,
+ const char* data,
+ size_t len,
+ bool fin) OVERRIDE {
+ if (fin && len == 0) {
+ // The framer invokes OnStreamFrameData with zero-length data and
+ // fin = true after processing a SYN_STREAM or SYN_REPLY frame
+ // that had the fin bit set.
+ return;
+ }
+ CloseConnection("SPDY DATA frame received.");
+ }
+
+ virtual void OnError(SpdyFramer* framer) OVERRIDE {
+ CloseConnection("SPDY framing error.");
+ }
+
+ virtual void OnDataFrameHeader(SpdyStreamId stream_id,
+ size_t length,
+ bool fin) OVERRIDE {
+ CloseConnection("SPDY DATA frame received.");
+ }
+
+ virtual void OnRstStream(SpdyStreamId stream_id,
+ SpdyRstStreamStatus status) OVERRIDE {
+ CloseConnection("SPDY RST_STREAM frame received.");
+ }
+
+ virtual void OnSetting(SpdySettingsIds id,
+ uint8 flags,
+ uint32 value) OVERRIDE {
+ CloseConnection("SPDY SETTINGS frame received.");
+ }
+
+ virtual void OnSettingsAck() OVERRIDE {
+ CloseConnection("SPDY SETTINGS frame received.");
+ }
+
+ virtual void OnSettingsEnd() OVERRIDE {
+ CloseConnection("SPDY SETTINGS frame received.");
+ }
+
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {
+ CloseConnection("SPDY PING frame received.");
+ }
+
+ virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
+ SpdyGoAwayStatus status) OVERRIDE {
+ CloseConnection("SPDY GOAWAY frame received.");
+ }
+
+ virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) OVERRIDE {
+ CloseConnection("SPDY HEADERS frame received.");
+ }
+
+ virtual void OnWindowUpdate(SpdyStreamId stream_id,
+ uint32 delta_window_size) OVERRIDE {
+ CloseConnection("SPDY WINDOW_UPDATE frame received.");
+ }
+
+ virtual void OnPushPromise(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ bool end) OVERRIDE {
+ LOG(DFATAL) << "PUSH_PROMISE frame received from a SPDY/3 framer";
+ CloseConnection("SPDY PUSH_PROMISE frame received.");
+ }
+
+ virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE {
+ CloseConnection("SPDY CONTINUATION frame received.");
+ }
+
+ // SpdyFramerDebugVisitorInterface implementation
+ virtual void OnSendCompressedFrame(SpdyStreamId stream_id,
+ SpdyFrameType type,
+ size_t payload_len,
+ size_t frame_len) OVERRIDE {}
+
+ virtual void OnReceiveCompressedFrame(SpdyStreamId stream_id,
+ SpdyFrameType type,
+ size_t frame_len) OVERRIDE {
+ if (stream_->IsConnected()) {
+ stream_->OnCompressedFrameSize(frame_len);
+ }
+ }
+
+ private:
+ void CloseConnection(const string& details) {
+ if (stream_->IsConnected()) {
+ stream_->CloseConnectionWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA, details);
+ }
+ }
+
+ private:
+ QuicHeadersStream* stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(SpdyFramerVisitor);
+};
+
+QuicHeadersStream::QuicHeadersStream(QuicSession* session)
+ : ReliableQuicStream(kHeadersStreamId, session),
+ stream_id_(kInvalidStreamId),
+ fin_(false),
+ frame_len_(0),
+ spdy_framer_(SPDY3),
+ spdy_framer_visitor_(new SpdyFramerVisitor(this)) {
+ spdy_framer_.set_visitor(spdy_framer_visitor_.get());
+ spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
+ // TODO(jri): Set headers to be always FEC protected.
+ DisableFlowControl();
+}
+
+QuicHeadersStream::~QuicHeadersStream() {}
+
+size_t QuicHeadersStream::WriteHeaders(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ scoped_ptr<SpdySerializedFrame> frame;
+ if (session()->is_server()) {
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.set_name_value_block(headers);
+ syn_reply.set_fin(fin);
+ frame.reset(spdy_framer_.SerializeFrame(syn_reply));
+ } else {
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_name_value_block(headers);
+ syn_stream.set_fin(fin);
+ frame.reset(spdy_framer_.SerializeFrame(syn_stream));
+ }
+ WriteOrBufferData(StringPiece(frame->data(), frame->size()), false,
+ ack_notifier_delegate);
+ return frame->size();
+}
+
+uint32 QuicHeadersStream::ProcessRawData(const char* data,
+ uint32 data_len) {
+ return spdy_framer_.ProcessInput(data, data_len);
+}
+
+QuicPriority QuicHeadersStream::EffectivePriority() const { return 0; }
+
+void QuicHeadersStream::OnSynStream(SpdyStreamId stream_id,
+ SpdyPriority priority,
+ bool fin) {
+ if (!session()->is_server()) {
+ CloseConnectionWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY SYN_STREAM frame received at the client");
+ return;
+ }
+ DCHECK_EQ(kInvalidStreamId, stream_id_);
+ stream_id_ = stream_id;
+ fin_ = fin;
+ session()->OnStreamHeadersPriority(stream_id, priority);
+}
+
+void QuicHeadersStream::OnSynReply(SpdyStreamId stream_id, bool fin) {
+ if (session()->is_server()) {
+ CloseConnectionWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY SYN_REPLY frame received at the server");
+ return;
+ }
+ DCHECK_EQ(kInvalidStreamId, stream_id_);
+ stream_id_ = stream_id;
+ fin_ = fin;
+}
+
+void QuicHeadersStream::OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len) {
+ DCHECK_EQ(stream_id_, stream_id);
+ if (len == 0) {
+ DCHECK_NE(0u, stream_id_);
+ DCHECK_NE(0u, frame_len_);
+ session()->OnStreamHeadersComplete(stream_id_, fin_, frame_len_);
+ // Reset state for the next frame.
+ stream_id_ = kInvalidStreamId;
+ fin_ = false;
+ frame_len_ = 0;
+ } else {
+ session()->OnStreamHeaders(stream_id_, StringPiece(header_data, len));
+ }
+}
+
+void QuicHeadersStream::OnCompressedFrameSize(size_t frame_len) {
+ DCHECK_EQ(kInvalidStreamId, stream_id_);
+ DCHECK_EQ(0u, frame_len_);
+ frame_len_ = frame_len;
+}
+
+bool QuicHeadersStream::IsConnected() {
+ return session()->connection()->connected();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_headers_stream.h b/chromium/net/quic/quic_headers_stream.h
new file mode 100644
index 00000000000..c3ccbdada40
--- /dev/null
+++ b/chromium/net/quic/quic_headers_stream.h
@@ -0,0 +1,82 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_HEADERS_STREAM_H_
+#define NET_QUIC_QUIC_HEADERS_STREAM_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/spdy/spdy_framer.h"
+
+namespace net {
+
+// Headers in QUIC are sent as SPDY SYN_STREAM or SYN_REPLY frames
+// over a reserved reliable stream with the id 2. Each endpoint (client
+// and server) will allocate an instance of QuicHeadersStream to send
+// and receive headers.
+class NET_EXPORT_PRIVATE QuicHeadersStream : public ReliableQuicStream {
+ public:
+ explicit QuicHeadersStream(QuicSession* session);
+ virtual ~QuicHeadersStream();
+
+ // Writes |headers| for |stream_id| in a SYN_STREAM or SYN_REPLY
+ // frame to the peer. If |fin| is true, the fin flag will be set on
+ // the SPDY frame. Returns the size, in bytes, of the resulting
+ // SPDY frame.
+ size_t WriteHeaders(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
+ // ReliableQuicStream implementation
+ virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE;
+ virtual QuicPriority EffectivePriority() const OVERRIDE;
+
+ private:
+ class SpdyFramerVisitor;
+
+ // The following methods are called by the SimpleVisitor.
+
+ // Called when a SYN_STREAM frame has been received.
+ void OnSynStream(SpdyStreamId stream_id,
+ SpdyPriority priority,
+ bool fin);
+
+ // Called when a SYN_REPLY frame been received.
+ void OnSynReply(SpdyStreamId stream_id, bool fin);
+
+ // Called when a chunk of header data is available. This is called
+ // after OnSynStream, or OnSynReply.
+ // |stream_id| The stream receiving the header data.
+ // |header_data| A buffer containing the header data chunk received.
+ // |len| The length of the header data buffer. A length of zero indicates
+ // that the header data block has been completely sent.
+ void OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len);
+
+ // Called when the size of the compressed frame payload is available.
+ void OnCompressedFrameSize(size_t frame_len);
+
+ // Returns true if the session is still connected.
+ bool IsConnected();
+
+ // Data about the stream whose headers are being processed.
+ QuicStreamId stream_id_;
+ bool fin_;
+ size_t frame_len_;
+
+ SpdyFramer spdy_framer_;
+ scoped_ptr<SpdyFramerVisitor> spdy_framer_visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicHeadersStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_HEADERS_STREAM_H_
diff --git a/chromium/net/quic/quic_headers_stream_test.cc b/chromium/net/quic/quic_headers_stream_test.cc
new file mode 100644
index 00000000000..0aa71088102
--- /dev/null
+++ b/chromium/net/quic/quic_headers_stream_test.cc
@@ -0,0 +1,334 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_headers_stream.h"
+
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/spdy/spdy_protocol.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+using testing::Invoke;
+using testing::StrictMock;
+using testing::WithArgs;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+class MockVisitor : public SpdyFramerVisitorInterface {
+ public:
+ MOCK_METHOD1(OnError, void(SpdyFramer* framer));
+ MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId stream_id,
+ size_t length,
+ bool fin));
+ MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId stream_id,
+ const char* data,
+ size_t len,
+ bool fin));
+ MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len));
+ MOCK_METHOD5(OnSynStream, void(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ bool fin,
+ bool unidirectional));
+ MOCK_METHOD2(OnSynReply, void(SpdyStreamId stream_id, bool fin));
+ MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id,
+ SpdyRstStreamStatus status));
+ MOCK_METHOD1(OnSettings, void(bool clear_persisted));
+ MOCK_METHOD3(OnSetting, void(SpdySettingsIds id, uint8 flags, uint32 value));
+ MOCK_METHOD0(OnSettingsAck, void());
+ MOCK_METHOD0(OnSettingsEnd, void());
+ MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
+ MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id,
+ SpdyGoAwayStatus status));
+ MOCK_METHOD3(OnHeaders, void(SpdyStreamId stream_id, bool fin, bool end));
+ MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id,
+ uint32 delta_window_size));
+ MOCK_METHOD2(OnCredentialFrameData, bool(const char* credential_data,
+ size_t len));
+ MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id));
+ MOCK_METHOD3(OnPushPromise, void(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ bool end));
+ MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end));
+ MOCK_METHOD6(OnAltSvc, void(SpdyStreamId stream_id,
+ uint32 max_age,
+ uint16 port,
+ StringPiece protocol_id,
+ StringPiece host,
+ StringPiece origin));
+};
+
+class QuicHeadersStreamTest : public ::testing::TestWithParam<bool> {
+ public:
+ static QuicVersionVector GetVersions() {
+ QuicVersionVector versions;
+ versions.push_back(QuicVersionMax());
+ return versions;
+ }
+
+ QuicHeadersStreamTest()
+ : connection_(new StrictMock<MockConnection>(is_server(), GetVersions())),
+ session_(connection_),
+ headers_stream_(QuicSessionPeer::GetHeadersStream(&session_)),
+ body_("hello world"),
+ framer_(SPDY3) {
+ headers_[":version"] = "HTTP/1.1";
+ headers_[":status"] = "200 Ok";
+ headers_["content-length"] = "11";
+ framer_.set_visitor(&visitor_);
+ EXPECT_EQ(QuicVersionMax(), session_.connection()->version());
+ EXPECT_TRUE(headers_stream_ != NULL);
+ }
+
+ QuicConsumedData SaveIov(const IOVector& data) {
+ const iovec* iov = data.iovec();
+ int count = data.Capacity();
+ for (int i = 0 ; i < count; ++i) {
+ saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len);
+ }
+ return QuicConsumedData(saved_data_.length(), false);
+ }
+
+ bool SaveHeaderData(const char* data, int len) {
+ saved_header_data_.append(data, len);
+ return true;
+ }
+
+ void SaveHeaderDataStringPiece(StringPiece data) {
+ saved_header_data_.append(data.data(), data.length());
+ }
+
+ void WriteHeadersAndExpectSynStream(QuicStreamId stream_id,
+ bool fin,
+ QuicPriority priority) {
+ WriteHeadersAndCheckData(stream_id, fin, priority, SYN_STREAM);
+ }
+
+ void WriteHeadersAndExpectSynReply(QuicStreamId stream_id,
+ bool fin) {
+ WriteHeadersAndCheckData(stream_id, fin, 0, SYN_REPLY);
+ }
+
+ void WriteHeadersAndCheckData(QuicStreamId stream_id,
+ bool fin,
+ QuicPriority priority,
+ SpdyFrameType type) {
+ // Write the headers and capture the outgoing data
+ EXPECT_CALL(session_, WritevData(kHeadersStreamId, _, _, false, _, NULL))
+ .WillOnce(WithArgs<1>(Invoke(this, &QuicHeadersStreamTest::SaveIov)));
+ headers_stream_->WriteHeaders(stream_id, headers_, fin, NULL);
+
+ // Parse the outgoing data and check that it matches was was written.
+ if (type == SYN_STREAM) {
+ EXPECT_CALL(visitor_, OnSynStream(stream_id, kNoAssociatedStream, 0,
+ // priority,
+ fin, kNotUnidirectional));
+ } else {
+ EXPECT_CALL(visitor_, OnSynReply(stream_id, fin));
+ }
+ EXPECT_CALL(visitor_, OnControlFrameHeaderData(stream_id, _, _))
+ .WillRepeatedly(WithArgs<1, 2>(
+ Invoke(this, &QuicHeadersStreamTest::SaveHeaderData)));
+ if (fin) {
+ EXPECT_CALL(visitor_, OnStreamFrameData(stream_id, NULL, 0, true));
+ }
+ framer_.ProcessInput(saved_data_.data(), saved_data_.length());
+ EXPECT_FALSE(framer_.HasError()) << framer_.error_code();
+
+ CheckHeaders();
+ saved_data_.clear();
+ }
+
+ void CheckHeaders() {
+ SpdyHeaderBlock headers;
+ EXPECT_EQ(saved_header_data_.length(),
+ framer_.ParseHeaderBlockInBuffer(saved_header_data_.data(),
+ saved_header_data_.length(),
+ &headers));
+ EXPECT_EQ(headers_, headers);
+ saved_header_data_.clear();
+ }
+
+ bool is_server() {
+ return GetParam();
+ }
+
+ void CloseConnection() {
+ QuicConnectionPeer::CloseConnection(connection_);
+ }
+
+ static const bool kNotUnidirectional = false;
+ static const bool kNoAssociatedStream = false;
+
+ StrictMock<MockConnection>* connection_;
+ StrictMock<MockSession> session_;
+ QuicHeadersStream* headers_stream_;
+ SpdyHeaderBlock headers_;
+ string body_;
+ string saved_data_;
+ string saved_header_data_;
+ SpdyFramer framer_;
+ StrictMock<MockVisitor> visitor_;
+};
+
+INSTANTIATE_TEST_CASE_P(Tests, QuicHeadersStreamTest, testing::Bool());
+
+TEST_P(QuicHeadersStreamTest, StreamId) {
+ EXPECT_EQ(3u, headers_stream_->id());
+}
+
+TEST_P(QuicHeadersStreamTest, EffectivePriority) {
+ EXPECT_EQ(0u, headers_stream_->EffectivePriority());
+}
+
+TEST_P(QuicHeadersStreamTest, WriteHeaders) {
+ for (QuicStreamId stream_id = kClientDataStreamId1;
+ stream_id < kClientDataStreamId3; stream_id += 2) {
+ for (int count = 0; count < 2; ++count) {
+ bool fin = (count == 0);
+ if (is_server()) {
+ WriteHeadersAndExpectSynReply(stream_id, fin);
+ } else {
+ for (QuicPriority priority = 0; priority < 7; ++priority) {
+ WriteHeadersAndExpectSynStream(stream_id, fin, priority);
+ }
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessRawData) {
+ for (QuicStreamId stream_id = kClientDataStreamId1;
+ stream_id < kClientDataStreamId3; stream_id += 2) {
+ for (int count = 0; count < 2; ++count) {
+ bool fin = (count == 0);
+ for (QuicPriority priority = 0; priority < 7; ++priority) {
+ // Replace with "WriteHeadersAndSaveData"
+ scoped_ptr<SpdySerializedFrame> frame;
+ if (is_server()) {
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_name_value_block(headers_);
+ syn_stream.set_fin(fin);
+ frame.reset(framer_.SerializeSynStream(syn_stream));
+ EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0));
+ } else {
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.set_name_value_block(headers_);
+ syn_reply.set_fin(fin);
+ frame.reset(framer_.SerializeSynReply(syn_reply));
+ }
+ EXPECT_CALL(session_, OnStreamHeaders(stream_id, _))
+ .WillRepeatedly(WithArgs<1>(
+ Invoke(this,
+ &QuicHeadersStreamTest::SaveHeaderDataStringPiece)));
+ EXPECT_CALL(session_,
+ OnStreamHeadersComplete(stream_id, fin, frame->size()));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+
+ CheckHeaders();
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) {
+ SpdyDataIR data(2, "");
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY DATA frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyRstStreamFrame) {
+ SpdyRstStreamIR data(2, RST_STREAM_PROTOCOL_ERROR, "");
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY RST_STREAM frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdySettingsFrame) {
+ SpdySettingsIR data;
+ data.AddSetting(SETTINGS_UPLOAD_BANDWIDTH, true, true, 0);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY SETTINGS frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) {
+ SpdyPingIR data(1);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY PING frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) {
+ SpdyGoAwayIR data(1, GOAWAY_PROTOCOL_ERROR, "go away");
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY GOAWAY frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyHeadersFrame) {
+ SpdyHeadersIR data(1);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY HEADERS frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) {
+ SpdyWindowUpdateIR data(1, 1);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY WINDOW_UPDATE frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, NoFlowControl) {
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+ EXPECT_FALSE(headers_stream_->flow_controller()->IsEnabled());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_http_stream.cc b/chromium/net/quic/quic_http_stream.cc
index f8b2e6517dc..20b60deb064 100644
--- a/chromium/net/quic/quic_http_stream.cc
+++ b/chromium/net/quic/quic_http_stream.cc
@@ -5,6 +5,7 @@
#include "net/quic/quic_http_stream.h"
#include "base/callback_helpers.h"
+#include "base/metrics/histogram.h"
#include "base/strings/stringprintf.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -37,6 +38,7 @@ QuicHttpStream::QuicHttpStream(const base::WeakPtr<QuicClientSession>& session)
response_status_(OK),
response_headers_received_(false),
read_buf_(new GrowableIOBuffer()),
+ closed_stream_received_bytes_(0),
user_buffer_len_(0),
weak_factory_(this) {
DCHECK(session_);
@@ -60,13 +62,16 @@ int QuicHttpStream::InitializeStream(const HttpRequestInfo* request_info,
if (request_info->url.SchemeIsSecure()) {
SSLInfo ssl_info;
- if (!session_->GetSSLInfo(&ssl_info) || !ssl_info.cert) {
+ bool secure_session = session_->GetSSLInfo(&ssl_info) && ssl_info.cert;
+ UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.SecureResourceSecureSession",
+ secure_session);
+ if (!secure_session)
return ERR_REQUEST_FOR_SECURE_RESOURCE_OVER_INSECURE_QUIC;
- }
}
stream_net_log_ = stream_net_log;
request_info_ = request_info;
+ request_time_ = base::Time::Now();
priority_ = priority;
int rv = stream_request_.StartRequest(
@@ -117,12 +122,9 @@ int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
// was being called even if we didn't yet allocate raw_request_body_buf_.
// && (request_body_stream_->size() ||
// request_body_stream_->is_chunked()))
- //
- // Use kMaxPacketSize as the buffer size, since the request
- // body data is written with this size at a time.
- // TODO(rch): use a smarter value since we can't write an entire
- // packet due to overhead.
- raw_request_body_buf_ = new IOBufferWithSize(kMaxPacketSize);
+ // Use 10 packets as the body buffer size to give enough space to
+ // help ensure we don't often send out partial packets.
+ raw_request_body_buf_ = new IOBufferWithSize(10 * kMaxPacketSize);
// The request body buffer is empty at first.
request_body_buf_ = new DrainableIOBuffer(raw_request_body_buf_.get(), 0);
}
@@ -162,10 +164,6 @@ int QuicHttpStream::ReadResponseHeaders(const CompletionCallback& callback) {
return ERR_IO_PENDING;
}
-const HttpResponseInfo* QuicHttpStream::GetResponseInfo() const {
- return response_info_;
-}
-
int QuicHttpStream::ReadResponseBody(
IOBuffer* buf, int buf_len, const CompletionCallback& callback) {
CHECK(buf);
@@ -213,6 +211,7 @@ int QuicHttpStream::ReadResponseBody(
void QuicHttpStream::Close(bool not_reusable) {
// Note: the not_reusable flag has no meaning for SPDY streams.
if (stream_) {
+ closed_stream_received_bytes_ = stream_->stream_bytes_read();
stream_->SetDelegate(NULL);
stream_->Reset(QUIC_STREAM_CANCELLED);
stream_ = NULL;
@@ -246,8 +245,11 @@ bool QuicHttpStream::IsConnectionReusable() const {
}
int64 QuicHttpStream::GetTotalReceivedBytes() const {
- // TODO(eustas): Implement.
- return 0;
+ if (stream_) {
+ return stream_->stream_bytes_read();
+ }
+
+ return closed_stream_received_bytes_;
}
bool QuicHttpStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
@@ -327,6 +329,7 @@ void QuicHttpStream::OnClose(QuicErrorCode error) {
response_status_ = ERR_ABORTED;
}
+ closed_stream_received_bytes_ = stream_->stream_bytes_read();
stream_ = NULL;
if (!callback_.is_null())
DoCallback(response_status_);
@@ -413,32 +416,23 @@ int QuicHttpStream::DoSendHeaders() {
if (!stream_)
return ERR_UNEXPECTED;
- if (request_.empty() && !stream_->CanWrite(
- base::Bind(&QuicHttpStream::OnIOComplete,
- weak_factory_.GetWeakPtr()))) {
- // Do not compress headers unless it is likely that they can be sent.
- next_state_ = STATE_SEND_HEADERS;
- return ERR_IO_PENDING;
- }
- request_ = stream_->compressor()->CompressHeadersWithPriority(
- ConvertRequestPriorityToQuicPriority(priority_), request_headers_);
-
// Log the actual request with the URL Request's net log.
stream_net_log_.AddEvent(
- NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS,
- base::Bind(&SpdyHeaderBlockNetLogCallback, &request_headers_));
+ NetLog::TYPE_HTTP_TRANSACTION_QUIC_SEND_REQUEST_HEADERS,
+ base::Bind(&QuicRequestNetLogCallback, stream_->id(), &request_headers_,
+ priority_));
// Also log to the QuicSession's net log.
stream_->net_log().AddEvent(
NetLog::TYPE_QUIC_HTTP_STREAM_SEND_REQUEST_HEADERS,
- base::Bind(&SpdyHeaderBlockNetLogCallback, &request_headers_));
- request_headers_.clear();
+ base::Bind(&QuicRequestNetLogCallback, stream_->id(), &request_headers_,
+ priority_));
bool has_upload_data = request_body_stream_ != NULL;
next_state_ = STATE_SEND_HEADERS_COMPLETE;
- return stream_->WriteStreamData(
- request_, !has_upload_data,
- base::Bind(&QuicHttpStream::OnIOComplete, weak_factory_.GetWeakPtr()));
+ int rv = stream_->WriteHeaders(request_headers_, !has_upload_data, NULL);
+ request_headers_.clear();
+ return rv;
}
int QuicHttpStream::DoSendHeadersComplete(int rv) {
@@ -546,6 +540,8 @@ int QuicHttpStream::ParseResponseHeaders() {
.Init(*request_info_, *response_info_->headers.get());
response_info_->was_npn_negotiated = true;
response_info_->npn_negotiated_protocol = "quic/1+spdy/3";
+ response_info_->response_time = base::Time::Now();
+ response_info_->request_time = request_time_;
response_headers_received_ = true;
return OK;
diff --git a/chromium/net/quic/quic_http_stream.h b/chromium/net/quic/quic_http_stream.h
index 7a96ea3d206..5c608226315 100644
--- a/chromium/net/quic/quic_http_stream.h
+++ b/chromium/net/quic/quic_http_stream.h
@@ -41,7 +41,6 @@ class NET_EXPORT_PRIVATE QuicHttpStream :
const CompletionCallback& callback) OVERRIDE;
virtual UploadProgress GetUploadProgress() const OVERRIDE;
virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE;
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE;
virtual int ReadResponseBody(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) OVERRIDE;
@@ -119,6 +118,8 @@ class NET_EXPORT_PRIVATE QuicHttpStream :
const HttpRequestInfo* request_info_;
// The request body to send, if any, owned by the caller.
UploadDataStream* request_body_stream_;
+ // Time the request was issued.
+ base::Time request_time_;
// The priority of the request.
RequestPriority priority_;
// |response_info_| is the HTTP response data object which is filled in
@@ -145,6 +146,9 @@ class NET_EXPORT_PRIVATE QuicHttpStream :
// TODO(rch): This is infinite buffering, which is bad.
std::list<scoped_refptr<IOBufferWithSize> > response_body_;
+ // Number of bytes received when the stream was closed.
+ int64 closed_stream_received_bytes_;
+
// The caller's callback to be used for asynchronous operations.
CompletionCallback callback_;
@@ -160,6 +164,8 @@ class NET_EXPORT_PRIVATE QuicHttpStream :
BoundNetLog stream_net_log_;
base::WeakPtrFactory<QuicHttpStream> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicHttpStream);
};
} // namespace net
diff --git a/chromium/net/quic/quic_http_stream_test.cc b/chromium/net/quic/quic_http_stream_test.cc
index 97ae97261be..c0643d4458d 100644
--- a/chromium/net/quic/quic_http_stream_test.cc
+++ b/chromium/net/quic/quic_http_stream_test.cc
@@ -16,17 +16,20 @@
#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_server_info.h"
#include "net/quic/quic_client_session.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_connection_helper.h"
#include "net/quic/quic_default_packet_writer.h"
#include "net/quic/quic_http_utils.h"
#include "net/quic/quic_reliable_client_stream.h"
+#include "net/quic/quic_write_blocked_list.h"
#include "net/quic/spdy_utils.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
#include "net/quic/test_tools/mock_random.h"
#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_test_packet_maker.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/test_task_runner.h"
#include "net/socket/socket_test_util.h"
@@ -34,7 +37,6 @@
#include "net/spdy/spdy_framer.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_protocol.h"
-#include "net/spdy/write_blocked_list.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -47,15 +49,18 @@ namespace test {
namespace {
const char kUploadData[] = "hello world!";
+const char kServerHostname[] = "www.google.com";
+const uint16 kServerPort = 80;
class TestQuicConnection : public QuicConnection {
public:
- TestQuicConnection(QuicGuid guid,
+ TestQuicConnection(const QuicVersionVector& versions,
+ QuicConnectionId connection_id,
IPEndPoint address,
QuicConnectionHelper* helper,
QuicPacketWriter* writer)
- : QuicConnection(guid, address, helper, writer, false,
- QuicSupportedVersions()) {
+ : QuicConnection(connection_id, address, helper, writer, false,
+ versions) {
}
void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) {
@@ -69,27 +74,13 @@ class TestQuicConnection : public QuicConnection {
class TestReceiveAlgorithm : public ReceiveAlgorithmInterface {
public:
- explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback)
- : feedback_(feedback) {
+ virtual bool GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* /*congestion_feedback*/) {
+ return false;
}
- bool GenerateCongestionFeedback(
- QuicCongestionFeedbackFrame* congestion_feedback) {
- if (feedback_ == NULL) {
- return false;
- }
- *congestion_feedback = *feedback_;
- return true;
- }
-
- MOCK_METHOD4(RecordIncomingPacket,
- void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool));
-
- private:
- MockClock clock_;
- QuicCongestionFeedbackFrame* feedback_;
-
- DISALLOW_COPY_AND_ASSIGN(TestReceiveAlgorithm);
+ MOCK_METHOD3(RecordIncomingPacket,
+ void(QuicByteCount, QuicPacketSequenceNumber, QuicTime));
};
// Subclass of QuicHttpStream that closes itself when the first piece of data
@@ -116,9 +107,12 @@ class QuicHttpStreamPeer {
}
};
-class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
+class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> {
protected:
- const static bool kFin = true;
+ static const bool kFin = true;
+ static const bool kIncludeVersion = true;
+ static const bool kIncludeCongestionFeedback = true;
+
// Holds a packet to be written to the wire, and the IO mode that should
// be used by the mock socket when performing the write.
struct PacketToWrite {
@@ -134,10 +128,10 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
: net_log_(BoundNetLog()),
use_closing_stream_(false),
read_buffer_(new IOBufferWithSize(4096)),
- guid_(2),
- framer_(QuicSupportedVersions(), QuicTime::Zero(), false),
- random_generator_(0),
- creator_(guid_, &framer_, &random_generator_, false) {
+ connection_id_(2),
+ stream_id_(kClientDataStreamId1),
+ maker_(GetParam(), connection_id_),
+ random_generator_(0) {
IPAddressNumber ip;
CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip));
peer_addr_ = IPEndPoint(ip, 443);
@@ -152,8 +146,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
}
// Adds a packet to the list of expected writes.
- void AddWrite(IoMode mode, QuicEncryptedPacket* packet) {
- writes_.push_back(PacketToWrite(mode, packet));
+ void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) {
+ writes_.push_back(PacketToWrite(SYNCHRONOUS, packet.release()));
}
// Returns the packet to be written at position |pos|.
@@ -165,8 +159,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
return socket_data_->at_read_eof() && socket_data_->at_write_eof();
}
- void ProcessPacket(const QuicEncryptedPacket& packet) {
- connection_->ProcessUdpPacket(self_addr_, peer_addr_, packet);
+ void ProcessPacket(scoped_ptr<QuicEncryptedPacket> packet) {
+ connection_->ProcessUdpPacket(self_addr_, peer_addr_, *packet);
}
// Configures the test fixture to use the list of expected writes.
@@ -186,25 +180,26 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
socket->Connect(peer_addr_);
runner_ = new TestTaskRunner(&clock_);
send_algorithm_ = new MockSendAlgorithm();
- receive_algorithm_ = new TestReceiveAlgorithm(NULL);
- EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)).
+ receive_algorithm_ = new TestReceiveAlgorithm();
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _)).
Times(AnyNumber());
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
+ OnPacketSent(_, _, _, _, _)).WillRepeatedly(Return(true));
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize));
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).
WillRepeatedly(Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillRepeatedly(
- Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly(
Return(QuicBandwidth::Zero()));
EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber());
helper_.reset(new QuicConnectionHelper(runner_.get(), &clock_,
&random_generator_));
writer_.reset(new QuicDefaultPacketWriter(socket));
- connection_ = new TestQuicConnection(guid_, peer_addr_, helper_.get(),
- writer_.get());
+ connection_ = new TestQuicConnection(SupportedVersions(GetParam()),
+ connection_id_, peer_addr_,
+ helper_.get(), writer_.get());
connection_->set_visitor(&visitor_);
connection_->SetSendAlgorithm(send_algorithm_);
connection_->SetReceiveAlgorithm(receive_algorithm_);
@@ -214,89 +209,76 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
scoped_ptr<DatagramClientSocket>(socket),
writer_.Pass(), NULL,
&crypto_client_stream_factory_,
- "www.google.com", DefaultQuicConfig(),
- &crypto_config_, NULL));
+ make_scoped_ptr((QuicServerInfo*)NULL),
+ QuicServerId(kServerHostname, kServerPort,
+ false, PRIVACY_MODE_DISABLED),
+ DefaultQuicConfig(), &crypto_config_,
+ base::MessageLoop::current()->
+ message_loop_proxy().get(),
+ NULL));
session_->GetCryptoStream()->CryptoConnect();
EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed());
stream_.reset(use_closing_stream_ ?
new AutoClosingStream(session_->GetWeakPtr()) :
new QuicHttpStream(session_->GetWeakPtr()));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
}
- void SetRequestString(const std::string& method,
- const std::string& path,
- RequestPriority priority) {
- SpdyHeaderBlock headers;
- headers[":method"] = method;
- headers[":host"] = "www.google.com";
- headers[":path"] = path;
- headers[":scheme"] = "http";
- headers[":version"] = "HTTP/1.1";
- request_data_ = SerializeHeaderBlock(headers, true, priority);
- }
-
- void SetResponseString(const std::string& status, const std::string& body) {
- SpdyHeaderBlock headers;
- headers[":status"] = status;
- headers[":version"] = "HTTP/1.1";
- headers["content-type"] = "text/plain";
- response_data_ = SerializeHeaderBlock(headers, false, DEFAULT_PRIORITY) +
- body;
+ void SetRequest(const std::string& method,
+ const std::string& path,
+ RequestPriority priority) {
+ request_headers_ = maker_.GetRequestHeaders(method, "http", path);
}
- std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers,
- bool write_priority,
- RequestPriority priority) {
- QuicSpdyCompressor compressor;
- if (write_priority) {
- return compressor.CompressHeadersWithPriority(
- ConvertRequestPriorityToQuicPriority(priority), headers);
- }
- return compressor.CompressHeaders(headers);
+ void SetResponse(const std::string& status, const std::string& body) {
+ response_headers_ = maker_.GetResponseHeaders(status);
+ response_data_ = body;
}
- // Returns a newly created packet to send kData on stream 3.
- QuicEncryptedPacket* ConstructDataPacket(
+ scoped_ptr<QuicEncryptedPacket> ConstructDataPacket(
QuicPacketSequenceNumber sequence_number,
bool should_include_version,
bool fin,
QuicStreamOffset offset,
base::StringPiece data) {
- InitializeHeader(sequence_number, should_include_version);
- QuicStreamFrame frame(3, fin, offset, MakeIOVector(data));
- return ConstructPacket(header_, QuicFrame(&frame));
+ return maker_.MakeDataPacket(
+ sequence_number, stream_id_, should_include_version, fin, offset, data);
}
- // Returns a newly created packet to RST_STREAM stream 3.
- QuicEncryptedPacket* ConstructRstStreamPacket(
- QuicPacketSequenceNumber sequence_number) {
- InitializeHeader(sequence_number, false);
- QuicRstStreamFrame frame(3, QUIC_STREAM_CANCELLED);
- return ConstructPacket(header_, QuicFrame(&frame));
+ scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ bool fin) {
+ return maker_.MakeRequestHeadersPacket(
+ sequence_number, stream_id_, kIncludeVersion, fin, request_headers_);
}
- // Returns a newly created packet to send ack data.
- QuicEncryptedPacket* ConstructAckPacket(
+ scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
QuicPacketSequenceNumber sequence_number,
- QuicPacketSequenceNumber largest_received,
- QuicPacketSequenceNumber least_unacked) {
- InitializeHeader(sequence_number, false);
+ bool fin) {
+ return maker_.MakeResponseHeadersPacket(
+ sequence_number, stream_id_, !kIncludeVersion, fin, response_headers_);
+ }
- QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked);
- ack.sent_info.entropy_hash = 0;
- ack.received_info.entropy_hash = 0;
+ scoped_ptr<QuicEncryptedPacket> ConstructRstStreamPacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return maker_.MakeRstPacket(
+ sequence_number, true, stream_id_,
+ AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING, GetParam()));
+ }
- return ConstructPacket(header_, QuicFrame(&ack));
+ scoped_ptr<QuicEncryptedPacket> ConstructAckAndRstStreamPacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return maker_.MakeAckAndRstPacket(
+ sequence_number, !kIncludeVersion, stream_id_, QUIC_STREAM_CANCELLED,
+ 2, 1, !kIncludeCongestionFeedback);
}
- // Returns a newly created packet to send ack data.
- QuicEncryptedPacket* ConstructRstPacket(
+ scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
QuicPacketSequenceNumber sequence_number,
- QuicStreamId stream_id) {
- InitializeHeader(sequence_number, false);
-
- QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR);
- return ConstructPacket(header_, QuicFrame(&rst));
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked) {
+ return maker_.MakeAckPacket(sequence_number, largest_received,
+ least_unacked, !kIncludeCongestionFeedback);
}
BoundNetLog net_log_;
@@ -318,63 +300,44 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
HttpRequestHeaders headers_;
HttpResponseInfo response_;
scoped_refptr<IOBufferWithSize> read_buffer_;
+ SpdyHeaderBlock request_headers_;
+ SpdyHeaderBlock response_headers_;
std::string request_data_;
std::string response_data_;
private:
- void InitializeHeader(QuicPacketSequenceNumber sequence_number,
- bool should_include_version) {
- header_.public_header.guid = guid_;
- header_.public_header.reset_flag = false;
- header_.public_header.version_flag = should_include_version;
- header_.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header_.packet_sequence_number = sequence_number;
- header_.fec_group = 0;
- header_.entropy_flag = false;
- header_.fec_flag = false;
- }
-
- QuicEncryptedPacket* ConstructPacket(const QuicPacketHeader& header,
- const QuicFrame& frame) {
- QuicFrames frames;
- frames.push_back(frame);
- scoped_ptr<QuicPacket> packet(
- framer_.BuildUnsizedDataPacket(header_, frames).packet);
- return framer_.EncryptPacket(
- ENCRYPTION_NONE, header.packet_sequence_number, *packet);
- }
-
- const QuicGuid guid_;
- QuicFramer framer_;
+ const QuicConnectionId connection_id_;
+ const QuicStreamId stream_id_;
+ QuicTestPacketMaker maker_;
IPEndPoint self_addr_;
IPEndPoint peer_addr_;
MockRandom random_generator_;
MockCryptoClientStreamFactory crypto_client_stream_factory_;
- QuicPacketCreator creator_;
- QuicPacketHeader header_;
scoped_ptr<StaticSocketDataProvider> socket_data_;
std::vector<PacketToWrite> writes_;
};
-TEST_F(QuicHttpStreamTest, RenewStreamForAuth) {
+INSTANTIATE_TEST_CASE_P(Version, QuicHttpStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicHttpStreamTest, RenewStreamForAuth) {
Initialize();
EXPECT_EQ(NULL, stream_->RenewStreamForAuth());
}
-TEST_F(QuicHttpStreamTest, CanFindEndOfResponse) {
+TEST_P(QuicHttpStreamTest, CanFindEndOfResponse) {
Initialize();
EXPECT_TRUE(stream_->CanFindEndOfResponse());
}
-TEST_F(QuicHttpStreamTest, IsConnectionReusable) {
+TEST_P(QuicHttpStreamTest, IsConnectionReusable) {
Initialize();
EXPECT_FALSE(stream_->IsConnectionReusable());
}
-TEST_F(QuicHttpStreamTest, GetRequest) {
- SetRequestString("GET", "/", DEFAULT_PRIORITY);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0,
- request_data_));
+TEST_P(QuicHttpStreamTest, GetRequest) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
Initialize();
request_.method = "GET";
@@ -384,26 +347,23 @@ TEST_F(QuicHttpStreamTest, GetRequest) {
net_log_, callback_.callback()));
EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
// Ack the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
EXPECT_EQ(ERR_IO_PENDING,
stream_->ReadResponseHeaders(callback_.callback()));
- // Send the response without a body.
- SetResponseString("404 Not Found", std::string());
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(2, false, kFin, 0, response_data_));
- ProcessPacket(*resp);
+ SetResponse("404 Not Found", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, kFin));
// Now that the headers have been processed, the callback will return.
EXPECT_EQ(OK, callback_.WaitForResult());
ASSERT_TRUE(response_.headers.get());
EXPECT_EQ(404, response_.headers->response_code());
EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+ EXPECT_FALSE(response_.response_time.is_null());
+ EXPECT_FALSE(response_.request_time.is_null());
// There is no body, so this should return immediately.
EXPECT_EQ(0, stream_->ReadResponseBody(read_buffer_.get(),
@@ -414,10 +374,9 @@ TEST_F(QuicHttpStreamTest, GetRequest) {
}
// Regression test for http://crbug.com/288128
-TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) {
- SetRequestString("GET", "/", DEFAULT_PRIORITY);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0,
- request_data_));
+TEST_P(QuicHttpStreamTest, GetRequestLargeResponse) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
Initialize();
request_.method = "GET";
@@ -427,11 +386,9 @@ TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) {
net_log_, callback_.callback()));
EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
// Ack the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
EXPECT_EQ(ERR_IO_PENDING,
stream_->ReadResponseHeaders(callback_.callback()));
@@ -461,55 +418,11 @@ TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) {
EXPECT_TRUE(AtEof());
}
-TEST_F(QuicHttpStreamTest, GetRequestFullResponseInSinglePacket) {
- SetRequestString("GET", "/", DEFAULT_PRIORITY);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
- Initialize();
-
- request_.method = "GET";
- request_.url = GURL("http://www.google.com/");
-
- EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
- net_log_, callback_.callback()));
- EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
- callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
-
- // Ack the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
-
- EXPECT_EQ(ERR_IO_PENDING,
- stream_->ReadResponseHeaders(callback_.callback()));
-
- // Send the response with a body.
- SetResponseString("200 OK", "hello world!");
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(2, false, kFin, 0, response_data_));
- ProcessPacket(*resp);
-
- // Now that the headers have been processed, the callback will return.
- EXPECT_EQ(OK, callback_.WaitForResult());
- ASSERT_TRUE(response_.headers.get());
- EXPECT_EQ(200, response_.headers->response_code());
- EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
-
- // There is no body, so this should return immediately.
- // Since the body has already arrived, this should return immediately.
- EXPECT_EQ(12, stream_->ReadResponseBody(read_buffer_.get(),
- read_buffer_->size(),
- callback_.callback()));
- EXPECT_TRUE(stream_->IsResponseBodyComplete());
- EXPECT_TRUE(AtEof());
-}
-
-TEST_F(QuicHttpStreamTest, SendPostRequest) {
- SetRequestString("POST", "/", DEFAULT_PRIORITY);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, !kFin, 0, request_data_));
- AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, kFin,
- request_data_.length(),
- kUploadData));
- AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 3, 1));
+TEST_P(QuicHttpStreamTest, SendPostRequest) {
+ SetRequest("POST", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, !kFin));
+ AddWrite(ConstructDataPacket(2, kIncludeVersion, kFin, 0, kUploadData));
+ AddWrite(ConstructAckPacket(3, 3, 1));
Initialize();
@@ -526,17 +439,13 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) {
net_log_, callback_.callback()));
EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
// Ack both packets in the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
// Send the response headers (but not the body).
- SetResponseString("200 OK", std::string());
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(2, false, !kFin, 0, response_data_));
- ProcessPacket(*resp);
+ SetResponse("200 OK", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, !kFin));
// Since the headers have already arrived, this should return immediately.
EXPECT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback()));
@@ -546,11 +455,7 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) {
// Send the response body.
const char kResponseBody[] = "Hello world!";
- scoped_ptr<QuicEncryptedPacket> resp_body(
- ConstructDataPacket(3, false, kFin, response_data_.length(),
- kResponseBody));
- ProcessPacket(*resp_body);
-
+ ProcessPacket(ConstructDataPacket(3, false, kFin, 0, kResponseBody));
// Since the body has already arrived, this should return immediately.
EXPECT_EQ(static_cast<int>(strlen(kResponseBody)),
stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
@@ -560,18 +465,14 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) {
EXPECT_TRUE(AtEof());
}
-TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) {
- SetRequestString("POST", "/", DEFAULT_PRIORITY);
+TEST_P(QuicHttpStreamTest, SendChunkedPostRequest) {
+ SetRequest("POST", "/", DEFAULT_PRIORITY);
size_t chunk_size = strlen(kUploadData);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, !kFin, 0, request_data_));
- AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, !kFin,
- request_data_.length(),
- kUploadData));
- AddWrite(SYNCHRONOUS, ConstructDataPacket(3, true, kFin,
- request_data_.length() + chunk_size,
- kUploadData));
- AddWrite(SYNCHRONOUS, ConstructAckPacket(4, 3, 1));
-
+ AddWrite(ConstructRequestHeadersPacket(1, !kFin));
+ AddWrite(ConstructDataPacket(2, kIncludeVersion, !kFin, 0, kUploadData));
+ AddWrite(ConstructDataPacket(3, kIncludeVersion, kFin, chunk_size,
+ kUploadData));
+ AddWrite(ConstructAckPacket(4, 3, 1));
Initialize();
UploadDataStream upload_data_stream(UploadDataStream::CHUNKED, 0);
@@ -586,19 +487,15 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) {
net_log_, callback_.callback()));
ASSERT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
upload_data_stream.AppendChunk(kUploadData, chunk_size, true);
// Ack both packets in the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
// Send the response headers (but not the body).
- SetResponseString("200 OK", std::string());
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(2, false, !kFin, 0, response_data_));
- ProcessPacket(*resp);
+ SetResponse("200 OK", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, !kFin));
// Since the headers have already arrived, this should return immediately.
ASSERT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback()));
@@ -608,10 +505,8 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) {
// Send the response body.
const char kResponseBody[] = "Hello world!";
- scoped_ptr<QuicEncryptedPacket> resp_body(
- ConstructDataPacket(3, false, kFin, response_data_.length(),
- kResponseBody));
- ProcessPacket(*resp_body);
+ ProcessPacket(ConstructDataPacket(3, false, kFin, response_data_.length(),
+ kResponseBody));
// Since the body has already arrived, this should return immediately.
ASSERT_EQ(static_cast<int>(strlen(kResponseBody)),
@@ -622,10 +517,10 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) {
EXPECT_TRUE(AtEof());
}
-TEST_F(QuicHttpStreamTest, DestroyedEarly) {
- SetRequestString("GET", "/", DEFAULT_PRIORITY);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
- AddWrite(SYNCHRONOUS, ConstructRstStreamPacket(2));
+TEST_P(QuicHttpStreamTest, DestroyedEarly) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
+ AddWrite(ConstructAckAndRstStreamPacket(2));
use_closing_stream_ = true;
Initialize();
@@ -636,29 +531,24 @@ TEST_F(QuicHttpStreamTest, DestroyedEarly) {
net_log_, callback_.callback()));
EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
// Ack the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
EXPECT_EQ(ERR_IO_PENDING,
stream_->ReadResponseHeaders(callback_.callback()));
// Send the response with a body.
- SetResponseString("404 OK", "hello world!");
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(2, false, kFin, 0, response_data_));
-
+ SetResponse("404 OK", "hello world!");
// In the course of processing this packet, the QuicHttpStream close itself.
- ProcessPacket(*resp);
+ ProcessPacket(ConstructResponseHeadersPacket(2, kFin));
EXPECT_TRUE(AtEof());
}
-TEST_F(QuicHttpStreamTest, Priority) {
- SetRequestString("GET", "/", MEDIUM);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
- AddWrite(SYNCHRONOUS, ConstructRstStreamPacket(2));
+TEST_P(QuicHttpStreamTest, Priority) {
+ SetRequest("GET", "/", MEDIUM);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
+ AddWrite(ConstructAckAndRstStreamPacket(2));
use_closing_stream_ = true;
Initialize();
@@ -672,38 +562,36 @@ TEST_F(QuicHttpStreamTest, Priority) {
QuicReliableClientStream* reliable_stream =
QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get());
DCHECK(reliable_stream);
- DCHECK_EQ(static_cast<QuicPriority>(kHighestPriority),
+ DCHECK_EQ(QuicWriteBlockedList::kHighestPriority,
reliable_stream->EffectivePriority());
EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
callback_.callback()));
- EXPECT_EQ(&response_, stream_->GetResponseInfo());
// Check that priority has now dropped back to MEDIUM.
DCHECK_EQ(MEDIUM, ConvertQuicPriorityToRequestPriority(
reliable_stream->EffectivePriority()));
// Ack the request.
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
- ProcessPacket(*ack);
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
EXPECT_EQ(ERR_IO_PENDING,
stream_->ReadResponseHeaders(callback_.callback()));
// Send the response with a body.
- SetResponseString("404 OK", "hello world!");
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(2, false, kFin, 0, response_data_));
-
+ SetResponse("404 OK", "hello world!");
// In the course of processing this packet, the QuicHttpStream close itself.
- ProcessPacket(*resp);
+ ProcessPacket(ConstructResponseHeadersPacket(2, kFin));
EXPECT_TRUE(AtEof());
}
// Regression test for http://crbug.com/294870
-TEST_F(QuicHttpStreamTest, CheckPriorityWithNoDelegate) {
- SetRequestString("GET", "/", MEDIUM);
+TEST_P(QuicHttpStreamTest, CheckPriorityWithNoDelegate) {
+ SetRequest("GET", "/", MEDIUM);
use_closing_stream_ = true;
+
+ AddWrite(ConstructRstStreamPacket(1));
+
Initialize();
request_.method = "GET";
@@ -718,48 +606,16 @@ TEST_F(QuicHttpStreamTest, CheckPriorityWithNoDelegate) {
DCHECK(reliable_stream);
QuicReliableClientStream::Delegate* delegate = reliable_stream->GetDelegate();
DCHECK(delegate);
- DCHECK_EQ(static_cast<QuicPriority>(kHighestPriority),
+ DCHECK_EQ(QuicWriteBlockedList::kHighestPriority,
reliable_stream->EffectivePriority());
// Set Delegate to NULL and make sure EffectivePriority returns highest
// priority.
reliable_stream->SetDelegate(NULL);
- DCHECK_EQ(static_cast<QuicPriority>(kHighestPriority),
+ DCHECK_EQ(QuicWriteBlockedList::kHighestPriority,
reliable_stream->EffectivePriority());
reliable_stream->SetDelegate(delegate);
}
-TEST_F(QuicHttpStreamTest, DontCompressHeadersWhenNotWritable) {
- SetRequestString("GET", "/", MEDIUM);
- AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
-
- Initialize();
- request_.method = "GET";
- request_.url = GURL("http://www.google.com/");
-
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
- WillRepeatedly(Return(QuicTime::Delta::Infinite()));
- EXPECT_EQ(OK, stream_->InitializeStream(&request_, MEDIUM,
- net_log_, callback_.callback()));
- EXPECT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_,
- callback_.callback()));
-
- // Verify that the headers have not been compressed and buffered in
- // the stream.
- QuicReliableClientStream* reliable_stream =
- QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get());
- EXPECT_FALSE(reliable_stream->HasBufferedData());
- EXPECT_FALSE(AtEof());
-
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
- WillRepeatedly(Return(QuicTime::Delta::Zero()));
-
- // Data should flush out now.
- connection_->OnCanWrite();
- EXPECT_FALSE(reliable_stream->HasBufferedData());
- EXPECT_TRUE(AtEof());
-}
-
} // namespace test
-
} // namespace net
diff --git a/chromium/net/quic/quic_http_utils.cc b/chromium/net/quic/quic_http_utils.cc
index 173403d82dc..2bad8a6a71a 100644
--- a/chromium/net/quic/quic_http_utils.cc
+++ b/chromium/net/quic/quic_http_utils.cc
@@ -20,4 +20,16 @@ NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority(
IDLE : static_cast<RequestPriority>(HIGHEST - priority);
}
+base::Value* QuicRequestNetLogCallback(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock* headers,
+ QuicPriority priority,
+ NetLog::LogLevel log_level) {
+ base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(
+ SpdyHeaderBlockNetLogCallback(headers, log_level));
+ dict->SetInteger("quic_priority", static_cast<int>(priority));
+ dict->SetInteger("quic_stream_id", static_cast<int>(stream_id));
+ return dict;
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_http_utils.h b/chromium/net/quic/quic_http_utils.h
index c7e031ae605..862b7c61214 100644
--- a/chromium/net/quic/quic_http_utils.h
+++ b/chromium/net/quic/quic_http_utils.h
@@ -5,9 +5,11 @@
#ifndef NET_QUIC_QUIC_HTTP_UTILS_H_
#define NET_QUIC_QUIC_HTTP_UTILS_H_
+#include "base/values.h"
#include "net/base/net_export.h"
#include "net/base/request_priority.h"
#include "net/quic/quic_protocol.h"
+#include "net/spdy/spdy_header_block.h"
namespace net {
@@ -17,6 +19,14 @@ NET_EXPORT_PRIVATE QuicPriority ConvertRequestPriorityToQuicPriority(
NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority(
QuicPriority priority);
+// Converts a SpdyHeaderBlock and priority into NetLog event parameters. Caller
+// takes ownership of returned value.
+NET_EXPORT base::Value* QuicRequestNetLogCallback(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock* headers,
+ QuicPriority priority,
+ NetLog::LogLevel log_level);
+
} // namespace net
#endif // NET_QUIC_QUIC_HTTP_UTILS_H_
diff --git a/chromium/net/quic/quic_in_memory_cache.cc b/chromium/net/quic/quic_in_memory_cache.cc
new file mode 100644
index 00000000000..45d25b24e40
--- /dev/null
+++ b/chromium/net/quic/quic_in_memory_cache.cc
@@ -0,0 +1,242 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_in_memory_cache.h"
+
+#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/tools/balsa/balsa_headers.h"
+
+using base::FilePath;
+using base::StringPiece;
+using std::string;
+
+// Specifies the directory used during QuicInMemoryCache
+// construction to seed the cache. Cache directory can be
+// generated using `wget -p --save-headers <url>
+
+namespace net {
+
+namespace {
+
+const FilePath::CharType* g_quic_in_memory_cache_dir = FILE_PATH_LITERAL("");
+
+// BalsaVisitor implementation (glue) which caches response bodies.
+class CachingBalsaVisitor : public NoOpBalsaVisitor {
+ public:
+ CachingBalsaVisitor() : done_framing_(false) {}
+ virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE {
+ AppendToBody(input, size);
+ }
+ virtual void MessageDone() OVERRIDE {
+ done_framing_ = true;
+ }
+ virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ void UnhandledError() {
+ LOG(DFATAL) << "Unhandled error framing HTTP.";
+ }
+ void AppendToBody(const char* input, size_t size) {
+ body_.append(input, size);
+ }
+ bool done_framing() const { return done_framing_; }
+ const string& body() const { return body_; }
+
+ private:
+ bool done_framing_;
+ string body_;
+};
+
+} // namespace
+
+// static
+QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
+ return Singleton<QuicInMemoryCache>::get();
+}
+
+const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
+ const BalsaHeaders& request_headers) const {
+ ResponseMap::const_iterator it = responses_.find(GetKey(request_headers));
+ if (it == responses_.end()) {
+ return NULL;
+ }
+ return it->second;
+}
+
+void QuicInMemoryCache::AddSimpleResponse(StringPiece method,
+ StringPiece path,
+ StringPiece version,
+ StringPiece response_code,
+ StringPiece response_detail,
+ StringPiece body) {
+ BalsaHeaders request_headers, response_headers;
+ request_headers.SetRequestFirstlineFromStringPieces(method,
+ path,
+ version);
+ response_headers.SetRequestFirstlineFromStringPieces(version,
+ response_code,
+ response_detail);
+ response_headers.AppendHeader("content-length",
+ base::IntToString(body.length()));
+
+ AddResponse(request_headers, response_headers, body);
+}
+
+void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers,
+ const BalsaHeaders& response_headers,
+ StringPiece response_body) {
+ VLOG(1) << "Adding response for: " << GetKey(request_headers);
+ if (ContainsKey(responses_, GetKey(request_headers))) {
+ LOG(DFATAL) << "Response for given request already exists!";
+ return;
+ }
+ Response* new_response = new Response();
+ new_response->set_headers(response_headers);
+ new_response->set_body(response_body);
+ responses_[GetKey(request_headers)] = new_response;
+}
+
+void QuicInMemoryCache::AddSpecialResponse(StringPiece method,
+ StringPiece path,
+ StringPiece version,
+ SpecialResponseType response_type) {
+ BalsaHeaders request_headers, response_headers;
+ request_headers.SetRequestFirstlineFromStringPieces(method,
+ path,
+ version);
+ AddResponse(request_headers, response_headers, "");
+ responses_[GetKey(request_headers)]->response_type_ = response_type;
+}
+
+QuicInMemoryCache::QuicInMemoryCache() {
+ Initialize();
+}
+
+void QuicInMemoryCache::ResetForTests() {
+ STLDeleteValues(&responses_);
+ Initialize();
+}
+
+void QuicInMemoryCache::Initialize() {
+ // If there's no defined cache dir, we have no initialization to do.
+ if (g_quic_in_memory_cache_dir[0] == '\0') {
+ VLOG(1) << "No cache directory found. Skipping initialization.";
+ return;
+ }
+ VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
+ << g_quic_in_memory_cache_dir;
+
+ FilePath directory(g_quic_in_memory_cache_dir);
+ base::FileEnumerator file_list(directory,
+ true,
+ base::FileEnumerator::FILES);
+
+ FilePath file = file_list.Next();
+ while (!file.empty()) {
+ // Need to skip files in .svn directories
+ if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != std::string::npos) {
+ file = file_list.Next();
+ continue;
+ }
+
+ BalsaHeaders request_headers, response_headers;
+
+ string file_contents;
+ base::ReadFileToString(file, &file_contents);
+
+ // Frame HTTP.
+ CachingBalsaVisitor caching_visitor;
+ BalsaFrame framer;
+ framer.set_balsa_headers(&response_headers);
+ framer.set_balsa_visitor(&caching_visitor);
+ size_t processed = 0;
+ while (processed < file_contents.length() &&
+ !caching_visitor.done_framing()) {
+ processed += framer.ProcessInput(file_contents.c_str() + processed,
+ file_contents.length() - processed);
+ }
+
+ string response_headers_str;
+ response_headers.DumpToString(&response_headers_str);
+ if (!caching_visitor.done_framing()) {
+ LOG(DFATAL) << "Did not frame entire message from file: " << file.value()
+ << " (" << processed << " of " << file_contents.length()
+ << " bytes).";
+ }
+ if (processed < file_contents.length()) {
+ // Didn't frame whole file. Assume remainder is body.
+ // This sometimes happens as a result of incompatibilities between
+ // BalsaFramer and wget's serialization of HTTP sans content-length.
+ caching_visitor.AppendToBody(file_contents.c_str() + processed,
+ file_contents.length() - processed);
+ processed += file_contents.length();
+ }
+
+ StringPiece base = file.AsUTF8Unsafe();
+ if (response_headers.HasHeader("X-Original-Url")) {
+ base = response_headers.GetHeader("X-Original-Url");
+ response_headers.RemoveAllOfHeader("X-Original-Url");
+ // Remove the protocol so that the string is of the form host + path,
+ // which is parsed properly below.
+ if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
+ base.remove_prefix(8);
+ } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
+ base.remove_prefix(7);
+ }
+ }
+ int path_start = base.find_first_of('/');
+ DCHECK_LT(0, path_start);
+ StringPiece host(base.substr(0, path_start));
+ StringPiece path(base.substr(path_start));
+ if (path[path.length() - 1] == ',') {
+ path.remove_suffix(1);
+ }
+ // Set up request headers. Assume method is GET and protocol is HTTP/1.1.
+ request_headers.SetRequestFirstlineFromStringPieces("GET",
+ path,
+ "HTTP/1.1");
+ request_headers.ReplaceOrAppendHeader("host", host);
+
+ VLOG(1) << "Inserting 'http://" << GetKey(request_headers)
+ << "' into QuicInMemoryCache.";
+
+ AddResponse(request_headers, response_headers, caching_visitor.body());
+
+ file = file_list.Next();
+ }
+}
+
+QuicInMemoryCache::~QuicInMemoryCache() {
+ STLDeleteValues(&responses_);
+}
+
+string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const {
+ StringPiece uri = request_headers.request_uri();
+ if (uri.size() == 0) {
+ return "";
+ }
+ StringPiece host;
+ if (uri[0] == '/') {
+ host = request_headers.GetHeader("host");
+ } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "https://")) {
+ uri.remove_prefix(8);
+ } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "http://")) {
+ uri.remove_prefix(7);
+ }
+ return host.as_string() + uri.as_string();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_in_memory_cache.h b/chromium/net/quic/quic_in_memory_cache.h
new file mode 100644
index 00000000000..752620abf2d
--- /dev/null
+++ b/chromium/net/quic/quic_in_memory_cache.h
@@ -0,0 +1,116 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
+#define NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
+
+#include <string>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/tools/balsa/balsa_frame.h"
+#include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/balsa/noop_balsa_visitor.h"
+
+template <typename T> struct DefaultSingletonTraits;
+
+namespace net {
+
+namespace test {
+class QuicInMemoryCachePeer;
+} // namespace
+
+class QuicServer;
+
+// In-memory cache for HTTP responses.
+// Reads from disk cache generated by:
+// `wget -p --save_headers <url>`
+class QuicInMemoryCache {
+ public:
+ enum SpecialResponseType {
+ REGULAR_RESPONSE, // Send the headers and body like a server should.
+ CLOSE_CONNECTION, // Close the connection (sending the close packet).
+ IGNORE_REQUEST, // Do nothing, expect the client to time out.
+ };
+
+ // Container for response header/body pairs.
+ class Response {
+ public:
+ Response() : response_type_(REGULAR_RESPONSE) {}
+ ~Response() {}
+
+ SpecialResponseType response_type() const { return response_type_; }
+ const BalsaHeaders& headers() const { return headers_; }
+ const base::StringPiece body() const { return base::StringPiece(body_); }
+
+ private:
+ friend class QuicInMemoryCache;
+
+ void set_headers(const BalsaHeaders& headers) {
+ headers_.CopyFrom(headers);
+ }
+ void set_body(base::StringPiece body) {
+ body.CopyToString(&body_);
+ }
+
+ SpecialResponseType response_type_;
+ BalsaHeaders headers_;
+ std::string body_;
+
+ DISALLOW_COPY_AND_ASSIGN(Response);
+ };
+
+ // Returns the singleton instance of the cache.
+ static QuicInMemoryCache* GetInstance();
+
+ // Retrieve a response from this cache for a given request.
+ // If no appropriate response exists, NULL is returned.
+ // Currently, responses are selected based on request URI only.
+ const Response* GetResponse(const BalsaHeaders& request_headers) const;
+
+ // Adds a simple response to the cache. The response headers will
+ // only contain the "content-length" header with the lenght of |body|.
+ void AddSimpleResponse(base::StringPiece method,
+ base::StringPiece path,
+ base::StringPiece version,
+ base::StringPiece response_code,
+ base::StringPiece response_detail,
+ base::StringPiece body);
+
+ // Add a response to the cache.
+ void AddResponse(const BalsaHeaders& request_headers,
+ const BalsaHeaders& response_headers,
+ base::StringPiece response_body);
+
+ // Simulate a special behavior at a particular path.
+ void AddSpecialResponse(base::StringPiece method,
+ base::StringPiece path,
+ base::StringPiece version,
+ SpecialResponseType response_type);
+
+ private:
+ typedef base::hash_map<std::string, Response*> ResponseMap;
+ friend struct DefaultSingletonTraits<QuicInMemoryCache>;
+ friend class test::QuicInMemoryCachePeer;
+
+ QuicInMemoryCache();
+ ~QuicInMemoryCache();
+
+ void ResetForTests();
+
+ void Initialize();
+
+ std::string GetKey(const BalsaHeaders& response_headers) const;
+
+ // Cached responses.
+ ResponseMap responses_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicInMemoryCache);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
diff --git a/chromium/net/quic/quic_network_transaction_unittest.cc b/chromium/net/quic/quic_network_transaction_unittest.cc
index 2e4610ce5e7..bd3e3cd94b2 100644
--- a/chromium/net/quic/quic_network_transaction_unittest.cc
+++ b/chromium/net/quic/quic_network_transaction_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <vector>
+
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
@@ -17,7 +19,7 @@
#include "net/http/http_server_properties_impl.h"
#include "net/http/http_stream.h"
#include "net/http/http_stream_factory.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/http/transport_security_state.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/proxy/proxy_resolver.h"
@@ -30,6 +32,7 @@
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_packet_maker.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/mock_client_socket_pool_manager.h"
@@ -50,18 +53,62 @@ static const char kQuicAlternateProtocolHttpHeader[] =
"Alternate-Protocol: 80:quic\r\n\r\n";
static const char kQuicAlternateProtocolHttpsHeader[] =
"Alternate-Protocol: 443:quic\r\n\r\n";
+
} // namespace
namespace net {
namespace test {
-class QuicNetworkTransactionTest : public PlatformTest {
+// Helper class to encapsulate MockReads and MockWrites for QUIC.
+// Simplify ownership issues and the interaction with the MockSocketFactory.
+class MockQuicData {
+ public:
+ ~MockQuicData() {
+ STLDeleteElements(&packets_);
+ }
+
+ void AddRead(scoped_ptr<QuicEncryptedPacket> packet) {
+ reads_.push_back(MockRead(SYNCHRONOUS, packet->data(), packet->length(),
+ sequence_number_++));
+ packets_.push_back(packet.release());
+ }
+
+ void AddRead(IoMode mode, int rv) {
+ reads_.push_back(MockRead(mode, rv));
+ }
+
+ void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) {
+ writes_.push_back(MockWrite(SYNCHRONOUS, packet->data(), packet->length(),
+ sequence_number_++));
+ packets_.push_back(packet.release());
+ }
+
+ void AddDelayedSocketDataToFactory(MockClientSocketFactory* factory,
+ size_t delay) {
+ MockRead* reads = reads_.empty() ? NULL : &reads_[0];
+ MockWrite* writes = writes_.empty() ? NULL : &writes_[0];
+ socket_data_.reset(new DelayedSocketData(
+ delay, reads, reads_.size(), writes, writes_.size()));
+ factory->AddSocketDataProvider(socket_data_.get());
+ }
+
+ private:
+ std::vector<QuicEncryptedPacket*> packets_;
+ std::vector<MockWrite> writes_;
+ std::vector<MockRead> reads_;
+ size_t sequence_number_;
+ scoped_ptr<SocketDataProvider> socket_data_;
+};
+
+class QuicNetworkTransactionTest
+ : public PlatformTest,
+ public ::testing::WithParamInterface<QuicVersion> {
protected:
QuicNetworkTransactionTest()
- : clock_(new MockClock),
+ : maker_(GetParam(), 0),
+ clock_(new MockClock),
ssl_config_service_(new SSLConfigServiceDefaults),
proxy_service_(ProxyService::CreateDirect()),
- compressor_(new QuicSpdyCompressor()),
auth_handler_factory_(
HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
random_generator_(0),
@@ -69,6 +116,7 @@ class QuicNetworkTransactionTest : public PlatformTest {
request_.method = "GET";
request_.url = GURL("http://www.google.com/");
request_.load_flags = 0;
+ clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
}
virtual void SetUp() {
@@ -83,146 +131,71 @@ class QuicNetworkTransactionTest : public PlatformTest {
PlatformTest::TearDown();
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
base::MessageLoop::current()->RunUntilIdle();
- HttpStreamFactory::set_use_alternate_protocols(false);
- HttpStreamFactory::SetNextProtos(std::vector<NextProto>());
- }
-
- scoped_ptr<QuicEncryptedPacket> ConstructRstPacket(
- QuicPacketSequenceNumber num,
- QuicStreamId stream_id) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = false;
- header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header.packet_sequence_number = num;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR);
- return scoped_ptr<QuicEncryptedPacket>(
- ConstructPacket(header, QuicFrame(&rst)));
}
scoped_ptr<QuicEncryptedPacket> ConstructConnectionClosePacket(
QuicPacketSequenceNumber num) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = false;
- header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header.packet_sequence_number = num;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicConnectionCloseFrame close;
- close.error_code = QUIC_CRYPTO_VERSION_NOT_SUPPORTED;
- close.error_details = "Time to panic!";
- return scoped_ptr<QuicEncryptedPacket>(
- ConstructPacket(header, QuicFrame(&close)));
+ return maker_.MakeConnectionClosePacket(num);
}
scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
QuicPacketSequenceNumber largest_received,
QuicPacketSequenceNumber least_unacked) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = false;
- header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header.packet_sequence_number = 2;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked);
-
- QuicCongestionFeedbackFrame feedback;
- feedback.type = kTCP;
- feedback.tcp.accumulated_number_of_lost_packets = 0;
- feedback.tcp.receive_window = 256000;
-
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
- QuicFrames frames;
- frames.push_back(QuicFrame(&ack));
- frames.push_back(QuicFrame(&feedback));
- scoped_ptr<QuicPacket> packet(
- framer.BuildUnsizedDataPacket(header, frames).packet);
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
- ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+ return maker_.MakeAckPacket(2, largest_received, least_unacked, true);
}
- std::string GetRequestString(const std::string& method,
- const std::string& scheme,
- const std::string& path) {
- SpdyHeaderBlock headers;
- headers[":method"] = method;
- headers[":host"] = "www.google.com";
- headers[":path"] = path;
- headers[":scheme"] = scheme;
- headers[":version"] = "HTTP/1.1";
- return SerializeHeaderBlock(headers);
+ SpdyHeaderBlock GetRequestHeaders(const std::string& method,
+ const std::string& scheme,
+ const std::string& path) {
+ return maker_.GetRequestHeaders(method, scheme, path);
}
- std::string GetResponseString(const std::string& status,
- const std::string& body) {
- SpdyHeaderBlock headers;
- headers[":status"] = status;
- headers[":version"] = "HTTP/1.1";
- headers["content-type"] = "text/plain";
- return compressor_->CompressHeaders(headers) + body;
+ SpdyHeaderBlock GetResponseHeaders(const std::string& status) {
+ return maker_.GetResponseHeaders(status);
}
- std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers) {
- QuicSpdyCompressor compressor;
- return compressor.CompressHeadersWithPriority(
- ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY), headers);
- }
-
- // Returns a newly created packet to send kData on stream 1.
- QuicEncryptedPacket* ConstructDataPacket(
+ scoped_ptr<QuicEncryptedPacket> ConstructDataPacket(
QuicPacketSequenceNumber sequence_number,
QuicStreamId stream_id,
bool should_include_version,
bool fin,
QuicStreamOffset offset,
base::StringPiece data) {
- InitializeHeader(sequence_number, should_include_version);
- QuicStreamFrame frame(stream_id, fin, offset, MakeIOVector(data));
- return ConstructPacket(header_, QuicFrame(&frame)).release();
+ return maker_.MakeDataPacket(
+ sequence_number, stream_id, should_include_version, fin, offset, data);
}
- scoped_ptr<QuicEncryptedPacket> ConstructPacket(
- const QuicPacketHeader& header,
- const QuicFrame& frame) {
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
- QuicFrames frames;
- frames.push_back(frame);
- scoped_ptr<QuicPacket> packet(
- framer.BuildUnsizedDataPacket(header, frames).packet);
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
- ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+ scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ return maker_.MakeRequestHeadersPacket(
+ sequence_number, stream_id, should_include_version, fin, headers);
}
- void InitializeHeader(QuicPacketSequenceNumber sequence_number,
- bool should_include_version) {
- header_.public_header.guid = random_generator_.RandUint64();
- header_.public_header.reset_flag = false;
- header_.public_header.version_flag = should_include_version;
- header_.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header_.packet_sequence_number = sequence_number;
- header_.fec_group = 0;
- header_.entropy_flag = false;
- header_.fec_flag = false;
+ scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ return maker_.MakeResponseHeadersPacket(
+ sequence_number, stream_id, should_include_version, fin, headers);
}
void CreateSession() {
- CreateSessionWithFactory(&socket_factory_);
+ CreateSessionWithFactory(&socket_factory_, false);
+ }
+
+ void CreateSessionWithNextProtos() {
+ CreateSessionWithFactory(&socket_factory_, true);
}
- void CreateSessionWithFactory(ClientSocketFactory* socket_factory) {
+ // If |use_next_protos| is true, enables SPDY and QUIC.
+ void CreateSessionWithFactory(ClientSocketFactory* socket_factory,
+ bool use_next_protos) {
params_.enable_quic = true;
params_.quic_clock = clock_;
params_.quic_random = &random_generator_;
@@ -235,6 +208,12 @@ class QuicNetworkTransactionTest : public PlatformTest {
params_.ssl_config_service = ssl_config_service_.get();
params_.http_auth_handler_factory = auth_handler_factory_.get();
params_.http_server_properties = http_server_properties.GetWeakPtr();
+ params_.quic_supported_versions = SupportedVersions(GetParam());
+
+ if (use_next_protos) {
+ params_.use_alternate_protocols = true;
+ params_.next_protos = NextProtosSpdy3();
+ }
session_ = new HttpNetworkSession(params_);
session_->quic_stream_factory()->set_require_confirmation(false);
@@ -308,13 +287,22 @@ class QuicNetworkTransactionTest : public PlatformTest {
EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol);
}
+ void ExpectQuicAlternateProtocolMapping() {
+ ASSERT_TRUE(session_->http_server_properties()->HasAlternateProtocol(
+ HostPortPair::FromURL(request_.url)));
+ const PortAlternateProtocolPair alternate =
+ session_->http_server_properties()->GetAlternateProtocol(
+ HostPortPair::FromURL(request_.url));
+ EXPECT_EQ(QUIC, alternate.protocol);
+ }
+
void AddHangingNonAlternateProtocolSocketData() {
MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING);
hanging_data_.set_connect_data(hanging_connect);
socket_factory_.AddSocketDataProvider(&hanging_data_);
}
- QuicPacketHeader header_;
+ QuicTestPacketMaker maker_;
scoped_refptr<HttpNetworkSession> session_;
MockClientSocketFactory socket_factory_;
MockCryptoClientStreamFactory crypto_client_stream_factory_;
@@ -324,7 +312,6 @@ class QuicNetworkTransactionTest : public PlatformTest {
TransportSecurityState transport_security_state_;
scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_;
scoped_ptr<ProxyService> proxy_service_;
- scoped_ptr<QuicSpdyCompressor> compressor_;
scoped_ptr<HttpAuthHandlerFactory> auth_handler_factory_;
MockRandom random_generator_;
HttpServerPropertiesImpl http_server_properties;
@@ -334,35 +321,26 @@ class QuicNetworkTransactionTest : public PlatformTest {
StaticSocketDataProvider hanging_data_;
};
-TEST_F(QuicNetworkTransactionTest, ForceQuic) {
+INSTANTIATE_TEST_CASE_P(Version, QuicNetworkTransactionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicNetworkTransactionTest, ForceQuic) {
params_.origin_to_force_quic_on =
HostPortPair::FromString("www.google.com:80");
- QuicStreamId stream_id = 3;
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, stream_id, true, true, 0,
- GetRequestString("GET", "http", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
-
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
- };
-
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, stream_id, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
- };
-
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
- socket_factory_.AddSocketDataProvider(&quic_data);
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// The non-alternate protocol job needs to hang in order to guarantee that
// the alternate-protocol job will "win".
@@ -405,19 +383,43 @@ TEST_F(QuicNetworkTransactionTest, ForceQuic) {
int log_stream_id;
ASSERT_TRUE(entries[pos].GetIntegerValue("stream_id", &log_stream_id));
- EXPECT_EQ(stream_id, static_cast<QuicStreamId>(log_stream_id));
+ EXPECT_EQ(3, log_stream_id);
}
-TEST_F(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) {
+TEST_P(QuicNetworkTransactionTest, QuicProxy) {
+ proxy_service_.reset(
+ ProxyService::CreateFixedFromPacResult("QUIC myproxy:70"));
+
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+ // There is no need to set up an alternate protocol job, because
+ // no attempt will be made to speak to the proxy over TCP.
+
+ CreateSession();
+
+ SendRequestAndExpectQuicResponse("hello!");
+}
+
+TEST_P(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) {
params_.origin_to_force_quic_on =
HostPortPair::FromString("www.google.com:80");
- MockRead quic_reads[] = {
- MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
- };
- StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
- NULL, 0);
- socket_factory_.AddSocketDataProvider(&quic_data);
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddRead(ASYNC, ERR_SOCKET_NOT_CONNECTED);
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 0);
CreateSession();
@@ -429,7 +431,7 @@ TEST_F(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) {
EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
}
-TEST_F(QuicNetworkTransactionTest, DoNotForceQuicForHttps) {
+TEST_P(QuicNetworkTransactionTest, DoNotForceQuicForHttps) {
// Attempt to "force" quic on 443, which will not be honored.
params_.origin_to_force_quic_on =
HostPortPair::FromString("www.google.com:443");
@@ -451,9 +453,7 @@ TEST_F(QuicNetworkTransactionTest, DoNotForceQuicForHttps) {
SendRequestAndExpectHttpResponse("hello world");
}
-TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
+TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) {
MockRead http_reads[] = {
MockRead("HTTP/1.1 200 OK\r\n"),
MockRead(kQuicAlternateProtocolHttpHeader),
@@ -466,46 +466,34 @@ TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) {
NULL, 0);
socket_factory_.AddSocketDataProvider(&http_data);
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, 3, true, true, 0,
- GetRequestString("GET", "http", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
-
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
- };
-
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, 3, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
- };
-
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
- socket_factory_.AddSocketDataProvider(&quic_data);
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// The non-alternate protocol job needs to hang in order to guarantee that
// the alternate-protocol job will "win".
AddHangingNonAlternateProtocolSocketData();
- CreateSession();
+ CreateSessionWithNextProtos();
SendRequestAndExpectHttpResponse("hello world");
SendRequestAndExpectQuicResponse("hello!");
}
-TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) {
+TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) {
params_.origin_to_force_quic_on =
HostPortPair::FromString("www.google.com:443");
params_.enable_quic_https = true;
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
MockRead http_reads[] = {
MockRead("HTTP/1.1 200 OK\r\n"),
@@ -519,43 +507,31 @@ TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) {
NULL, 0);
socket_factory_.AddSocketDataProvider(&http_data);
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, 3, true, true, 0,
- GetRequestString("GET", "https", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
- };
-
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, 3, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
- };
-
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
-
- socket_factory_.AddSocketDataProvider(&quic_data);
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// The non-alternate protocol job needs to hang in order to guarantee that
// the alternate-protocol job will "win".
AddHangingNonAlternateProtocolSocketData();
- CreateSession();
+ CreateSessionWithNextProtos();
// TODO(rtenneti): Test QUIC over HTTPS, GetSSLInfo().
SendRequestAndExpectHttpResponse("hello world");
}
-TEST_F(QuicNetworkTransactionTest, HungAlternateProtocol) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
+TEST_P(QuicNetworkTransactionTest, HungAlternateProtocol) {
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::COLD_START);
@@ -594,7 +570,7 @@ TEST_F(QuicNetworkTransactionTest, HungAlternateProtocol) {
http_writes, arraysize(http_writes));
socket_factory.AddSocketDataProvider(&http_data2);
- CreateSessionWithFactory(&socket_factory);
+ CreateSessionWithFactory(&socket_factory, true);
// Run the first request.
http_data.StopAfter(arraysize(http_reads) + arraysize(http_writes));
@@ -613,70 +589,43 @@ TEST_F(QuicNetworkTransactionTest, HungAlternateProtocol) {
ASSERT_TRUE(!quic_data.at_write_eof());
}
-TEST_F(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, 3, true, true, 0,
- GetRequestString("GET", "http", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
-
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
- };
-
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, 3, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
- };
-
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
-
- socket_factory_.AddSocketDataProvider(&quic_data);
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// The non-alternate protocol job needs to hang in order to guarantee that
// the alternate-protocol job will "win".
AddHangingNonAlternateProtocolSocketData();
- CreateSession();
+ CreateSessionWithNextProtos();
AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
SendRequestAndExpectQuicResponse("hello!");
}
-TEST_F(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, 3, true, true, 0,
- GetRequestString("GET", "http", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
-
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
- };
-
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, 3, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
- };
-
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
-
- socket_factory_.AddSocketDataProvider(&quic_data);
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// In order for a new QUIC session to be established via alternate-protocol
// without racing an HTTP connection, we need the host resolution to happen
@@ -692,38 +641,65 @@ TEST_F(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) {
NULL,
net_log_.bound());
- CreateSession();
+ CreateSessionWithNextProtos();
AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
SendRequestAndExpectQuicResponse("hello!");
}
-TEST_F(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
- scoped_ptr<QuicEncryptedPacket> req(
- ConstructDataPacket(1, 3, true, true, 0,
- GetRequestString("GET", "http", "/")));
- scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0));
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithProxy) {
+ proxy_service_.reset(
+ ProxyService::CreateFixedFromPacResult("PROXY myproxy:70"));
- MockWrite quic_writes[] = {
- MockWrite(SYNCHRONOUS, req->data(), req->length()),
- MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
+ // Since we are using a proxy, the QUIC job will not succeed.
+ MockWrite http_writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET http://www.google.com/ HTTP/1.1\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "Host: www.google.com\r\n"),
+ MockWrite(SYNCHRONOUS, 2, "Proxy-Connection: keep-alive\r\n\r\n")
};
- scoped_ptr<QuicEncryptedPacket> resp(
- ConstructDataPacket(
- 1, 3, false, true, 0, GetResponseString("200 OK", "hello!")));
- MockRead quic_reads[] = {
- MockRead(SYNCHRONOUS, resp->data(), resp->length()),
- MockRead(ASYNC, OK), // EOF
+ MockRead http_reads[] = {
+ MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 4, kQuicAlternateProtocolHttpHeader),
+ MockRead(SYNCHRONOUS, 5, "hello world"),
+ MockRead(SYNCHRONOUS, OK, 6)
};
- DelayedSocketData quic_data(
- 1, // wait for one write to finish before reading.
- quic_reads, arraysize(quic_reads),
- quic_writes, arraysize(quic_writes));
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ http_writes, arraysize(http_writes));
+ socket_factory_.AddSocketDataProvider(&http_data);
- socket_factory_.AddSocketDataProvider(&quic_data);
+ // In order for a new QUIC session to be established via alternate-protocol
+ // without racing an HTTP connection, we need the host resolution to happen
+ // synchronously.
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule("www.google.com", "192.168.0.1", "");
+ HostResolver::RequestInfo info(HostPortPair("www.google.com", 80));
+ AddressList address;
+ host_resolver_.Resolve(info,
+ DEFAULT_PRIORITY,
+ &address,
+ CompletionCallback(),
+ NULL,
+ net_log_.bound());
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+ SendRequestAndExpectHttpResponse("hello world");
+}
+
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
// The non-alternate protocol job needs to hang in order to guarantee that
// the alternate-protocol job will "win".
@@ -741,7 +717,7 @@ TEST_F(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
CompletionCallback(), NULL, net_log_.bound());
- CreateSession();
+ CreateSessionWithNextProtos();
session_->quic_stream_factory()->set_require_confirmation(true);
AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
@@ -756,9 +732,7 @@ TEST_F(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
EXPECT_EQ(OK, callback.WaitForResult());
}
-TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocol) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
+TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocol) {
// Alternate-protocol job
scoped_ptr<QuicEncryptedPacket> close(ConstructConnectionClosePacket(1));
MockRead quic_reads[] = {
@@ -781,15 +755,13 @@ TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocol) {
NULL, 0);
socket_factory_.AddSocketDataProvider(&http_data);
- CreateSession();
+ CreateSessionWithNextProtos();
AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
SendRequestAndExpectHttpResponse("hello from http");
ExpectBrokenAlternateProtocolMapping();
}
-TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
-
+TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) {
// Alternate-protocol job
MockRead quic_reads[] = {
MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
@@ -810,16 +782,45 @@ TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) {
NULL, 0);
socket_factory_.AddSocketDataProvider(&http_data);
- CreateSession();
+ CreateSessionWithNextProtos();
AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
SendRequestAndExpectHttpResponse("hello from http");
ExpectBrokenAlternateProtocolMapping();
}
-TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
- HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too.
+TEST_P(QuicNetworkTransactionTest, NoBrokenAlternateProtocolIfTcpFails) {
+ // Alternate-protocol job will fail when the session attempts to read.
+ MockRead quic_reads[] = {
+ MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
+ };
+ StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data);
+ // Main job will also fail.
+ MockRead http_reads[] = {
+ MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ NULL, 0);
+ http_data.set_connect_data(MockConnect(ASYNC, ERR_SOCKET_NOT_CONNECTED));
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
+ scoped_ptr<HttpNetworkTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
+ TestCompletionCallback callback;
+ int rv = trans->Start(&request_, callback.callback(), net_log_.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, callback.WaitForResult());
+ ExpectQuicAlternateProtocolMapping();
+}
+
+TEST_P(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
// Alternate-protocol job
MockRead quic_reads[] = {
MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
@@ -830,6 +831,11 @@ TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
AddHangingNonAlternateProtocolSocketData();
+ // Second Alternate-protocol job which will race with the TCP job.
+ StaticSocketDataProvider quic_data2(quic_reads, arraysize(quic_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data2);
+
// Final job that will proceed when the QUIC job fails.
MockRead http_reads[] = {
MockRead("HTTP/1.1 200 OK\r\n\r\n"),
@@ -842,7 +848,7 @@ TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
NULL, 0);
socket_factory_.AddSocketDataProvider(&http_data);
- CreateSession();
+ CreateSessionWithNextProtos();
AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
@@ -854,5 +860,100 @@ TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
EXPECT_TRUE(quic_data.at_write_eof());
}
+TEST_P(QuicNetworkTransactionTest, DISABLED_HangingZeroRttFallback) {
+ // Alternate-protocol job
+ MockRead quic_reads[] = {
+ MockRead(ASYNC, ERR_IO_PENDING),
+ };
+ StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ // Main job that will proceed when the QUIC job fails.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello from http"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+
+ SendRequestAndExpectHttpResponse("hello from http");
+}
+
+TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocolOnConnectFailure) {
+ // Alternate-protocol job will fail before creating a QUIC session.
+ StaticSocketDataProvider quic_data(NULL, 0, NULL, 0);
+ quic_data.set_connect_data(MockConnect(SYNCHRONOUS,
+ ERR_INTERNET_DISCONNECTED));
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ // Main job which will succeed even though the alternate job fails.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello from http"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
+ SendRequestAndExpectHttpResponse("hello from http");
+
+ ExpectBrokenAlternateProtocolMapping();
+}
+
+TEST_P(QuicNetworkTransactionTest, ConnectionCloseDuringConnect) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddRead(ConstructConnectionClosePacket(1));
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 0);
+
+ // When the QUIC connection fails, we will try the request again over HTTP.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead(kQuicAlternateProtocolHttpHeader),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ NULL, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ // In order for a new QUIC session to be established via alternate-protocol
+ // without racing an HTTP connection, we need the host resolution to happen
+ // synchronously.
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule("www.google.com", "192.168.0.1", "");
+ HostResolver::RequestInfo info(HostPortPair("www.google.com", 80));
+ AddressList address;
+ host_resolver_.Resolve(info,
+ DEFAULT_PRIORITY,
+ &address,
+ CompletionCallback(),
+ NULL,
+ net_log_.bound());
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+ SendRequestAndExpectHttpResponse("hello world");
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_packet_creator.cc b/chromium/net/quic/quic_packet_creator.cc
index cb37cc271de..4eb0ea99a83 100644
--- a/chromium/net/quic/quic_packet_creator.cc
+++ b/chromium/net/quic/quic_packet_creator.cc
@@ -4,6 +4,7 @@
#include "net/quic/quic_packet_creator.h"
+#include "base/basictypes.h"
#include "base/logging.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_ack_notifier.h"
@@ -54,18 +55,22 @@ class QuicRandomBoolSource {
DISALLOW_COPY_AND_ASSIGN(QuicRandomBoolSource);
};
-QuicPacketCreator::QuicPacketCreator(QuicGuid guid,
+QuicPacketCreator::QuicPacketCreator(QuicConnectionId connection_id,
QuicFramer* framer,
- QuicRandom* random_generator,
- bool is_server)
- : guid_(guid),
+ QuicRandom* random_generator)
+ : connection_id_(connection_id),
+ encryption_level_(ENCRYPTION_NONE),
framer_(framer),
random_bool_source_(new QuicRandomBoolSource(random_generator)),
sequence_number_(0),
+ should_fec_protect_(false),
fec_group_number_(0),
- is_server_(is_server),
- send_version_in_packet_(!is_server),
- sequence_number_length_(options_.send_sequence_number_length),
+ send_version_in_packet_(!framer->is_server()),
+ max_packet_length_(kDefaultMaxPacketSize),
+ max_packets_per_fec_group_(0),
+ connection_id_length_(PACKET_8BYTE_CONNECTION_ID),
+ next_sequence_number_length_(PACKET_1BYTE_SEQUENCE_NUMBER),
+ sequence_number_length_(next_sequence_number_length_),
packet_size_(0) {
framer_->set_fec_builder(this);
}
@@ -77,23 +82,79 @@ void QuicPacketCreator::OnBuiltFecProtectedPayload(
const QuicPacketHeader& header, StringPiece payload) {
if (fec_group_.get()) {
DCHECK_NE(0u, header.fec_group);
- fec_group_->Update(header, payload);
+ fec_group_->Update(encryption_level_, header, payload);
}
}
bool QuicPacketCreator::ShouldSendFec(bool force_close) const {
+ DCHECK(!HasPendingFrames());
return fec_group_.get() != NULL && fec_group_->NumReceivedPackets() > 0 &&
- (force_close ||
- fec_group_->NumReceivedPackets() >= options_.max_packets_per_fec_group);
+ (force_close || fec_group_->NumReceivedPackets() >=
+ max_packets_per_fec_group_);
}
-void QuicPacketCreator::MaybeStartFEC() {
- if (options_.max_packets_per_fec_group > 0 && fec_group_.get() == NULL) {
- DCHECK(queued_frames_.empty());
- // Set the fec group number to the sequence number of the next packet.
- fec_group_number_ = sequence_number() + 1;
- fec_group_.reset(new QuicFecGroup());
+bool QuicPacketCreator::IsFecGroupOpen() const {
+ return ShouldSendFec(true);
+}
+
+void QuicPacketCreator::StartFecProtectingPackets() {
+ if (!IsFecEnabled()) {
+ LOG(DFATAL) << "Cannot start FEC protection when FEC is not enabled.";
+ return;
+ }
+ // TODO(jri): This currently requires that the generator flush out any
+ // pending frames when FEC protection is turned on. If current packet can be
+ // converted to an FEC protected packet, do it. This will require the
+ // generator to check if the resulting expansion still allows the incoming
+ // frame to be added to the packet.
+ if (HasPendingFrames()) {
+ LOG(DFATAL) << "Cannot start FEC protection with pending frames.";
+ return;
+ }
+ DCHECK(!should_fec_protect_);
+ should_fec_protect_ = true;
+}
+
+void QuicPacketCreator::StopFecProtectingPackets() {
+ if (fec_group_.get() != NULL) {
+ LOG(DFATAL) << "Cannot stop FEC protection with open FEC group.";
+ return;
}
+ DCHECK(should_fec_protect_);
+ should_fec_protect_ = false;
+ fec_group_number_ = 0;
+}
+
+bool QuicPacketCreator::IsFecProtected() const {
+ return should_fec_protect_;
+}
+
+bool QuicPacketCreator::IsFecEnabled() const {
+ return max_packets_per_fec_group_ > 0;
+}
+
+InFecGroup QuicPacketCreator::MaybeUpdateLengthsAndStartFec() {
+ if (fec_group_.get() != NULL) {
+ // Don't update any lengths when an FEC group is open, to ensure same
+ // packet header size in all packets within a group.
+ return IN_FEC_GROUP;
+ }
+ if (!queued_frames_.empty()) {
+ // Don't change creator state if there are frames queued.
+ return fec_group_.get() == NULL ? NOT_IN_FEC_GROUP : IN_FEC_GROUP;
+ }
+
+ // Update sequence number length only on packet and FEC group boundaries.
+ sequence_number_length_ = next_sequence_number_length_;
+
+ if (!should_fec_protect_) {
+ return NOT_IN_FEC_GROUP;
+ }
+ // Start a new FEC group since protection is on. Set the fec group number to
+ // the sequence number of the next packet.
+ fec_group_number_ = sequence_number() + 1;
+ fec_group_.reset(new QuicFecGroup());
+ return IN_FEC_GROUP;
}
// Stops serializing version of the protocol in packets sent after this call.
@@ -110,38 +171,44 @@ void QuicPacketCreator::StopSendingVersion() {
void QuicPacketCreator::UpdateSequenceNumberLength(
QuicPacketSequenceNumber least_packet_awaited_by_peer,
- QuicByteCount bytes_per_second) {
+ QuicByteCount congestion_window) {
DCHECK_LE(least_packet_awaited_by_peer, sequence_number_ + 1);
// Since the packet creator will not change sequence number length mid FEC
// group, include the size of an FEC group to be safe.
const QuicPacketSequenceNumber current_delta =
- options_.max_packets_per_fec_group + sequence_number_ + 1
+ max_packets_per_fec_group_ + sequence_number_ + 1
- least_packet_awaited_by_peer;
- const uint64 congestion_window =
- bytes_per_second / options_.max_packet_length;
- const uint64 delta = max(current_delta, congestion_window);
-
- options_.send_sequence_number_length =
+ const uint64 congestion_window_packets =
+ congestion_window / max_packet_length_;
+ const uint64 delta = max(current_delta, congestion_window_packets);
+ next_sequence_number_length_ =
QuicFramer::GetMinSequenceNumberLength(delta * 4);
}
bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id,
QuicStreamOffset offset) const {
+ // TODO(jri): This is a simple safe decision for now, but make
+ // is_in_fec_group a parameter. Same as with all public methods in
+ // QuicPacketCreator.
return BytesFree() >
- QuicFramer::GetMinStreamFrameSize(framer_->version(), id, offset, true);
+ QuicFramer::GetMinStreamFrameSize(framer_->version(), id, offset, true,
+ should_fec_protect_ ? IN_FEC_GROUP :
+ NOT_IN_FEC_GROUP);
}
// static
size_t QuicPacketCreator::StreamFramePacketOverhead(
QuicVersion version,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length,
+ QuicStreamOffset offset,
InFecGroup is_in_fec_group) {
- return GetPacketHeaderSize(guid_length, include_version,
+ return GetPacketHeaderSize(connection_id_length, include_version,
sequence_number_length, is_in_fec_group) +
// Assumes this is a stream with a single lone packet.
- QuicFramer::GetMinStreamFrameSize(version, 1u, 0u, true);
+ QuicFramer::GetMinStreamFrameSize(version, 1u, offset, true,
+ is_in_fec_group);
}
size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
@@ -149,51 +216,31 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
QuicStreamOffset offset,
bool fin,
QuicFrame* frame) {
- DCHECK_GT(options_.max_packet_length,
- StreamFramePacketOverhead(
- framer_->version(), PACKET_8BYTE_GUID, kIncludeVersion,
- PACKET_6BYTE_SEQUENCE_NUMBER, IN_FEC_GROUP));
- if (!HasRoomForStreamFrame(id, offset)) {
- LOG(DFATAL) << "No room for Stream frame, BytesFree: " << BytesFree()
- << " MinStreamFrameSize: "
- << QuicFramer::GetMinStreamFrameSize(
- framer_->version(), id, offset, true);
- }
+ DCHECK_GT(max_packet_length_, StreamFramePacketOverhead(
+ framer_->version(), PACKET_8BYTE_CONNECTION_ID, kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, offset, IN_FEC_GROUP));
+
+ InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec();
+
+ LOG_IF(DFATAL, !HasRoomForStreamFrame(id, offset))
+ << "No room for Stream frame, BytesFree: " << BytesFree()
+ << " MinStreamFrameSize: "
+ << QuicFramer::GetMinStreamFrameSize(
+ framer_->version(), id, offset, true, is_in_fec_group);
if (data.Empty()) {
- if (!fin) {
- LOG(DFATAL) << "Creating a stream frame with no data or fin.";
- }
+ LOG_IF(DFATAL, !fin)
+ << "Creating a stream frame with no data or fin.";
// Create a new packet for the fin, if necessary.
*frame = QuicFrame(new QuicStreamFrame(id, true, offset, data));
return 0;
}
- const size_t free_bytes = BytesFree();
- size_t bytes_consumed = 0;
const size_t data_size = data.TotalBufferSize();
-
- // When a STREAM frame is the last frame in a packet, it consumes two fewer
- // bytes of framing overhead.
- // Anytime more data is available than fits in with the extra two bytes,
- // the frame will be the last, and up to two extra bytes are consumed.
- // TODO(ianswett): If QUIC pads, the 1 byte PADDING frame does not fit when
- // 1 byte is available, because then the STREAM frame isn't the last.
-
- // The minimum frame size(0 bytes of data) if it's not the last frame.
size_t min_frame_size = QuicFramer::GetMinStreamFrameSize(
- framer_->version(), id, offset, false);
- // Check if it's the last frame in the packet.
- if (data_size + min_frame_size > free_bytes) {
- // The minimum frame size(0 bytes of data) if it is the last frame.
- size_t min_last_frame_size = QuicFramer::GetMinStreamFrameSize(
- framer_->version(), id, offset, true);
- bytes_consumed =
- min<size_t>(free_bytes - min_last_frame_size, data_size);
- } else {
- DCHECK_LT(data_size, BytesFree());
- bytes_consumed = data_size;
- }
+ framer_->version(), id, offset, /*last_frame_in_packet=*/ true,
+ is_in_fec_group);
+ size_t bytes_consumed = min<size_t>(BytesFree() - min_frame_size, data_size);
bool set_fin = fin && bytes_consumed == data_size; // Last frame.
IOVector frame_data;
@@ -224,25 +271,22 @@ size_t QuicPacketCreator::CreateStreamFrameWithNotifier(
SerializedPacket QuicPacketCreator::ReserializeAllFrames(
const QuicFrames& frames,
QuicSequenceNumberLength original_length) {
- const QuicSequenceNumberLength start_length = sequence_number_length_;
- const QuicSequenceNumberLength start_options_length =
- options_.send_sequence_number_length;
- const QuicFecGroupNumber start_fec_group = fec_group_number_;
- const size_t start_max_packets_per_fec_group =
- options_.max_packets_per_fec_group;
-
- // Temporarily set the sequence number length and disable FEC.
+ DCHECK(fec_group_.get() == NULL);
+ const QuicSequenceNumberLength saved_length = sequence_number_length_;
+ const QuicSequenceNumberLength saved_next_length =
+ next_sequence_number_length_;
+ const bool saved_should_fec_protect = should_fec_protect_;
+
+ // Temporarily set the sequence number length and stop FEC protection.
sequence_number_length_ = original_length;
- options_.send_sequence_number_length = original_length;
- fec_group_number_ = 0;
- options_.max_packets_per_fec_group = 0;
+ next_sequence_number_length_ = original_length;
+ should_fec_protect_ = false;
- // Serialize the packet and restore the fec and sequence number length state.
+ // Serialize the packet and restore the FEC and sequence number length state.
SerializedPacket serialized_packet = SerializeAllFrames(frames);
- sequence_number_length_ = start_length;
- options_.send_sequence_number_length = start_options_length;
- fec_group_number_ = start_fec_group;
- options_.max_packets_per_fec_group = start_max_packets_per_fec_group;
+ sequence_number_length_ = saved_length;
+ next_sequence_number_length_ = saved_next_length;
+ should_fec_protect_ = saved_should_fec_protect;
return serialized_packet;
}
@@ -253,9 +297,8 @@ SerializedPacket QuicPacketCreator::SerializeAllFrames(
// frames from SendStreamData()[send_stream_should_flush_ == false &&
// data.empty() == true] and retransmit due to RTO.
DCHECK_EQ(0u, queued_frames_.size());
- if (frames.empty()) {
- LOG(DFATAL) << "Attempt to serialize empty packet";
- }
+ LOG_IF(DFATAL, frames.empty())
+ << "Attempt to serialize empty packet";
for (size_t i = 0; i < frames.size(); ++i) {
bool success = AddFrame(frames[i], false);
DCHECK(success);
@@ -265,43 +308,46 @@ SerializedPacket QuicPacketCreator::SerializeAllFrames(
return packet;
}
-bool QuicPacketCreator::HasPendingFrames() {
+bool QuicPacketCreator::HasPendingFrames() const {
return !queued_frames_.empty();
}
-size_t QuicPacketCreator::BytesFree() const {
- const size_t max_plaintext_size =
- framer_->GetMaxPlaintextSize(options_.max_packet_length);
- DCHECK_GE(max_plaintext_size, PacketSize());
+bool QuicPacketCreator::HasPendingRetransmittableFrames() const {
+ return queued_retransmittable_frames_.get() != NULL &&
+ !queued_retransmittable_frames_->frames().empty();
+}
- // If the last frame in the packet is a stream frame, then it can be
- // two bytes smaller than if it were not the last. So this means that
- // there are two fewer bytes available to the next frame in this case.
+size_t QuicPacketCreator::ExpansionOnNewFrame() const {
+ // If packet is FEC protected, there's no expansion.
+ if (should_fec_protect_) {
+ return 0;
+ }
+ // If the last frame in the packet is a stream frame, then it will expand to
+ // include the stream_length field when a new frame is added.
bool has_trailing_stream_frame =
!queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME;
- size_t expanded_packet_size = PacketSize() +
- (has_trailing_stream_frame ? kQuicStreamPayloadLengthSize : 0);
+ return has_trailing_stream_frame ? kQuicStreamPayloadLengthSize : 0;
+}
- if (expanded_packet_size >= max_plaintext_size) {
- return 0;
- }
- return max_plaintext_size - expanded_packet_size;
+size_t QuicPacketCreator::BytesFree() const {
+ const size_t max_plaintext_size =
+ framer_->GetMaxPlaintextSize(max_packet_length_);
+ DCHECK_GE(max_plaintext_size, PacketSize());
+ return max_plaintext_size - min(max_plaintext_size, PacketSize()
+ + ExpansionOnNewFrame());
}
size_t QuicPacketCreator::PacketSize() const {
- if (queued_frames_.empty()) {
- // Only adjust the sequence number length when the FEC group is not open,
- // to ensure no packets in a group are too large.
- if (fec_group_.get() == NULL ||
- fec_group_->NumReceivedPackets() == 0) {
- sequence_number_length_ = options_.send_sequence_number_length;
- }
- packet_size_ = GetPacketHeaderSize(options_.send_guid_length,
- send_version_in_packet_,
- sequence_number_length_,
- options_.max_packets_per_fec_group == 0 ?
- NOT_IN_FEC_GROUP : IN_FEC_GROUP);
+ if (!queued_frames_.empty()) {
+ return packet_size_;
}
+ if (fec_group_.get() == NULL) {
+ // Update sequence number length on packet and FEC boundary.
+ sequence_number_length_ = next_sequence_number_length_;
+ }
+ packet_size_ = GetPacketHeaderSize(
+ connection_id_length_, send_version_in_packet_, sequence_number_length_,
+ should_fec_protect_ ? IN_FEC_GROUP : NOT_IN_FEC_GROUP);
return packet_size_;
}
@@ -310,36 +356,32 @@ bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame) {
}
SerializedPacket QuicPacketCreator::SerializePacket() {
- if (queued_frames_.empty()) {
- LOG(DFATAL) << "Attempt to serialize empty packet";
- }
+ LOG_IF(DFATAL, queued_frames_.empty())
+ << "Attempt to serialize empty packet";
+ DCHECK_GE(sequence_number_ + 1, fec_group_number_);
QuicPacketHeader header;
- FillPacketHeader(fec_group_number_, false, false, &header);
+ FillPacketHeader(should_fec_protect_ ? fec_group_number_ : 0, false, &header);
MaybeAddPadding();
size_t max_plaintext_size =
- framer_->GetMaxPlaintextSize(options_.max_packet_length);
+ framer_->GetMaxPlaintextSize(max_packet_length_);
DCHECK_GE(max_plaintext_size, packet_size_);
- // ACK and CONNECTION_CLOSE Frames will be truncated only if they're
- // the first frame in the packet. If truncation is to occur, then
- // GetSerializedFrameLength will have returned all bytes free.
- bool possibly_truncated =
- packet_size_ != max_plaintext_size ||
- queued_frames_.size() != 1 ||
- (queued_frames_.back().type == ACK_FRAME ||
- queued_frames_.back().type == CONNECTION_CLOSE_FRAME);
+ // ACK Frames will be truncated only if they're the only frame in the packet,
+ // and if packet_size_ was set to max_plaintext_size. If truncation occurred,
+ // then GetSerializedFrameLength will have returned all bytes free.
+ bool possibly_truncated = packet_size_ == max_plaintext_size &&
+ queued_frames_.size() == 1 &&
+ queued_frames_.back().type == ACK_FRAME;
SerializedPacket serialized =
framer_->BuildDataPacket(header, queued_frames_, packet_size_);
- if (!serialized.packet) {
- LOG(DFATAL) << "Failed to serialize " << queued_frames_.size()
- << " frames.";
- }
+ LOG_IF(DFATAL, !serialized.packet)
+ << "Failed to serialize " << queued_frames_.size() << " frames.";
// Because of possible truncation, we can't be confident that our
// packet size calculation worked correctly.
- if (!possibly_truncated)
+ if (!possibly_truncated) {
DCHECK_EQ(packet_size_, serialized.packet->length());
-
+ }
packet_size_ = 0;
queued_frames_.clear();
serialized.retransmittable_frames = queued_retransmittable_frames_.release();
@@ -347,23 +389,24 @@ SerializedPacket QuicPacketCreator::SerializePacket() {
}
SerializedPacket QuicPacketCreator::SerializeFec() {
- DCHECK_LT(0u, fec_group_->NumReceivedPackets());
+ if (fec_group_.get() == NULL || fec_group_->NumReceivedPackets() <= 0) {
+ LOG(DFATAL) << "SerializeFEC called but no group or zero packets in group.";
+ // TODO(jri): Make this a public method of framer?
+ SerializedPacket kNoPacket(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL);
+ return kNoPacket;
+ }
DCHECK_EQ(0u, queued_frames_.size());
QuicPacketHeader header;
- FillPacketHeader(fec_group_number_, true,
- fec_group_->entropy_parity(), &header);
+ FillPacketHeader(fec_group_number_, true, &header);
QuicFecData fec_data;
fec_data.fec_group = fec_group_->min_protected_packet();
fec_data.redundancy = fec_group_->payload_parity();
SerializedPacket serialized = framer_->BuildFecPacket(header, fec_data);
fec_group_.reset(NULL);
- fec_group_number_ = 0;
packet_size_ = 0;
- if (!serialized.packet) {
- LOG(DFATAL) << "Failed to serialize fec packet for group:"
- << fec_data.fec_group;
- }
- DCHECK_GE(options_.max_packet_length, serialized.packet->length());
+ LOG_IF(DFATAL, !serialized.packet)
+ << "Failed to serialize fec packet for group:" << fec_data.fec_group;
+ DCHECK_GE(max_packet_length_, serialized.packet->length());
return serialized;
}
@@ -376,64 +419,59 @@ SerializedPacket QuicPacketCreator::SerializeConnectionClose(
QuicEncryptedPacket* QuicPacketCreator::SerializeVersionNegotiationPacket(
const QuicVersionVector& supported_versions) {
- DCHECK(is_server_);
+ DCHECK(framer_->is_server());
QuicPacketPublicHeader header;
- header.guid = guid_;
+ header.connection_id = connection_id_;
header.reset_flag = false;
header.version_flag = true;
header.versions = supported_versions;
QuicEncryptedPacket* encrypted =
framer_->BuildVersionNegotiationPacket(header, supported_versions);
DCHECK(encrypted);
- DCHECK_GE(options_.max_packet_length, encrypted->length());
+ DCHECK_GE(max_packet_length_, encrypted->length());
return encrypted;
}
void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group,
bool fec_flag,
- bool fec_entropy_flag,
QuicPacketHeader* header) {
- header->public_header.guid = guid_;
+ header->public_header.connection_id = connection_id_;
header->public_header.reset_flag = false;
header->public_header.version_flag = send_version_in_packet_;
header->fec_flag = fec_flag;
header->packet_sequence_number = ++sequence_number_;
header->public_header.sequence_number_length = sequence_number_length_;
-
- bool entropy_flag;
- if (fec_flag) {
- // FEC packets don't have an entropy of their own. Entropy flag for FEC
- // packets is the XOR of entropy of previous packets.
- entropy_flag = fec_entropy_flag;
- } else {
- entropy_flag = random_bool_source_->RandBool();
- }
- header->entropy_flag = entropy_flag;
+ header->entropy_flag = random_bool_source_->RandBool();
header->is_in_fec_group = fec_group == 0 ? NOT_IN_FEC_GROUP : IN_FEC_GROUP;
header->fec_group = fec_group;
}
bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) {
- return frame.type != ACK_FRAME && frame.type != CONGESTION_FEEDBACK_FRAME &&
- frame.type != PADDING_FRAME;
+ switch (frame.type) {
+ case ACK_FRAME:
+ case CONGESTION_FEEDBACK_FRAME:
+ case PADDING_FRAME:
+ case STOP_WAITING_FRAME:
+ return false;
+ default:
+ return true;
+ }
}
bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
bool save_retransmittable_frames) {
+ DVLOG(1) << "Adding frame: " << frame;
+ InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec();
+
size_t frame_len = framer_->GetSerializedFrameLength(
- frame, BytesFree(), queued_frames_.empty(), true,
- options()->send_sequence_number_length);
+ frame, BytesFree(), queued_frames_.empty(), true, is_in_fec_group,
+ sequence_number_length_);
if (frame_len == 0) {
return false;
}
DCHECK_LT(0u, packet_size_);
- MaybeStartFEC();
- packet_size_ += frame_len;
- // If the last frame in the packet was a stream frame, then once we add the
- // new frame it's serialization will be two bytes larger.
- if (!queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME) {
- packet_size_ += kQuicStreamPayloadLengthSize;
- }
+ packet_size_ += ExpansionOnNewFrame() + frame_len;
+
if (save_retransmittable_frames && ShouldRetransmit(frame)) {
if (queued_retransmittable_frames_.get() == NULL) {
queued_retransmittable_frames_.reset(new RetransmittableFrames());
diff --git a/chromium/net/quic/quic_packet_creator.h b/chromium/net/quic/quic_packet_creator.h
index b6756af4059..10b8e8883b8 100644
--- a/chromium/net/quic/quic_packet_creator.h
+++ b/chromium/net/quic/quic_packet_creator.h
@@ -29,27 +29,10 @@ class QuicRandomBoolSource;
class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
public:
- // Options for controlling how packets are created.
- struct Options {
- Options()
- : max_packet_length(kDefaultMaxPacketSize),
- max_packets_per_fec_group(0),
- send_guid_length(PACKET_8BYTE_GUID),
- send_sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER) {}
-
- size_t max_packet_length;
- // 0 indicates fec is disabled.
- size_t max_packets_per_fec_group;
- // Length of guid to send over the wire.
- QuicGuidLength send_guid_length;
- QuicSequenceNumberLength send_sequence_number_length;
- };
-
// QuicRandom* required for packet entropy.
- QuicPacketCreator(QuicGuid guid,
+ QuicPacketCreator(QuicConnectionId connection_id,
QuicFramer* framer,
- QuicRandom* random_generator,
- bool is_server);
+ QuicRandom* random_generator);
virtual ~QuicPacketCreator();
@@ -57,10 +40,24 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
virtual void OnBuiltFecProtectedPayload(const QuicPacketHeader& header,
base::StringPiece payload) OVERRIDE;
+ // Turn on FEC protection for subsequently created packets. FEC should be
+ // enabled first (max_packets_per_fec_group should be non-zero) for FEC
+ // protection to start.
+ void StartFecProtectingPackets();
+
+ // Turn off FEC protection for subsequently created packets. If the creator
+ // has any open FEC group, call will fail. It is the caller's responsibility
+ // to flush out FEC packets in generation, and to verify with ShouldSendFec()
+ // that there is no open FEC group.
+ void StopFecProtectingPackets();
+
// Checks if it's time to send an FEC packet. |force_close| forces this to
- // return true if an fec group is open.
+ // return true if an FEC group is open.
bool ShouldSendFec(bool force_close) const;
+ // Returns true if an FEC packet is under construction.
+ bool IsFecGroupOpen() const;
+
// Makes the framer not serialize the protocol version in sent packets.
void StopSendingVersion();
@@ -68,14 +65,15 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// can be safely changed.
void UpdateSequenceNumberLength(
QuicPacketSequenceNumber least_packet_awaited_by_peer,
- QuicByteCount bytes_per_second);
+ QuicByteCount congestion_window);
// The overhead the framing will add for a packet with one frame.
static size_t StreamFramePacketOverhead(
QuicVersion version,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length,
+ QuicStreamOffset offset,
InFecGroup is_in_fec_group);
bool HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset) const;
@@ -109,11 +107,27 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// Re-serializes frames with the original packet's sequence number length.
// Used for retransmitting packets to ensure they aren't too long.
+ // Caller must ensure that any open FEC group is closed before calling this
+ // method.
SerializedPacket ReserializeAllFrames(
- const QuicFrames& frames, QuicSequenceNumberLength original_length);
+ const QuicFrames& frames,
+ QuicSequenceNumberLength original_length);
// Returns true if there are frames pending to be serialized.
- bool HasPendingFrames();
+ bool HasPendingFrames() const;
+
+ // Returns true if there are retransmittable frames pending to be serialized.
+ bool HasPendingRetransmittableFrames() const;
+
+ // Returns whether FEC protection is currently enabled. Note: Enabled does not
+ // mean that an FEC group is currently active; i.e., IsFecProtected() may
+ // still return false.
+ bool IsFecEnabled() const;
+
+ // Returns true if subsequent packets will be FEC protected. Note: True does
+ // not mean that an FEC packet is currently under construction; i.e.,
+ // fec_group_.get() may still be NULL, until MaybeStartFec() is called.
+ bool IsFecProtected() const;
// Returns the number of bytes which are available to be used by additional
// frames in the packet. Since stream frames are slightly smaller when they
@@ -121,6 +135,13 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// value than max_packet_size - PacketSize(), in this case.
size_t BytesFree() const;
+ // Returns the number of bytes that the packet will expand by if a new frame
+ // is added to the packet. If the last frame was a stream frame, it will
+ // expand slightly when a new frame is added, and this method returns the
+ // amount of expected expansion. If the packet is in an FEC group, no
+ // expansion happens and this method always returns zero.
+ size_t ExpansionOnNewFrame() const;
+
// Returns the number of bytes in the current packet, including the header,
// if serialized with the current frames. Adding a frame to the packet
// may change the serialized length of existing frames, as per the comment
@@ -161,6 +182,11 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
QuicEncryptedPacket* SerializeVersionNegotiationPacket(
const QuicVersionVector& supported_versions);
+ // Sets the encryption level that will be applied to new packets.
+ void set_encryption_level(EncryptionLevel level) {
+ encryption_level_ = level;
+ }
+
// Sequence number of the last created packet, or 0 if no packets have been
// created.
QuicPacketSequenceNumber sequence_number() const {
@@ -171,8 +197,39 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
sequence_number_ = s;
}
- Options* options() {
- return &options_;
+ QuicConnectionIdLength connection_id_length() const {
+ return connection_id_length_;
+ }
+
+ QuicSequenceNumberLength next_sequence_number_length() const {
+ return next_sequence_number_length_;
+ }
+
+ void set_next_sequence_number_length(QuicSequenceNumberLength length) {
+ next_sequence_number_length_ = length;
+ }
+
+ size_t max_packet_length() const {
+ return max_packet_length_;
+ }
+
+ void set_max_packet_length(size_t length) {
+ // |max_packet_length_| should not be changed mid-packet or mid-FEC group.
+ DCHECK(fec_group_.get() == NULL && queued_frames_.empty());
+ max_packet_length_ = length;
+ }
+
+ // Returns current max number of packets covered by an FEC group.
+ size_t max_packets_per_fec_group() const {
+ return max_packets_per_fec_group_;
+ }
+
+ // Sets creator's max number of packets covered by an FEC group.
+ void set_max_packets_per_fec_group(
+ size_t max_packets_per_fec_group) {
+ // To turn off FEC protection, use StopFecProtectingPackets().
+ DCHECK_NE(0u, max_packets_per_fec_group);
+ max_packets_per_fec_group_ = max_packets_per_fec_group;
}
private:
@@ -180,13 +237,16 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
static bool ShouldRetransmit(const QuicFrame& frame);
- // Starts a new FEC group with the next serialized packet, if FEC is enabled
- // and there is not already an FEC group open.
- void MaybeStartFEC();
+ // Updates sequence number and max packet lengths on a packet or FEC group
+ // boundary.
+ void MaybeUpdateLengths();
+
+ // Updates lengths and also starts an FEC group if FEC protection is on and
+ // there is not already an FEC group open.
+ InFecGroup MaybeUpdateLengthsAndStartFec();
void FillPacketHeader(QuicFecGroupNumber fec_group,
bool fec_flag,
- bool fec_entropy_flag,
QuicPacketHeader* header);
// Allows a frame to be added without creating retransmittable frames.
@@ -198,21 +258,32 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// padding frame.
void MaybeAddPadding();
- Options options_;
- QuicGuid guid_;
+ QuicConnectionId connection_id_;
+ EncryptionLevel encryption_level_;
QuicFramer* framer_;
scoped_ptr<QuicRandomBoolSource> random_bool_source_;
QuicPacketSequenceNumber sequence_number_;
+ // If true, any created packets will be FEC protected.
+ bool should_fec_protect_;
QuicFecGroupNumber fec_group_number_;
scoped_ptr<QuicFecGroup> fec_group_;
- // bool to keep track if this packet creator is being used the server.
- bool is_server_;
// Controls whether protocol version should be included while serializing the
// packet.
bool send_version_in_packet_;
- // The sequence number length for the current packet and the current FEC group
- // if FEC is enabled.
- // Mutable so PacketSize() can adjust it when the packet is empty.
+ // Maximum length including headers and encryption (UDP payload length.)
+ size_t max_packet_length_;
+ // 0 indicates FEC is disabled.
+ size_t max_packets_per_fec_group_;
+ // Length of connection_id to send over the wire.
+ QuicConnectionIdLength connection_id_length_;
+ // Staging variable to hold next packet sequence number length. When sequence
+ // number length is to be changed, this variable holds the new length until
+ // a packet or FEC group boundary, when the creator's sequence_number_length_
+ // can be changed to this new value.
+ QuicSequenceNumberLength next_sequence_number_length_;
+ // Sequence number length for the current packet and for the current FEC group
+ // when FEC is enabled. Mutable so PacketSize() can adjust it when the packet
+ // is empty.
mutable QuicSequenceNumberLength sequence_number_length_;
// packet_size_ is mutable because it's just a cache of the current size.
// packet_size should never be read directly, use PacketSize() instead.
diff --git a/chromium/net/quic/quic_packet_creator_test.cc b/chromium/net/quic/quic_packet_creator_test.cc
index 33f2e5c3010..af9b53afecf 100644
--- a/chromium/net/quic/quic_packet_creator_test.cc
+++ b/chromium/net/quic/quic_packet_creator_test.cc
@@ -10,11 +10,14 @@
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_framer_peer.h"
#include "net/quic/test_tools/quic_packet_creator_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
using base::StringPiece;
+using std::ostream;
using std::string;
using std::vector;
using testing::DoAll;
@@ -27,19 +30,52 @@ namespace net {
namespace test {
namespace {
-class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> {
+// Run tests with combinations of {QuicVersion, ToggleVersionSerialization}.
+struct TestParams {
+ TestParams(QuicVersion version,
+ bool version_serialization)
+ : version(version),
+ version_serialization(version_serialization) {
+ }
+
+ friend ostream& operator<<(ostream& os, const TestParams& p) {
+ os << "{ client_version: " << QuicVersionToString(p.version)
+ << " include version: " << p.version_serialization << " }";
+ return os;
+ }
+
+ QuicVersion version;
+ bool version_serialization;
+};
+
+// Constructs various test permutations.
+vector<TestParams> GetTestParams() {
+ vector<TestParams> params;
+ QuicVersionVector all_supported_versions = QuicSupportedVersions();
+ for (size_t i = 0; i < all_supported_versions.size(); ++i) {
+ params.push_back(TestParams(all_supported_versions[i], true));
+ params.push_back(TestParams(all_supported_versions[i], false));
+ }
+ return params;
+}
+
+class QuicPacketCreatorTest : public ::testing::TestWithParam<TestParams> {
protected:
QuicPacketCreatorTest()
- : server_framer_(QuicSupportedVersions(), QuicTime::Zero(), true),
- client_framer_(QuicSupportedVersions(), QuicTime::Zero(), false),
+ : server_framer_(SupportedVersions(GetParam().version), QuicTime::Zero(),
+ true),
+ client_framer_(SupportedVersions(GetParam().version), QuicTime::Zero(),
+ false),
sequence_number_(0),
- guid_(2),
+ connection_id_(2),
data_("foo"),
- creator_(guid_, &client_framer_, &mock_random_, false) {
+ creator_(connection_id_, &client_framer_, &mock_random_) {
client_framer_.set_visitor(&framer_visitor_);
+ client_framer_.set_received_entropy_calculator(&entropy_calculator_);
server_framer_.set_visitor(&framer_visitor_);
}
- ~QuicPacketCreatorTest() {
+
+ virtual ~QuicPacketCreatorTest() OVERRIDE {
}
void ProcessPacket(QuicPacket* packet) {
@@ -64,30 +100,36 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> {
}
// Returns the number of bytes consumed by the header of packet, including
- // the version, that is not in an FEC group.
- size_t GetPacketHeaderOverhead() {
- return GetPacketHeaderSize(creator_.options()->send_guid_length,
+ // the version.
+ size_t GetPacketHeaderOverhead(InFecGroup is_in_fec_group) {
+ return GetPacketHeaderSize(creator_.connection_id_length(),
kIncludeVersion,
- creator_.options()->send_sequence_number_length,
- NOT_IN_FEC_GROUP);
+ creator_.next_sequence_number_length(),
+ is_in_fec_group);
}
// Returns the number of bytes of overhead that will be added to a packet
// of maximum length.
size_t GetEncryptionOverhead() {
- return creator_.options()->max_packet_length -
- client_framer_.GetMaxPlaintextSize(
- creator_.options()->max_packet_length);
+ return creator_.max_packet_length() - client_framer_.GetMaxPlaintextSize(
+ creator_.max_packet_length());
}
// Returns the number of bytes consumed by the non-data fields of a stream
// frame, assuming it is the last frame in the packet
- size_t GetStreamFrameOverhead() {
- return QuicFramer::GetMinStreamFrameSize(
- client_framer_.version(), kStreamId, kOffset, true);
+ size_t GetStreamFrameOverhead(InFecGroup is_in_fec_group) {
+ return QuicFramer::GetMinStreamFrameSize(client_framer_.version(),
+ kClientDataStreamId1, kOffset,
+ true, is_in_fec_group);
+ }
+
+ // Enables and turns on FEC protection. Returns true if FEC protection is on.
+ bool SwitchFecProtectionOn(size_t max_packets_per_fec_group) {
+ creator_.set_max_packets_per_fec_group(max_packets_per_fec_group);
+ creator_.StartFecProtectingPackets();
+ return creator_.IsFecProtected();
}
- static const QuicStreamId kStreamId = 1u;
static const QuicStreamOffset kOffset = 1u;
QuicFrames frames_;
@@ -95,14 +137,21 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> {
QuicFramer client_framer_;
testing::StrictMock<MockFramerVisitor> framer_visitor_;
QuicPacketSequenceNumber sequence_number_;
- QuicGuid guid_;
+ QuicConnectionId connection_id_;
string data_;
MockRandom mock_random_;
QuicPacketCreator creator_;
+ MockEntropyCalculator entropy_calculator_;
};
-TEST_F(QuicPacketCreatorTest, SerializeFrames) {
- frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u)));
+// Run all packet creator tests with all supported versions of QUIC, and with
+// and without version in the packet header.
+INSTANTIATE_TEST_CASE_P(QuicPacketCreatorTests,
+ QuicPacketCreatorTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicPacketCreatorTest, SerializeFrames) {
+ frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u))));
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, true, 0u, IOVector())));
SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
@@ -113,7 +162,9 @@ TEST_F(QuicPacketCreatorTest, SerializeFrames) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnAckFrame(_));
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
@@ -124,9 +175,12 @@ TEST_F(QuicPacketCreatorTest, SerializeFrames) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
- creator_.options()->max_packets_per_fec_group = 6;
- ASSERT_FALSE(creator_.ShouldSendFec(false));
+TEST_P(QuicPacketCreatorTest, SerializeWithFEC) {
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
@@ -135,7 +189,9 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_));
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
@@ -144,16 +200,20 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
ProcessPacket(serialized.packet);
delete serialized.packet;
- ASSERT_FALSE(creator_.ShouldSendFec(false));
- ASSERT_TRUE(creator_.ShouldSendFec(true));
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ // Should return true since there are packets in the FEC group.
+ ASSERT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
serialized = creator_.SerializeFec();
ASSERT_EQ(2u, serialized.sequence_number);
-
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnFecData(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -162,11 +222,10 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
- frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u)));
+TEST_P(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
+ frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u))));
creator_.AddSavedFrame(frames_[0]);
- creator_.options()->send_sequence_number_length =
- PACKET_4BYTE_SEQUENCE_NUMBER;
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
SerializedPacket serialized = creator_.SerializePacket();
// The sequence number length will not change mid-packet.
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
@@ -174,7 +233,9 @@ TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnAckFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -191,7 +252,9 @@ TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnAckFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -200,22 +263,130 @@ TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) {
- creator_.options()->max_packets_per_fec_group = 6;
- ASSERT_FALSE(creator_.ShouldSendFec(false));
+TEST_P(QuicPacketCreatorTest, ChangeSequenceNumberLengthMidPacket) {
+ if (GetParam().version <= QUIC_VERSION_15) {
+ return;
+ }
+ // Changing the sequence number length with queued frames in the creator
+ // should hold the change until after any currently queued frames are
+ // serialized.
+
+ // Packet 1.
+ // Queue a frame in the creator.
+ EXPECT_FALSE(creator_.HasPendingFrames());
+ QuicFrame ack_frame = QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u)));
+ creator_.AddSavedFrame(ack_frame);
+
+ // Now change sequence number length.
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
+
+ // Add a STOP_WAITING frame since it contains a packet sequence number,
+ // whose length should be 1.
+ QuicStopWaitingFrame stop_waiting_frame;
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&stop_waiting_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
- frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u)));
+ // Ensure the packet is successfully created.
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ // Verify that header in transmitted packet has 1 byte sequence length.
+ QuicPacketHeader header;
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce(
+ DoAll(SaveArg<0>(&header), Return(true)));
+ EXPECT_CALL(framer_visitor_, OnAckFrame(_));
+ EXPECT_CALL(framer_visitor_, OnStopWaitingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ header.public_header.sequence_number_length);
+ delete serialized.packet;
+
+ // Packet 2.
+ EXPECT_FALSE(creator_.HasPendingFrames());
+ // Generate Packet 2 with one frame -- sequence number length should now
+ // change to 4 bytes.
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&stop_waiting_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ // Ensure the packet is successfully created.
+ serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ // Verify that header in transmitted packet has 4 byte sequence length.
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce(
+ DoAll(SaveArg<0>(&header), Return(true)));
+ EXPECT_CALL(framer_visitor_, OnStopWaitingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ header.public_header.sequence_number_length);
+
+ delete serialized.packet;
+ delete ack_frame.ack_frame;
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) {
+ // Test goal is to test the following sequence (P1 => generate Packet 1):
+ // P1 <change seq num length> P2 FEC,
+ // and we expect that sequence number length should not change until the end
+ // of the open FEC group.
+
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u))));
+
+ // Generate Packet 1.
creator_.AddSavedFrame(frames_[0]);
// Change the sequence number length mid-FEC group and it should not change.
- creator_.options()->send_sequence_number_length =
- PACKET_4BYTE_SEQUENCE_NUMBER;
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
SerializedPacket serialized = creator_.SerializePacket();
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_));
+ EXPECT_CALL(framer_visitor_, OnAckFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+
+ // Generate Packet 2.
+ creator_.AddSavedFrame(frames_[0]);
+ serialized = creator_.SerializePacket();
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_));
EXPECT_CALL(framer_visitor_, OnAckFrame(_));
@@ -224,17 +395,23 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) {
ProcessPacket(serialized.packet);
delete serialized.packet;
- ASSERT_FALSE(creator_.ShouldSendFec(false));
- ASSERT_TRUE(creator_.ShouldSendFec(true));
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ // Should return true since there are packets in the FEC group.
+ ASSERT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
+ // Force generation of FEC packet.
serialized = creator_.SerializeFec();
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
- ASSERT_EQ(2u, serialized.sequence_number);
+ ASSERT_EQ(3u, serialized.sequence_number);
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnFecData(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -249,19 +426,18 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
+TEST_P(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
// If the original packet sequence number length, the current sequence number
// length, and the configured send sequence number length are different, the
// retransmit must sent with the original length and the others do not change.
- creator_.options()->send_sequence_number_length =
- PACKET_4BYTE_SEQUENCE_NUMBER;
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
QuicPacketCreatorPeer::SetSequenceNumberLength(&creator_,
PACKET_2BYTE_SEQUENCE_NUMBER);
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
SerializedPacket serialized =
creator_.ReserializeAllFrames(frames_, PACKET_1BYTE_SEQUENCE_NUMBER);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
QuicPacketCreatorPeer::GetSequenceNumberLength(&creator_));
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
@@ -270,7 +446,9 @@ TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -279,7 +457,7 @@ TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) {
+TEST_P(QuicPacketCreatorTest, SerializeConnectionClose) {
QuicConnectionCloseFrame frame;
frame.error_code = QUIC_NO_ERROR;
frame.error_details = "error";
@@ -290,7 +468,9 @@ TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) {
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
@@ -299,7 +479,84 @@ TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) {
delete serialized.packet;
}
-TEST_F(QuicPacketCreatorTest, CreateStreamFrame) {
+TEST_P(QuicPacketCreatorTest, SwitchFecOnOffWithNoGroup) {
+ // Enable FEC protection.
+ creator_.set_max_packets_per_fec_group(6);
+ EXPECT_TRUE(creator_.IsFecEnabled());
+ EXPECT_FALSE(creator_.IsFecProtected());
+
+ // Turn on FEC protection.
+ creator_.StartFecProtectingPackets();
+ EXPECT_TRUE(creator_.IsFecProtected());
+ // We have no packets in the FEC group, so no FEC packet can be created.
+ EXPECT_FALSE(creator_.ShouldSendFec(/*force_close=*/true));
+ // Since no packets are in FEC group yet, we should be able to turn FEC
+ // off with no trouble.
+ creator_.StopFecProtectingPackets();
+ EXPECT_FALSE(creator_.IsFecProtected());
+}
+
+TEST_P(QuicPacketCreatorTest, SwitchFecOnOffWithGroupInProgress) {
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
+ frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
+ SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
+ delete frames_[0].stream_frame;
+ delete serialized.packet;
+
+ EXPECT_TRUE(creator_.IsFecProtected());
+ // We do not have enough packets in the FEC group to trigger an FEC packet.
+ EXPECT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ // Should return true since there are packets in the FEC group.
+ EXPECT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
+
+ // Switching FEC off should not change creator state, since there is an
+ // FEC packet under construction.
+ EXPECT_DFATAL(creator_.StopFecProtectingPackets(),
+ "Cannot stop FEC protection with open FEC group.");
+ EXPECT_TRUE(creator_.IsFecProtected());
+ // Confirm that FEC packet is still under construction.
+ EXPECT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
+
+ serialized = creator_.SerializeFec();
+ delete serialized.packet;
+
+ // Switching FEC on/off should work now.
+ creator_.StopFecProtectingPackets();
+ EXPECT_FALSE(creator_.IsFecProtected());
+ creator_.StartFecProtectingPackets();
+ EXPECT_TRUE(creator_.IsFecProtected());
+}
+
+TEST_P(QuicPacketCreatorTest, SwitchFecOnWithStreamFrameQueued) {
+ // Add a stream frame to the creator.
+ QuicFrame frame;
+ size_t consumed = creator_.CreateStreamFrame(
+ 1u, MakeIOVector("test"), 0u, false, &frame);
+ EXPECT_EQ(4u, consumed);
+ ASSERT_TRUE(frame.stream_frame);
+ EXPECT_TRUE(creator_.AddSavedFrame(frame));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ creator_.set_max_packets_per_fec_group(6);
+ EXPECT_TRUE(creator_.IsFecEnabled());
+ EXPECT_DFATAL(creator_.StartFecProtectingPackets(),
+ "Cannot start FEC protection with pending frames.");
+ EXPECT_FALSE(creator_.IsFecProtected());
+
+ // Serialize packet for transmission.
+ SerializedPacket serialized = creator_.SerializePacket();
+ delete serialized.packet;
+ delete serialized.retransmittable_frames;
+ EXPECT_FALSE(creator_.HasPendingFrames());
+
+ // Since all pending frames have been serialized, turning FEC on should work.
+ creator_.StartFecProtectingPackets();
+ EXPECT_TRUE(creator_.IsFecProtected());
+}
+
+TEST_P(QuicPacketCreatorTest, CreateStreamFrame) {
QuicFrame frame;
size_t consumed = creator_.CreateStreamFrame(1u, MakeIOVector("test"), 0u,
false, &frame);
@@ -308,7 +565,7 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrame) {
delete frame.stream_frame;
}
-TEST_F(QuicPacketCreatorTest, CreateStreamFrameFin) {
+TEST_P(QuicPacketCreatorTest, CreateStreamFrameFin) {
QuicFrame frame;
size_t consumed = creator_.CreateStreamFrame(1u, MakeIOVector("test"), 10u,
true, &frame);
@@ -317,7 +574,7 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFin) {
delete frame.stream_frame;
}
-TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) {
+TEST_P(QuicPacketCreatorTest, CreateStreamFrameFinOnly) {
QuicFrame frame;
size_t consumed = creator_.CreateStreamFrame(1u, IOVector(), 0u, true,
&frame);
@@ -326,17 +583,20 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) {
delete frame.stream_frame;
}
-TEST_F(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
- const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead();
+TEST_P(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead();
for (size_t i = overhead; i < overhead + 100; ++i) {
- creator_.options()->max_packet_length = i;
- const bool should_have_room = i > overhead + GetStreamFrameOverhead();
- ASSERT_EQ(should_have_room,
- creator_.HasRoomForStreamFrame(kStreamId, kOffset));
+ creator_.set_max_packet_length(i);
+ const bool should_have_room = i > overhead + GetStreamFrameOverhead(
+ NOT_IN_FEC_GROUP);
+ ASSERT_EQ(should_have_room, creator_.HasRoomForStreamFrame(
+ kClientDataStreamId1, kOffset));
if (should_have_room) {
QuicFrame frame;
size_t bytes_consumed = creator_.CreateStreamFrame(
- kStreamId, MakeIOVector("testdata"), kOffset, false, &frame);
+ kClientDataStreamId1, MakeIOVector("testdata"), kOffset, false,
+ &frame);
EXPECT_LT(0u, bytes_consumed);
ASSERT_TRUE(creator_.AddSavedFrame(frame));
SerializedPacket serialized_packet = creator_.SerializePacket();
@@ -347,10 +607,10 @@ TEST_F(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
}
}
-TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) {
+TEST_P(QuicPacketCreatorTest, StreamFrameConsumption) {
// Compute the total overhead for a single frame in packet.
- const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead()
- + GetStreamFrameOverhead();
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP);
size_t capacity = kDefaultMaxPacketSize - overhead;
// Now, test various sizes around this size.
for (int delta = -5; delta <= 5; ++delta) {
@@ -358,12 +618,13 @@ TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) {
size_t bytes_free = delta > 0 ? 0 : 0 - delta;
QuicFrame frame;
size_t bytes_consumed = creator_.CreateStreamFrame(
- kStreamId, MakeIOVector(data), kOffset, false, &frame);
+ kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame);
EXPECT_EQ(capacity - bytes_free, bytes_consumed);
ASSERT_TRUE(creator_.AddSavedFrame(frame));
// BytesFree() returns bytes available for the next frame, which will
// be two bytes smaller since the stream frame would need to be grown.
+ EXPECT_EQ(2u, creator_.ExpansionOnNewFrame());
size_t expected_bytes_free = bytes_free < 3 ? 0 : bytes_free - 2;
EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta;
SerializedPacket serialized_packet = creator_.SerializePacket();
@@ -373,10 +634,40 @@ TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) {
}
}
-TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
+TEST_P(QuicPacketCreatorTest, StreamFrameConsumptionWithFec) {
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
// Compute the total overhead for a single frame in packet.
- const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead()
- + GetStreamFrameOverhead();
+ const size_t overhead = GetPacketHeaderOverhead(IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(IN_FEC_GROUP);
+ size_t capacity = kDefaultMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+ QuicFrame frame;
+ size_t bytes_consumed = creator_.CreateStreamFrame(
+ kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame);
+ EXPECT_EQ(capacity - bytes_free, bytes_consumed);
+
+ ASSERT_TRUE(creator_.AddSavedFrame(frame));
+ // BytesFree() returns bytes available for the next frame. Since stream
+ // frame does not grow for FEC protected packets, this should be the same
+ // as bytes_free (bound by 0).
+ EXPECT_EQ(0u, creator_.ExpansionOnNewFrame());
+ size_t expected_bytes_free = bytes_free > 0 ? bytes_free : 0;
+ EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta;
+ SerializedPacket serialized_packet = creator_.SerializePacket();
+ ASSERT_TRUE(serialized_packet.packet);
+ delete serialized_packet.packet;
+ delete serialized_packet.retransmittable_frames;
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP);
ASSERT_GT(kMaxPacketSize, overhead);
size_t capacity = kDefaultMaxPacketSize - overhead;
// Now, test various sizes around this size.
@@ -386,7 +677,7 @@ TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
QuicFrame frame;
size_t bytes_consumed = creator_.CreateStreamFrame(
- kStreamId, MakeIOVector(data), kOffset, false, &frame);
+ kCryptoStreamId, MakeIOVector(data), kOffset, false, &frame);
EXPECT_LT(0u, bytes_consumed);
ASSERT_TRUE(creator_.AddSavedFrame(frame));
SerializedPacket serialized_packet = creator_.SerializePacket();
@@ -406,10 +697,10 @@ TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
}
}
-TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
+TEST_P(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
// Compute the total overhead for a single frame in packet.
- const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead()
- + GetStreamFrameOverhead();
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP);
ASSERT_GT(kDefaultMaxPacketSize, overhead);
size_t capacity = kDefaultMaxPacketSize - overhead;
// Now, test various sizes around this size.
@@ -419,7 +710,7 @@ TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
QuicFrame frame;
size_t bytes_consumed = creator_.CreateStreamFrame(
- kStreamId + 2, MakeIOVector(data), kOffset, false, &frame);
+ kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame);
EXPECT_LT(0u, bytes_consumed);
ASSERT_TRUE(creator_.AddSavedFrame(frame));
SerializedPacket serialized_packet = creator_.SerializePacket();
@@ -436,8 +727,8 @@ TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
}
}
-TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) {
- QuicPacketCreatorPeer::SetIsServer(&creator_, true);
+TEST_P(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) {
+ QuicFramerPeer::SetIsServer(&client_framer_, true);
QuicVersionVector versions;
versions.push_back(test::QuicVersionMax());
scoped_ptr<QuicEncryptedPacket> encrypted(
@@ -446,64 +737,66 @@ TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnVersionNegotiationPacket(_));
}
- client_framer_.ProcessPacket(*encrypted.get());
+ QuicFramerPeer::SetIsServer(&client_framer_, false);
+ client_framer_.ProcessPacket(*encrypted);
}
-TEST_F(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) {
+TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) {
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.set_sequence_number(64);
creator_.UpdateSequenceNumberLength(2, 10000);
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.set_sequence_number(64 * 256);
creator_.UpdateSequenceNumberLength(2, 10000);
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.set_sequence_number(64 * 256 * 256);
creator_.UpdateSequenceNumberLength(2, 10000);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.set_sequence_number(GG_UINT64_C(64) * 256 * 256 * 256 * 256);
creator_.UpdateSequenceNumberLength(2, 10000);
EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
}
-TEST_F(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthBandwidth) {
+TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthBandwidth) {
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.UpdateSequenceNumberLength(1, 10000);
EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.UpdateSequenceNumberLength(1, 10000 * 256);
EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.UpdateSequenceNumberLength(1, 10000 * 256 * 256);
EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
creator_.UpdateSequenceNumberLength(
1, GG_UINT64_C(1000) * 256 * 256 * 256 * 256);
EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
- creator_.options()->send_sequence_number_length);
+ creator_.next_sequence_number_length());
}
-TEST_F(QuicPacketCreatorTest, CreateStreamFrameWithNotifier) {
+TEST_P(QuicPacketCreatorTest, CreateStreamFrameWithNotifier) {
// Ensure that if CreateStreamFrame does not consume any data (e.g. a FIN only
// frame) then any QuicAckNotifier that is passed in still gets attached to
// the frame.
- MockAckNotifierDelegate delegate;
- QuicAckNotifier notifier(&delegate);
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ QuicAckNotifier notifier(delegate.get());
QuicFrame frame;
IOVector empty_iovector;
bool fin = true;
@@ -514,12 +807,8 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameWithNotifier) {
delete frame.stream_frame;
}
-INSTANTIATE_TEST_CASE_P(ToggleVersionSerialization,
- QuicPacketCreatorTest,
- ::testing::Values(false, true));
-
TEST_P(QuicPacketCreatorTest, SerializeFrame) {
- if (!GetParam()) {
+ if (!GetParam().version_serialization) {
creator_.StopSendingVersion();
}
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
@@ -530,27 +819,30 @@ TEST_P(QuicPacketCreatorTest, SerializeFrame) {
{
InSequence s;
EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce(
DoAll(SaveArg<0>(&header), Return(true)));
EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
EXPECT_CALL(framer_visitor_, OnPacketComplete());
}
ProcessPacket(serialized.packet);
- EXPECT_EQ(GetParam(), header.public_header.version_flag);
+ EXPECT_EQ(GetParam().version_serialization,
+ header.public_header.version_flag);
delete serialized.packet;
}
TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) {
- if (!GetParam()) {
+ if (!GetParam().version_serialization) {
creator_.StopSendingVersion();
}
// A string larger than fits into a frame.
size_t payload_length;
- creator_.options()->max_packet_length = GetPacketLengthForOneStream(
+ creator_.set_max_packet_length(GetPacketLengthForOneStream(
client_framer_.version(),
QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
- PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length);
+ PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length));
QuicFrame frame;
const string too_long_payload(payload_length * 2, 'a');
size_t consumed = creator_.CreateStreamFrame(
@@ -562,21 +854,21 @@ TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) {
}
TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) {
- if (!GetParam()) {
+ if (!GetParam().version_serialization) {
creator_.StopSendingVersion();
}
const size_t max_plaintext_size =
- client_framer_.GetMaxPlaintextSize(creator_.options()->max_packet_length);
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
EXPECT_FALSE(creator_.HasPendingFrames());
EXPECT_EQ(max_plaintext_size -
GetPacketHeaderSize(
- creator_.options()->send_guid_length,
+ creator_.connection_id_length(),
QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
creator_.BytesFree());
// Add a variety of frame types and then a padding frame.
- QuicAckFrame ack_frame(0u, QuicTime::Zero(), 0u);
+ QuicAckFrame ack_frame(MakeAckFrame(0u, 0u));
EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
EXPECT_TRUE(creator_.HasPendingFrames());
@@ -614,14 +906,94 @@ TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) {
EXPECT_FALSE(creator_.HasPendingFrames());
EXPECT_EQ(max_plaintext_size -
GetPacketHeaderSize(
- creator_.options()->send_guid_length,
+ creator_.connection_id_length(),
QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
PACKET_1BYTE_SEQUENCE_NUMBER,
NOT_IN_FEC_GROUP),
creator_.BytesFree());
}
-TEST_F(QuicPacketCreatorTest, EntropyFlag) {
+TEST_P(QuicPacketCreatorTest, SerializeTruncatedAckFrameWithLargePacketSize) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ creator_.set_max_packet_length(kMaxPacketSize);
+ const size_t max_plaintext_size =
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
+
+ // Serialized length of ack frame with 2000 nack ranges should be limited by
+ // the number of nack ranges that can be fit in an ack frame.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(2000u, 0u);
+ size_t frame_len = client_framer_.GetSerializedFrameLength(
+ QuicFrame(&ack_frame), creator_.BytesFree(), true, true,
+ NOT_IN_FEC_GROUP, PACKET_1BYTE_SEQUENCE_NUMBER);
+ EXPECT_GT(creator_.BytesFree(), frame_len);
+ EXPECT_GT(max_plaintext_size, creator_.PacketSize());
+
+ // Add ack frame to creator.
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_GT(max_plaintext_size, creator_.PacketSize());
+ EXPECT_LT(0u, creator_.BytesFree());
+
+ // Make sure that an additional stream frame can be added to the packet.
+ QuicFrame stream_frame;
+ size_t consumed = creator_.CreateStreamFrame(
+ 2u, MakeIOVector("test"), 0u, false, &stream_frame);
+ EXPECT_EQ(4u, consumed);
+ ASSERT_TRUE(stream_frame.stream_frame);
+ EXPECT_TRUE(creator_.AddSavedFrame(stream_frame));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ // Ensure the packet is successfully created, and the packet size estimate
+ // matches the serialized packet length.
+ EXPECT_CALL(entropy_calculator_,
+ EntropyHash(_)).WillOnce(testing::Return(0));
+ size_t est_packet_size = creator_.PacketSize();
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_EQ(est_packet_size, serialized.packet->length());
+ delete serialized.retransmittable_frames;
+ delete serialized.packet;
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeTruncatedAckFrameWithSmallPacketSize) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ creator_.set_max_packet_length(500u);
+
+ const size_t max_plaintext_size =
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
+ EXPECT_EQ(max_plaintext_size - creator_.PacketSize(), creator_.BytesFree());
+
+ // Serialized length of ack frame with 2000 nack ranges should be limited by
+ // the packet size.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(2000u, 0u);
+ size_t frame_len = client_framer_.GetSerializedFrameLength(
+ QuicFrame(&ack_frame), creator_.BytesFree(), true, true,
+ NOT_IN_FEC_GROUP, PACKET_1BYTE_SEQUENCE_NUMBER);
+ EXPECT_EQ(creator_.BytesFree(), frame_len);
+
+ // Add ack frame to creator.
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_EQ(max_plaintext_size, creator_.PacketSize());
+ EXPECT_EQ(0u, creator_.BytesFree());
+
+ // Ensure the packet is successfully created, and the packet size estimate
+ // may not match the serialized packet length.
+ EXPECT_CALL(entropy_calculator_,
+ EntropyHash(_)).WillOnce(Return(0));
+ size_t est_packet_size = creator_.PacketSize();
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_GE(est_packet_size, serialized.packet->length());
+ delete serialized.packet;
+}
+
+
+TEST_P(QuicPacketCreatorTest, EntropyFlag) {
frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
for (int i = 0; i < 2; ++i) {
diff --git a/chromium/net/quic/quic_packet_generator.cc b/chromium/net/quic/quic_packet_generator.cc
index 3785f7d2f8d..9e7473ebbf0 100644
--- a/chromium/net/quic/quic_packet_generator.cc
+++ b/chromium/net/quic/quic_packet_generator.cc
@@ -4,6 +4,7 @@
#include "net/quic/quic_packet_generator.h"
+#include "base/basictypes.h"
#include "base/logging.h"
#include "net/quic/quic_fec_group.h"
#include "net/quic/quic_utils.h"
@@ -14,15 +15,18 @@ namespace net {
class QuicAckNotifier;
-QuicPacketGenerator::QuicPacketGenerator(DelegateInterface* delegate,
- DebugDelegateInterface* debug_delegate,
- QuicPacketCreator* creator)
+QuicPacketGenerator::QuicPacketGenerator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator,
+ DelegateInterface* delegate)
: delegate_(delegate),
- debug_delegate_(debug_delegate),
- packet_creator_(creator),
+ debug_delegate_(NULL),
+ packet_creator_(connection_id, framer, random_generator),
batch_mode_(false),
+ should_fec_protect_(false),
should_send_ack_(false),
- should_send_feedback_(false) {
+ should_send_feedback_(false),
+ should_send_stop_waiting_(false) {
}
QuicPacketGenerator::~QuicPacketGenerator() {
@@ -50,15 +54,34 @@ QuicPacketGenerator::~QuicPacketGenerator() {
case GOAWAY_FRAME:
delete it->goaway_frame;
break;
+ case WINDOW_UPDATE_FRAME:
+ delete it->window_update_frame;
+ break;
+ case BLOCKED_FRAME:
+ delete it->blocked_frame;
+ break;
+ case STOP_WAITING_FRAME:
+ delete it->stop_waiting_frame;
+ break;
+ case PING_FRAME:
+ delete it->ping_frame;
+ break;
case NUM_FRAME_TYPES:
DCHECK(false) << "Cannot delete type: " << it->type;
}
}
}
-void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback) {
+void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback,
+ bool also_send_stop_waiting) {
should_send_ack_ = true;
should_send_feedback_ = also_send_feedback;
+ should_send_stop_waiting_ = also_send_stop_waiting;
+ SendQueuedFrames(false);
+}
+
+void QuicPacketGenerator::SetShouldSendStopWaiting() {
+ should_send_stop_waiting_ = true;
SendQueuedFrames(false);
}
@@ -71,20 +94,26 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
const IOVector& data_to_write,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier* notifier) {
IsHandshake handshake = id == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE;
- // The caller should have flushed pending frames before sending handshake
- // messages.
- DCHECK(handshake == NOT_HANDSHAKE || !HasPendingFrames());
- SendQueuedFrames(false);
+ // To make reasoning about crypto frames easier, we don't combine them with
+ // other retransmittable frames in a single packet.
+ const bool flush = handshake == IS_HANDSHAKE &&
+ packet_creator_.HasPendingRetransmittableFrames();
+ SendQueuedFrames(flush);
size_t total_bytes_consumed = 0;
bool fin_consumed = false;
- if (!packet_creator_->HasRoomForStreamFrame(id, offset)) {
+ if (!packet_creator_.HasRoomForStreamFrame(id, offset)) {
SerializeAndSendPacket();
}
+ if (fec_protection == MUST_FEC_PROTECT) {
+ MaybeStartFecProtection();
+ }
+
IOVector data = data_to_write;
size_t data_size = data.TotalBufferSize();
while (delegate_->ShouldGeneratePacket(NOT_RETRANSMISSION,
@@ -93,10 +122,10 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
size_t bytes_consumed;
if (notifier != NULL) {
// We want to track which packet this stream frame ends up in.
- bytes_consumed = packet_creator_->CreateStreamFrameWithNotifier(
+ bytes_consumed = packet_creator_.CreateStreamFrameWithNotifier(
id, data, offset + total_bytes_consumed, fin, notifier, &frame);
} else {
- bytes_consumed = packet_creator_->CreateStreamFrame(
+ bytes_consumed = packet_creator_.CreateStreamFrame(
id, data, offset + total_bytes_consumed, fin, &frame);
}
if (!AddFrame(frame)) {
@@ -110,10 +139,10 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
total_bytes_consumed += bytes_consumed;
fin_consumed = fin && total_bytes_consumed == data_size;
data.Consume(bytes_consumed);
- DCHECK(data.Empty() || packet_creator_->BytesFree() == 0u);
+ DCHECK(data.Empty() || packet_creator_.BytesFree() == 0u);
// TODO(ianswett): Restore packet reordering.
- if (!InBatchMode() || !packet_creator_->HasRoomForStreamFrame(id, offset)) {
+ if (!InBatchMode() || !packet_creator_.HasRoomForStreamFrame(id, offset)) {
SerializeAndSendPacket();
}
@@ -121,27 +150,33 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
// We're done writing the data. Exit the loop.
// We don't make this a precondition because we could have 0 bytes of data
// if we're simply writing a fin.
+ if (fec_protection == MUST_FEC_PROTECT) {
+ // Turn off FEC protection when we're done writing protected data.
+ DVLOG(1) << "Turning FEC protection OFF";
+ should_fec_protect_ = false;
+ }
break;
}
}
- // Ensure the FEC group is closed at the end of this method if not in batch
- // mode.
- if (!InBatchMode() && packet_creator_->ShouldSendFec(true)) {
- SerializedPacket serialized_fec = packet_creator_->SerializeFec();
- DCHECK(serialized_fec.packet);
- delegate_->OnSerializedPacket(serialized_fec);
+ // Don't allow the handshake to be bundled with other retransmittable frames.
+ if (handshake == IS_HANDSHAKE) {
+ SendQueuedFrames(true);
}
- DCHECK(InBatchMode() || !packet_creator_->HasPendingFrames());
+ // Try to close FEC group since we've either run out of data to send or we're
+ // blocked. If not in batch mode, force close the group.
+ MaybeSendFecPacketAndCloseGroup(!InBatchMode());
+
+ DCHECK(InBatchMode() || !packet_creator_.HasPendingFrames());
return QuicConsumedData(total_bytes_consumed, fin_consumed);
}
bool QuicPacketGenerator::CanSendWithNextPendingFrameAddition() const {
DCHECK(HasPendingFrames());
HasRetransmittableData retransmittable =
- (should_send_ack_ || should_send_feedback_) ? NO_RETRANSMITTABLE_DATA
- : HAS_RETRANSMITTABLE_DATA;
+ (should_send_ack_ || should_send_feedback_ || should_send_stop_waiting_)
+ ? NO_RETRANSMITTABLE_DATA : HAS_RETRANSMITTABLE_DATA;
if (retransmittable == HAS_RETRANSMITTABLE_DATA) {
DCHECK(!queued_control_frames_.empty()); // These are retransmittable.
}
@@ -160,17 +195,58 @@ void QuicPacketGenerator::SendQueuedFrames(bool flush) {
}
if (!InBatchMode() || flush) {
- if (packet_creator_->HasPendingFrames()) {
+ if (packet_creator_.HasPendingFrames()) {
SerializeAndSendPacket();
}
-
// Ensure the FEC group is closed at the end of this method unless other
// writes are pending.
- if (packet_creator_->ShouldSendFec(true)) {
- SerializedPacket serialized_fec = packet_creator_->SerializeFec();
- DCHECK(serialized_fec.packet);
- delegate_->OnSerializedPacket(serialized_fec);
- }
+ MaybeSendFecPacketAndCloseGroup(true);
+ }
+}
+
+void QuicPacketGenerator::MaybeStartFecProtection() {
+ if (!packet_creator_.IsFecEnabled()) {
+ return;
+ }
+ DVLOG(1) << "Turning FEC protection ON";
+ should_fec_protect_ = true;
+ if (packet_creator_.IsFecProtected()) {
+ // Only start creator's FEC protection if not already on.
+ return;
+ }
+ if (HasQueuedFrames()) {
+ // TODO(jri): This currently requires that the generator flush out any
+ // pending frames when FEC protection is turned on. If current packet can be
+ // converted to an FEC protected packet, do it. This will require the
+ // generator to check if the resulting expansion still allows the incoming
+ // frame to be added to the packet.
+ SendQueuedFrames(true);
+ }
+ packet_creator_.StartFecProtectingPackets();
+ DCHECK(packet_creator_.IsFecProtected());
+}
+
+void QuicPacketGenerator::MaybeSendFecPacketAndCloseGroup(bool force) {
+ if (!packet_creator_.IsFecProtected() ||
+ packet_creator_.HasPendingFrames()) {
+ return;
+ }
+
+ if (packet_creator_.ShouldSendFec(force)) {
+ // TODO(jri): SerializeFec can return a NULL packet, and this should
+ // cause an early return, with a call to
+ // delegate_->OnPacketGenerationError.
+ SerializedPacket serialized_fec = packet_creator_.SerializeFec();
+ DCHECK(serialized_fec.packet);
+ delegate_->OnSerializedPacket(serialized_fec);
+ }
+
+ // Turn FEC protection off if the creator does not have an FEC group open.
+ // Note: We only wait until the frames queued in the creator are flushed;
+ // pending frames in the generator will not keep us from turning FEC off.
+ if (!should_fec_protect_ && !packet_creator_.IsFecGroupOpen()) {
+ packet_creator_.StopFecProtectingPackets();
+ DCHECK(!packet_creator_.IsFecProtected());
}
}
@@ -192,12 +268,12 @@ void QuicPacketGenerator::FlushAllQueuedFrames() {
}
bool QuicPacketGenerator::HasQueuedFrames() const {
- return packet_creator_->HasPendingFrames() || HasPendingFrames();
+ return packet_creator_.HasPendingFrames() || HasPendingFrames();
}
bool QuicPacketGenerator::HasPendingFrames() const {
return should_send_ack_ || should_send_feedback_ ||
- !queued_control_frames_.empty();
+ should_send_stop_waiting_ || !queued_control_frames_.empty();
}
bool QuicPacketGenerator::AddNextPendingFrame() {
@@ -219,9 +295,18 @@ bool QuicPacketGenerator::AddNextPendingFrame() {
return !should_send_feedback_;
}
- if (queued_control_frames_.empty()) {
- LOG(DFATAL) << "AddNextPendingFrame called with no queued control frames.";
+ if (should_send_stop_waiting_) {
+ pending_stop_waiting_frame_.reset(delegate_->CreateStopWaitingFrame());
+ // If we can't this add the frame now, then we still need to do so later.
+ should_send_stop_waiting_ =
+ !AddFrame(QuicFrame(pending_stop_waiting_frame_.get()));
+ // Return success if we have cleared out this flag (i.e., added the frame).
+ // If we still need to send, then the frame is full, and we have failed.
+ return !should_send_stop_waiting_;
}
+
+ LOG_IF(DFATAL, queued_control_frames_.empty())
+ << "AddNextPendingFrame called with no queued control frames.";
if (!AddFrame(queued_control_frames_.back())) {
// Packet was full.
return false;
@@ -231,7 +316,7 @@ bool QuicPacketGenerator::AddNextPendingFrame() {
}
bool QuicPacketGenerator::AddFrame(const QuicFrame& frame) {
- bool success = packet_creator_->AddSavedFrame(frame);
+ bool success = packet_creator_.AddSavedFrame(frame);
if (success && debug_delegate_) {
debug_delegate_->OnFrameAddedToPacket(frame);
}
@@ -239,15 +324,48 @@ bool QuicPacketGenerator::AddFrame(const QuicFrame& frame) {
}
void QuicPacketGenerator::SerializeAndSendPacket() {
- SerializedPacket serialized_packet = packet_creator_->SerializePacket();
+ SerializedPacket serialized_packet = packet_creator_.SerializePacket();
DCHECK(serialized_packet.packet);
delegate_->OnSerializedPacket(serialized_packet);
+ MaybeSendFecPacketAndCloseGroup(false);
+}
- if (packet_creator_->ShouldSendFec(false)) {
- SerializedPacket serialized_fec = packet_creator_->SerializeFec();
- DCHECK(serialized_fec.packet);
- delegate_->OnSerializedPacket(serialized_fec);
- }
+void QuicPacketGenerator::StopSendingVersion() {
+ packet_creator_.StopSendingVersion();
+}
+
+QuicPacketSequenceNumber QuicPacketGenerator::sequence_number() const {
+ return packet_creator_.sequence_number();
+}
+
+size_t QuicPacketGenerator::max_packet_length() const {
+ return packet_creator_.max_packet_length();
+}
+
+void QuicPacketGenerator::set_max_packet_length(size_t length) {
+ packet_creator_.set_max_packet_length(length);
+}
+
+QuicEncryptedPacket* QuicPacketGenerator::SerializeVersionNegotiationPacket(
+ const QuicVersionVector& supported_versions) {
+ return packet_creator_.SerializeVersionNegotiationPacket(supported_versions);
+}
+
+SerializedPacket QuicPacketGenerator::ReserializeAllFrames(
+ const QuicFrames& frames,
+ QuicSequenceNumberLength original_length) {
+ return packet_creator_.ReserializeAllFrames(frames, original_length);
+}
+
+void QuicPacketGenerator::UpdateSequenceNumberLength(
+ QuicPacketSequenceNumber least_packet_awaited_by_peer,
+ QuicByteCount congestion_window) {
+ return packet_creator_.UpdateSequenceNumberLength(
+ least_packet_awaited_by_peer, congestion_window);
+}
+
+void QuicPacketGenerator::set_encryption_level(EncryptionLevel level) {
+ packet_creator_.set_encryption_level(level);
}
} // namespace net
diff --git a/chromium/net/quic/quic_packet_generator.h b/chromium/net/quic/quic_packet_generator.h
index 1ff04882ae1..d408aeab31b 100644
--- a/chromium/net/quic/quic_packet_generator.h
+++ b/chromium/net/quic/quic_packet_generator.h
@@ -54,9 +54,14 @@
#define NET_QUIC_QUIC_PACKET_GENERATOR_H_
#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_types.h"
namespace net {
+namespace test {
+class QuicPacketGeneratorPeer;
+} // namespace test
+
class QuicAckNotifier;
class NET_EXPORT_PRIVATE QuicPacketGenerator {
@@ -69,6 +74,7 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
IsHandshake handshake) = 0;
virtual QuicAckFrame* CreateAckFrame() = 0;
virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() = 0;
+ virtual QuicStopWaitingFrame* CreateStopWaitingFrame() = 0;
// Takes ownership of |packet.packet| and |packet.retransmittable_frames|.
virtual bool OnSerializedPacket(const SerializedPacket& packet) = 0;
virtual void CloseConnection(QuicErrorCode error, bool from_peer) = 0;
@@ -77,25 +83,33 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
// Interface which gets callbacks from the QuicPacketGenerator at interesting
// points. Implementations must not mutate the state of the generator
// as a result of these callbacks.
- class NET_EXPORT_PRIVATE DebugDelegateInterface {
+ class NET_EXPORT_PRIVATE DebugDelegate {
public:
- virtual ~DebugDelegateInterface() {}
+ virtual ~DebugDelegate() {}
// Called when a frame has been added to the current packet.
- virtual void OnFrameAddedToPacket(const QuicFrame& frame) = 0;
+ virtual void OnFrameAddedToPacket(const QuicFrame& frame) {}
};
- QuicPacketGenerator(DelegateInterface* delegate,
- DebugDelegateInterface* debug_delegate,
- QuicPacketCreator* creator);
+ QuicPacketGenerator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator,
+ DelegateInterface* delegate);
virtual ~QuicPacketGenerator();
// Indicates that an ACK frame should be sent. If |also_send_feedback| is
// true, then it also indicates a CONGESTION_FEEDBACK frame should be sent.
+ // If |also_send_stop_waiting| is true, then it also indicates that a
+ // STOP_WAITING frame should be sent as well.
// The contents of the frame(s) will be generated via a call to the delegates
// CreateAckFrame() and CreateFeedbackFrame() when the packet is serialized.
- void SetShouldSendAck(bool also_send_feedback);
+ void SetShouldSendAck(bool also_send_feedback,
+ bool also_send_stop_waiting);
+
+ // Indicates that a STOP_WAITING frame should be sent.
+ void SetShouldSendStopWaiting();
+
void AddControlFrame(const QuicFrame& frame);
// Given some data, may consume part or all of it and pass it to the
@@ -109,6 +123,7 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier* notifier);
// Indicates whether batch mode is currently enabled.
@@ -123,11 +138,63 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
bool HasQueuedFrames() const;
- void set_debug_delegate(DebugDelegateInterface* debug_delegate) {
+ // Makes the framer not serialize the protocol version in sent packets.
+ void StopSendingVersion();
+
+ // Creates a version negotiation packet which supports |supported_versions|.
+ // Caller owns the created packet. Also, sets the entropy hash of the
+ // serialized packet to a random bool and returns that value as a member of
+ // SerializedPacket.
+ QuicEncryptedPacket* SerializeVersionNegotiationPacket(
+ const QuicVersionVector& supported_versions);
+
+
+ // Re-serializes frames with the original packet's sequence number length.
+ // Used for retransmitting packets to ensure they aren't too long.
+ // Caller must ensure that any open FEC group is closed before calling this
+ // method.
+ SerializedPacket ReserializeAllFrames(
+ const QuicFrames& frames,
+ QuicSequenceNumberLength original_length);
+
+ // Update the sequence number length to use in future packets as soon as it
+ // can be safely changed.
+ void UpdateSequenceNumberLength(
+ QuicPacketSequenceNumber least_packet_awaited_by_peer,
+ QuicByteCount congestion_window);
+
+ // Sets the encryption level that will be applied to new packets.
+ void set_encryption_level(EncryptionLevel level);
+
+ // Sequence number of the last created packet, or 0 if no packets have been
+ // created.
+ QuicPacketSequenceNumber sequence_number() const;
+
+ size_t max_packet_length() const;
+
+ void set_max_packet_length(size_t length);
+
+ void set_debug_delegate(DebugDelegate* debug_delegate) {
debug_delegate_ = debug_delegate;
}
private:
+ friend class test::QuicPacketGeneratorPeer;
+
+ // Turn on FEC protection for subsequent packets in the generator.
+ // If no FEC group is currently open in the creator, this method flushes any
+ // queued frames in the generator and in the creator, and it then turns FEC on
+ // in the creator. This method may be called with an open FEC group in the
+ // creator, in which case, only the generator's state is altered.
+ void MaybeStartFecProtection();
+
+ // Serializes and calls the delegate on an FEC packet if one was under
+ // construction in the creator. When |force| is false, it relies on the
+ // creator being ready to send an FEC packet, otherwise FEC packet is sent
+ // as long as one is under construction in the creator. Also tries to turns
+ // off FEC protection in the creator if it's off in the generator.
+ void MaybeSendFecPacketAndCloseGroup(bool force);
+
void SendQueuedFrames(bool flush);
// Test to see if we have pending ack, feedback, or control frames.
@@ -144,23 +211,29 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
void SerializeAndSendPacket();
DelegateInterface* delegate_;
- DebugDelegateInterface* debug_delegate_;
+ DebugDelegate* debug_delegate_;
- QuicPacketCreator* packet_creator_;
+ QuicPacketCreator packet_creator_;
QuicFrames queued_control_frames_;
// True if batch mode is currently enabled.
bool batch_mode_;
+ // True if FEC protection is on. The creator may have an open FEC group even
+ // if this variable is false.
+ bool should_fec_protect_;
+
// Flags to indicate the need for just-in-time construction of a frame.
bool should_send_ack_;
bool should_send_feedback_;
+ bool should_send_stop_waiting_;
// If we put a non-retransmittable frame (namley ack or feedback frame) in
// this packet, then we have to hold a reference to it until we flush (and
// serialize it). Retransmittable frames are referenced elsewhere so that they
// can later be (optionally) retransmitted.
scoped_ptr<QuicAckFrame> pending_ack_frame_;
scoped_ptr<QuicCongestionFeedbackFrame> pending_feedback_frame_;
+ scoped_ptr<QuicStopWaitingFrame> pending_stop_waiting_frame_;
DISALLOW_COPY_AND_ASSIGN(QuicPacketGenerator);
};
diff --git a/chromium/net/quic/quic_packet_generator_test.cc b/chromium/net/quic/quic_packet_generator_test.cc
index 8907db95a12..331b1834667 100644
--- a/chromium/net/quic/quic_packet_generator_test.cc
+++ b/chromium/net/quic/quic_packet_generator_test.cc
@@ -11,6 +11,8 @@
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/quic/test_tools/quic_packet_generator_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/simple_quic_framer.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -31,7 +33,7 @@ namespace {
class MockDelegate : public QuicPacketGenerator::DelegateInterface {
public:
MockDelegate() {}
- virtual ~MockDelegate() {}
+ virtual ~MockDelegate() OVERRIDE {}
MOCK_METHOD3(ShouldGeneratePacket,
bool(TransmissionType transmission_type,
@@ -39,6 +41,7 @@ class MockDelegate : public QuicPacketGenerator::DelegateInterface {
IsHandshake handshake));
MOCK_METHOD0(CreateAckFrame, QuicAckFrame*());
MOCK_METHOD0(CreateFeedbackFrame, QuicCongestionFeedbackFrame*());
+ MOCK_METHOD0(CreateStopWaitingFrame, QuicStopWaitingFrame*());
MOCK_METHOD1(OnSerializedPacket, bool(const SerializedPacket& packet));
MOCK_METHOD2(CloseConnection, void(QuicErrorCode, bool));
@@ -81,6 +84,7 @@ struct PacketContents {
num_feedback_frames(0),
num_goaway_frames(0),
num_rst_stream_frames(0),
+ num_stop_waiting_frames(0),
num_stream_frames(0),
fec_group(0) {
}
@@ -90,6 +94,7 @@ struct PacketContents {
size_t num_feedback_frames;
size_t num_goaway_frames;
size_t num_rst_stream_frames;
+ size_t num_stop_waiting_frames;
size_t num_stream_frames;
QuicFecGroupNumber fec_group;
@@ -101,16 +106,18 @@ class QuicPacketGeneratorTest : public ::testing::Test {
protected:
QuicPacketGeneratorTest()
: framer_(QuicSupportedVersions(), QuicTime::Zero(), false),
- creator_(42, &framer_, &random_, false),
- generator_(&delegate_, NULL, &creator_),
+ generator_(42, &framer_, &random_, &delegate_),
+ creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)),
packet_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
packet2_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
packet3_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
packet4_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
- packet5_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL) {
+ packet5_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
+ packet6_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL),
+ packet7_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL) {
}
- ~QuicPacketGeneratorTest() {
+ virtual ~QuicPacketGeneratorTest() OVERRIDE {
delete packet_.packet;
delete packet_.retransmittable_frames;
delete packet2_.packet;
@@ -121,11 +128,15 @@ class QuicPacketGeneratorTest : public ::testing::Test {
delete packet4_.retransmittable_frames;
delete packet5_.packet;
delete packet5_.retransmittable_frames;
+ delete packet6_.packet;
+ delete packet6_.retransmittable_frames;
+ delete packet7_.packet;
+ delete packet7_.retransmittable_frames;
}
QuicAckFrame* CreateAckFrame() {
// TODO(rch): Initialize this so it can be verified later.
- return new QuicAckFrame(0, QuicTime::Zero(), 0);
+ return new QuicAckFrame(MakeAckFrame(0, 0));
}
QuicCongestionFeedbackFrame* CreateFeedbackFrame() {
@@ -135,8 +146,15 @@ class QuicPacketGeneratorTest : public ::testing::Test {
return frame;
}
+ QuicStopWaitingFrame* CreateStopWaitingFrame() {
+ QuicStopWaitingFrame* frame = new QuicStopWaitingFrame();
+ frame->entropy_hash = 0;
+ frame->least_unacked = 0;
+ return frame;
+ }
+
QuicRstStreamFrame* CreateRstStreamFrame() {
- return new QuicRstStreamFrame(1, QUIC_STREAM_NO_ERROR);
+ return new QuicRstStreamFrame(1, QUIC_STREAM_NO_ERROR, 0);
}
QuicGoAwayFrame* CreateGoAwayFrame() {
@@ -149,7 +167,7 @@ class QuicPacketGeneratorTest : public ::testing::Test {
contents.num_goaway_frames + contents.num_rst_stream_frames +
contents.num_stream_frames;
size_t num_frames = contents.num_feedback_frames + contents.num_ack_frames +
- num_retransmittable_frames;
+ contents.num_stop_waiting_frames + num_retransmittable_frames;
if (num_retransmittable_frames == 0) {
ASSERT_TRUE(packet.retransmittable_frames == NULL);
@@ -173,6 +191,8 @@ class QuicPacketGeneratorTest : public ::testing::Test {
simple_framer_.rst_stream_frames().size());
EXPECT_EQ(contents.num_stream_frames,
simple_framer_.stream_frames().size());
+ EXPECT_EQ(contents.num_stop_waiting_frames,
+ simple_framer_.stop_waiting_frames().size());
EXPECT_EQ(contents.fec_group, simple_framer_.header().fec_group);
}
@@ -204,21 +224,23 @@ class QuicPacketGeneratorTest : public ::testing::Test {
QuicFramer framer_;
MockRandom random_;
- QuicPacketCreator creator_;
StrictMock<MockDelegate> delegate_;
QuicPacketGenerator generator_;
+ QuicPacketCreator* creator_;
SimpleQuicFramer simple_framer_;
SerializedPacket packet_;
SerializedPacket packet2_;
SerializedPacket packet3_;
SerializedPacket packet4_;
SerializedPacket packet5_;
+ SerializedPacket packet6_;
+ SerializedPacket packet7_;
private:
scoped_ptr<char[]> data_array_;
};
-class MockDebugDelegate : public QuicPacketGenerator::DebugDelegateInterface {
+class MockDebugDelegate : public QuicPacketGenerator::DebugDelegate {
public:
MOCK_METHOD1(OnFrameAddedToPacket,
void(const QuicFrame&));
@@ -227,7 +249,7 @@ class MockDebugDelegate : public QuicPacketGenerator::DebugDelegateInterface {
TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) {
delegate_.SetCanNotWrite();
- generator_.SetShouldSendAck(false);
+ generator_.SetShouldSendAck(false, false);
EXPECT_TRUE(generator_.HasQueuedFrames());
}
@@ -241,7 +263,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) {
EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
EXPECT_CALL(debug_delegate, OnFrameAddedToPacket(_)).Times(1);
- generator_.SetShouldSendAck(false);
+ generator_.SetShouldSendAck(false, false);
EXPECT_TRUE(generator_.HasQueuedFrames());
}
@@ -252,7 +274,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) {
EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
DoAll(SaveArg<0>(&packet_), Return(true)));
- generator_.SetShouldSendAck(false);
+ generator_.SetShouldSendAck(false, false);
EXPECT_FALSE(generator_.HasQueuedFrames());
PacketContents contents;
@@ -269,7 +291,7 @@ TEST_F(QuicPacketGeneratorTest,
EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
Return(CreateFeedbackFrame()));
- generator_.SetShouldSendAck(true);
+ generator_.SetShouldSendAck(true, false);
EXPECT_TRUE(generator_.HasQueuedFrames());
}
@@ -280,16 +302,19 @@ TEST_F(QuicPacketGeneratorTest,
EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
Return(CreateFeedbackFrame()));
+ EXPECT_CALL(delegate_, CreateStopWaitingFrame()).WillOnce(
+ Return(CreateStopWaitingFrame()));
EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
DoAll(SaveArg<0>(&packet_), Return(true)));
- generator_.SetShouldSendAck(true);
+ generator_.SetShouldSendAck(true, true);
EXPECT_FALSE(generator_.HasQueuedFrames());
PacketContents contents;
contents.num_ack_frames = 1;
contents.num_feedback_frames = 1;
+ contents.num_stop_waiting_frames = 1;
CheckPacketContains(contents, packet_);
}
@@ -351,8 +376,8 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) {
TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) {
delegate_.SetCanNotWrite();
- QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 2,
- true, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(0u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -362,8 +387,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) {
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 2,
- true, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -374,8 +399,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) {
EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
DoAll(SaveArg<0>(&packet_), Return(true)));
- QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 2,
- true, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -390,9 +415,10 @@ TEST_F(QuicPacketGeneratorTest,
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- generator_.ConsumeData(1, MakeIOVector("foo"), 2, true, NULL);
- QuicConsumedData consumed = generator_.ConsumeData(3, MakeIOVector("quux"), 7,
- false, NULL);
+ generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 2, true,
+ MAY_FEC_PROTECT, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(4u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -402,9 +428,10 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) {
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- generator_.ConsumeData(1, MakeIOVector("foo"), 2, true, NULL);
- QuicConsumedData consumed = generator_.ConsumeData(3, MakeIOVector("quux"), 7,
- false, NULL);
+ generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 2, true,
+ MAY_FEC_PROTECT, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(4u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -424,7 +451,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) {
delegate_.SetCanWriteAnything();
// Send FEC every two packets.
- creator_.options()->max_packets_per_fec_group = 2;
+ creator_->set_max_packets_per_fec_group(2);
{
InSequence dummy;
@@ -440,10 +467,12 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) {
DoAll(SaveArg<0>(&packet5_), Return(true)));
}
- // Send enough data to create 3 packets: two full and one partial.
+ // Send enough data to create 3 packets: two full and one partial. Send
+ // with MUST_FEC_PROTECT flag.
size_t data_len = 2 * kDefaultMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true,
+ MUST_FEC_PROTECT, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -459,9 +488,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) {
TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
delegate_.SetCanWriteAnything();
- // Send FEC every six packets.
- creator_.options()->max_packets_per_fec_group = 6;
-
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(6);
{
InSequence dummy;
EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
@@ -472,10 +500,12 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
DoAll(SaveArg<0>(&packet3_), Return(true)));
}
- // Send enough data to create 2 packets: one full and one partial.
+ // Send enough data to create 2 packets: one full and one partial. Send
+ // with MUST_FEC_PROTECT flag.
size_t data_len = 1 * kDefaultMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true,
+ MUST_FEC_PROTECT, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -488,16 +518,19 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
// Set the packet size be enough for two stream frames with 0 stream offset,
// but not enough for a stream frame of 0 offset and one with non-zero offset.
- creator_.options()->max_packet_length =
+ size_t length =
NullEncrypter().GetCiphertextSize(0) +
- GetPacketHeaderSize(creator_.options()->send_guid_length,
+ GetPacketHeaderSize(creator_->connection_id_length(),
true,
- creator_.options()->send_sequence_number_length,
+ creator_->next_sequence_number_length(),
NOT_IN_FEC_GROUP) +
// Add an extra 3 bytes for the payload and 1 byte so BytesFree is larger
// than the GetMinStreamFrameSize.
- QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, false) + 3 +
- QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, true) + 1;
+ QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, false,
+ NOT_IN_FEC_GROUP) + 3 +
+ QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, true,
+ NOT_IN_FEC_GROUP) + 1;
+ creator_->set_max_packet_length(length);
delegate_.SetCanWriteAnything();
{
InSequence dummy;
@@ -509,8 +542,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
generator_.StartBatchOperations();
// Queue enough data to prevent a stream frame with a non-zero offset from
// fitting.
- QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 0,
- false, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 0, false, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -518,7 +551,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
// This frame will not fit with the existing frame, causing the queued frame
// to be serialized, and it will not fit with another frame like it, so it is
// serialized by itself.
- consumed = generator_.ConsumeData(1, MakeIOVector("bar"), 3, true, NULL);
+ consumed = generator_.ConsumeData(kHeadersStreamId, MakeIOVector("bar"), 3,
+ true, MAY_FEC_PROTECT, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -529,10 +563,259 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
CheckPacketContains(contents, packet2_);
}
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOff) {
+ delegate_.SetCanWriteAnything();
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Send one unprotected data packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ QuicConsumedData consumed =
+ generator_.ConsumeData(5, CreateData(1u), 0, true, MAY_FEC_PROTECT,
+ NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(creator_->IsFecProtected());
+ // Verify that one data packet was sent.
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet2_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet3_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet4_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet5_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet6_), Return(true)));
+ }
+ // Send enough data to create 3 packets with MUST_FEC_PROTECT flag.
+ size_t data_len = 2 * kDefaultMaxPacketSize + 100;
+ consumed = generator_.ConsumeData(7, CreateData(data_len), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ // Verify that two FEC packets were sent.
+ CheckPacketHasSingleStreamFrame(packet2_);
+ CheckPacketHasSingleStreamFrame(packet3_);
+ CheckPacketIsFec(packet4_, /*fec_group=*/2u);
+ CheckPacketHasSingleStreamFrame(packet5_);
+ CheckPacketIsFec(packet6_, /*fec_group=*/5u); // Sent at the end of stream.
+
+ // Send one unprotected data packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet7_), Return(true)));
+ consumed = generator_.ConsumeData(7, CreateData(1u), 0, true,
+ MAY_FEC_PROTECT, NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(creator_->IsFecProtected());
+ // Verify that one unprotected data packet was sent.
+ CheckPacketContains(contents, packet7_);
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFrameInCreator) {
+ delegate_.SetCanWriteAnything();
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+
+ generator_.StartBatchOperations();
+ // Queue enough data to prevent a stream frame with a non-zero offset from
+ // fitting.
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 7, CreateData(1u), 0, true, MAY_FEC_PROTECT, NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ EXPECT_TRUE(creator_->HasPendingFrames());
+
+ // Queue protected data for sending. Should cause queued frames to be flushed.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ EXPECT_FALSE(creator_->IsFecProtected());
+ consumed = generator_.ConsumeData(7, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ // Transmitted packet was not FEC protected.
+ CheckPacketContains(contents, packet_);
+ EXPECT_TRUE(creator_->IsFecProtected());
+ EXPECT_TRUE(creator_->HasPendingFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFramesInGenerator) {
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+
+ // Queue control frames in generator.
+ delegate_.SetCanNotWrite();
+ generator_.SetShouldSendAck(true, true);
+ delegate_.SetCanWriteAnything();
+ generator_.StartBatchOperations();
+
+ // Set up frames to write into the creator when control frames are written.
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+ EXPECT_CALL(delegate_, CreateStopWaitingFrame()).WillOnce(
+ Return(CreateStopWaitingFrame()));
+
+ // Generator should have queued control frames, and creator should be empty.
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(creator_->HasPendingFrames());
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Queue protected data for sending. Should cause queued frames to be flushed.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ QuicConsumedData consumed = generator_.ConsumeData(7, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ contents.num_feedback_frames = 1;
+ contents.num_stop_waiting_frames = 1;
+ CheckPacketContains(contents, packet_);
+
+ // FEC protection should be on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentFramesProtected) {
+ delegate_.SetCanWriteAnything();
+
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Queue stream frame to be protected in creator.
+ generator_.StartBatchOperations();
+ QuicConsumedData consumed = generator_.ConsumeData(5, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ // Creator has a pending protected frame.
+ EXPECT_TRUE(creator_->HasPendingFrames());
+ EXPECT_TRUE(creator_->IsFecProtected());
+
+ // Add enough unprotected data to exceed size of current packet, so that
+ // current packet is sent. Both frames will be sent out in a single packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ size_t data_len = kDefaultMaxPacketSize;
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 2u;
+ contents.fec_group = 1u;
+ CheckPacketContains(contents, packet_);
+ // FEC protection should still be on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentPacketsProtected) {
+ delegate_.SetCanWriteAnything();
+
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ generator_.StartBatchOperations();
+ // Send first packet, FEC protected.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ // Write enough data to cause a packet to be emitted.
+ size_t data_len = kDefaultMaxPacketSize;
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 5, CreateData(data_len), 0, true, MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 1u;
+ contents.fec_group = 1u;
+ CheckPacketContains(contents, packet_);
+
+ // FEC should still be on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+
+ // Send enough unprotected data to cause second packet to be sent, which gets
+ // protected because it happens to fall within an open FEC group. Data packet
+ // will be followed by FEC packet.
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet2_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet3_), Return(true)));
+ }
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ contents.num_stream_frames = 2u;
+ CheckPacketContains(contents, packet2_);
+ CheckPacketIsFec(packet3_, /*fec_group=*/1u);
+
+ // FEC protection should be off in creator.
+ EXPECT_FALSE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffThenOnWithCreatorProtectionOn) {
+ delegate_.SetCanWriteAnything();
+ generator_.StartBatchOperations();
+
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Queue one byte of FEC protected data.
+ QuicConsumedData consumed = generator_.ConsumeData(5, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_TRUE(creator_->HasPendingFrames());
+
+ // Add more unprotected data causing first packet to be sent, FEC protected.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ size_t data_len = kDefaultMaxPacketSize;
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 2u;
+ contents.fec_group = 1u;
+ CheckPacketContains(contents, packet_);
+
+ // FEC group is still open in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+
+ // Add data that should be protected, large enough to cause second packet to
+ // be sent. Data packet should be followed by FEC packet.
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet2_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet3_), Return(true)));
+ }
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MUST_FEC_PROTECT, NULL);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ CheckPacketContains(contents, packet2_);
+ CheckPacketIsFec(packet3_, /*fec_group=*/1u);
+
+ // FEC protection should remain on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+}
+
TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
delegate_.SetCanNotWrite();
- generator_.SetShouldSendAck(true);
+ generator_.SetShouldSendAck(true, false);
generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -547,7 +830,8 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
Return(CreateFeedbackFrame()));
// Send some data and a control frame
- generator_.ConsumeData(3, MakeIOVector("quux"), 7, false, NULL);
+ generator_.ConsumeData(3, MakeIOVector("quux"), 7, false,
+ MAY_FEC_PROTECT, NULL);
generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
// All five frames will be flushed out in a single packet.
@@ -568,7 +852,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) {
delegate_.SetCanNotWrite();
- generator_.SetShouldSendAck(true);
+ generator_.SetShouldSendAck(true, false);
generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -594,7 +878,8 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) {
// Send enough data to exceed one packet
size_t data_len = kDefaultMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
diff --git a/chromium/net/quic/quic_packet_writer.h b/chromium/net/quic/quic_packet_writer.h
index 5df30c8fa14..16b7adeadc6 100644
--- a/chromium/net/quic/quic_packet_writer.h
+++ b/chromium/net/quic/quic_packet_writer.h
@@ -10,7 +10,6 @@
namespace net {
-class QuicBlockedWriterInterface;
struct WriteResult;
// An interface between writers and the entity managing the
@@ -26,14 +25,20 @@ class NET_EXPORT_PRIVATE QuicPacketWriter {
// and error_code is populated.
virtual WriteResult WritePacket(
const char* buffer, size_t buf_len,
- const net::IPAddressNumber& self_address,
- const net::IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) = 0;
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) = 0;
// Returns true if the writer buffers and subsequently rewrites data
// when an attempt to write results in the underlying socket becoming
// write blocked.
virtual bool IsWriteBlockedDataBuffered() const = 0;
+
+ // Returns true if the network socket is not writable.
+ virtual bool IsWriteBlocked() const = 0;
+
+ // Records that the socket has become writable, for example when an EPOLLOUT
+ // is received or an asynchronous write completes.
+ virtual void SetWritable() = 0;
};
} // namespace net
diff --git a/chromium/net/quic/quic_protocol.cc b/chromium/net/quic/quic_protocol.cc
index cc37c111d7e..398c94d525a 100644
--- a/chromium/net/quic/quic_protocol.cc
+++ b/chromium/net/quic/quic_protocol.cc
@@ -15,48 +15,46 @@ using std::string;
namespace net {
-size_t GetPacketHeaderSize(QuicPacketHeader header) {
- return GetPacketHeaderSize(header.public_header.guid_length,
+size_t GetPacketHeaderSize(const QuicPacketHeader& header) {
+ return GetPacketHeaderSize(header.public_header.connection_id_length,
header.public_header.version_flag,
header.public_header.sequence_number_length,
header.is_in_fec_group);
}
-size_t GetPacketHeaderSize(QuicGuidLength guid_length,
+size_t GetPacketHeaderSize(QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length,
InFecGroup is_in_fec_group) {
- return kPublicFlagsSize + guid_length +
+ return kPublicFlagsSize + connection_id_length +
(include_version ? kQuicVersionSize : 0) + sequence_number_length +
kPrivateFlagsSize + (is_in_fec_group == IN_FEC_GROUP ? kFecGroupSize : 0);
}
-size_t GetPublicResetPacketSize() {
- return kPublicFlagsSize + PACKET_8BYTE_GUID + kPublicResetNonceSize +
- PACKET_6BYTE_SEQUENCE_NUMBER;
-}
-
size_t GetStartOfFecProtectedData(
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length) {
- return GetPacketHeaderSize(
- guid_length, include_version, sequence_number_length, IN_FEC_GROUP);
+ return GetPacketHeaderSize(connection_id_length,
+ include_version,
+ sequence_number_length,
+ IN_FEC_GROUP);
}
size_t GetStartOfEncryptedData(
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length) {
// Don't include the fec size, since encryption starts before private flags.
- return GetPacketHeaderSize(
- guid_length, include_version, sequence_number_length, NOT_IN_FEC_GROUP) -
- kPrivateFlagsSize;
+ return GetPacketHeaderSize(connection_id_length,
+ include_version,
+ sequence_number_length,
+ NOT_IN_FEC_GROUP) - kPrivateFlagsSize;
}
QuicPacketPublicHeader::QuicPacketPublicHeader()
- : guid(0),
- guid_length(PACKET_8BYTE_GUID),
+ : connection_id(0),
+ connection_id_length(PACKET_8BYTE_CONNECTION_ID),
reset_flag(false),
version_flag(false),
sequence_number_length(PACKET_6BYTE_SEQUENCE_NUMBER) {
@@ -64,8 +62,8 @@ QuicPacketPublicHeader::QuicPacketPublicHeader()
QuicPacketPublicHeader::QuicPacketPublicHeader(
const QuicPacketPublicHeader& other)
- : guid(other.guid),
- guid_length(other.guid_length),
+ : connection_id(other.connection_id),
+ connection_id_length(other.connection_id_length),
reset_flag(other.reset_flag),
version_flag(other.version_flag),
sequence_number_length(other.sequence_number_length),
@@ -93,7 +91,21 @@ QuicPacketHeader::QuicPacketHeader(const QuicPacketPublicHeader& header)
fec_group(0) {
}
-QuicStreamFrame::QuicStreamFrame() {}
+QuicPublicResetPacket::QuicPublicResetPacket()
+ : nonce_proof(0),
+ rejected_sequence_number(0) {}
+
+QuicPublicResetPacket::QuicPublicResetPacket(
+ const QuicPacketPublicHeader& header)
+ : public_header(header),
+ nonce_proof(0),
+ rejected_sequence_number(0) {}
+
+QuicStreamFrame::QuicStreamFrame()
+ : stream_id(0),
+ fin(false),
+ offset(0),
+ notifier(NULL) {}
QuicStreamFrame::QuicStreamFrame(const QuicStreamFrame& frame)
: stream_id(frame.stream_id),
@@ -132,6 +144,11 @@ uint32 MakeQuicTag(char a, char b, char c, char d) {
static_cast<uint32>(d) << 24;
}
+bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag) {
+ return std::find(tag_vector.begin(), tag_vector.end(), tag)
+ != tag_vector.end();
+}
+
QuicVersionVector QuicSupportedVersions() {
QuicVersionVector supported_versions;
for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
@@ -142,8 +159,18 @@ QuicVersionVector QuicSupportedVersions() {
QuicTag QuicVersionToQuicTag(const QuicVersion version) {
switch (version) {
- case QUIC_VERSION_12:
- return MakeQuicTag('Q', '0', '1', '2');
+ case QUIC_VERSION_15:
+ return MakeQuicTag('Q', '0', '1', '5');
+ case QUIC_VERSION_16:
+ return MakeQuicTag('Q', '0', '1', '6');
+ case QUIC_VERSION_17:
+ return MakeQuicTag('Q', '0', '1', '7');
+ case QUIC_VERSION_18:
+ return MakeQuicTag('Q', '0', '1', '8');
+ case QUIC_VERSION_19:
+ return MakeQuicTag('Q', '0', '1', '9');
+ case QUIC_VERSION_20:
+ return MakeQuicTag('Q', '0', '2', '0');
default:
// This shold be an ERROR because we should never attempt to convert an
// invalid QuicVersion to be written to the wire.
@@ -160,7 +187,7 @@ QuicVersion QuicTagToQuicVersion(const QuicTag version_tag) {
}
// Reading from the client so this should not be considered an ERROR.
DVLOG(1) << "Unsupported QuicTag version: "
- << QuicUtils::TagToString(version_tag);
+ << QuicUtils::TagToString(version_tag);
return QUIC_VERSION_UNSUPPORTED;
}
@@ -170,7 +197,12 @@ return #x
string QuicVersionToString(const QuicVersion version) {
switch (version) {
- RETURN_STRING_LITERAL(QUIC_VERSION_12);
+ RETURN_STRING_LITERAL(QUIC_VERSION_15);
+ RETURN_STRING_LITERAL(QUIC_VERSION_16);
+ RETURN_STRING_LITERAL(QUIC_VERSION_17);
+ RETURN_STRING_LITERAL(QUIC_VERSION_18);
+ RETURN_STRING_LITERAL(QUIC_VERSION_19);
+ RETURN_STRING_LITERAL(QUIC_VERSION_20);
default:
return "QUIC_VERSION_UNSUPPORTED";
}
@@ -188,8 +220,8 @@ string QuicVersionVectorToString(const QuicVersionVector& versions) {
}
ostream& operator<<(ostream& os, const QuicPacketHeader& header) {
- os << "{ guid: " << header.public_header.guid
- << ", guid_length:" << header.public_header.guid_length
+ os << "{ connection_id: " << header.public_header.connection_id
+ << ", connection_id_length:" << header.public_header.connection_id_length
<< ", sequence_number_length:"
<< header.public_header.sequence_number_length
<< ", reset_flag: " << header.public_header.reset_flag
@@ -210,10 +242,10 @@ ostream& operator<<(ostream& os, const QuicPacketHeader& header) {
}
ReceivedPacketInfo::ReceivedPacketInfo()
- : largest_observed(0),
+ : entropy_hash(0),
+ largest_observed(0),
delta_time_largest_observed(QuicTime::Delta::Infinite()),
- is_truncated(false) {
-}
+ is_truncated(false) {}
ReceivedPacketInfo::~ReceivedPacketInfo() {}
@@ -231,21 +263,122 @@ void InsertMissingPacketsBetween(ReceivedPacketInfo* received_info,
}
}
-SentPacketInfo::SentPacketInfo() {}
+QuicStopWaitingFrame::QuicStopWaitingFrame()
+ : entropy_hash(0),
+ least_unacked(0) {
+}
+
+QuicStopWaitingFrame::~QuicStopWaitingFrame() {}
+
+QuicAckFrame::QuicAckFrame() {}
+
+CongestionFeedbackMessageTCP::CongestionFeedbackMessageTCP()
+ : receive_window(0) {
+}
+
+CongestionFeedbackMessageInterArrival::CongestionFeedbackMessageInterArrival() {
+}
+
+CongestionFeedbackMessageInterArrival::
+ ~CongestionFeedbackMessageInterArrival() {}
+
+QuicCongestionFeedbackFrame::QuicCongestionFeedbackFrame() : type(kTCP) {}
+
+QuicCongestionFeedbackFrame::~QuicCongestionFeedbackFrame() {}
-SentPacketInfo::~SentPacketInfo() {}
+QuicRstStreamErrorCode AdjustErrorForVersion(
+ QuicRstStreamErrorCode error_code,
+ QuicVersion version) {
+ switch (error_code) {
+ case QUIC_RST_FLOW_CONTROL_ACCOUNTING:
+ if (version <= QUIC_VERSION_17) {
+ return QUIC_STREAM_NO_ERROR;
+ }
+ break;
+ default:
+ return error_code;
+ }
+ return error_code;
+}
+
+QuicRstStreamFrame::QuicRstStreamFrame()
+ : stream_id(0),
+ error_code(QUIC_STREAM_NO_ERROR) {
+}
+
+QuicRstStreamFrame::QuicRstStreamFrame(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicStreamOffset bytes_written)
+ : stream_id(stream_id),
+ error_code(error_code),
+ byte_offset(bytes_written) {
+ DCHECK_LE(error_code, numeric_limits<uint8>::max());
+}
+
+QuicConnectionCloseFrame::QuicConnectionCloseFrame()
+ : error_code(QUIC_NO_ERROR) {
+}
+
+QuicFrame::QuicFrame() {}
+
+QuicFrame::QuicFrame(QuicPaddingFrame* padding_frame)
+ : type(PADDING_FRAME),
+ padding_frame(padding_frame) {
+}
+
+QuicFrame::QuicFrame(QuicStreamFrame* stream_frame)
+ : type(STREAM_FRAME),
+ stream_frame(stream_frame) {
+}
-// Testing convenience method.
-QuicAckFrame::QuicAckFrame(QuicPacketSequenceNumber largest_observed,
- QuicTime largest_observed_receive_time,
- QuicPacketSequenceNumber least_unacked) {
- received_info.largest_observed = largest_observed;
- received_info.entropy_hash = 0;
- sent_info.least_unacked = least_unacked;
- sent_info.entropy_hash = 0;
+QuicFrame::QuicFrame(QuicAckFrame* frame)
+ : type(ACK_FRAME),
+ ack_frame(frame) {
}
-ostream& operator<<(ostream& os, const SentPacketInfo& sent_info) {
+QuicFrame::QuicFrame(QuicCongestionFeedbackFrame* frame)
+ : type(CONGESTION_FEEDBACK_FRAME),
+ congestion_feedback_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicStopWaitingFrame* frame)
+ : type(STOP_WAITING_FRAME),
+ stop_waiting_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicPingFrame* frame)
+ : type(PING_FRAME),
+ ping_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicRstStreamFrame* frame)
+ : type(RST_STREAM_FRAME),
+ rst_stream_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicConnectionCloseFrame* frame)
+ : type(CONNECTION_CLOSE_FRAME),
+ connection_close_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicGoAwayFrame* frame)
+ : type(GOAWAY_FRAME),
+ goaway_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicWindowUpdateFrame* frame)
+ : type(WINDOW_UPDATE_FRAME),
+ window_update_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicBlockedFrame* frame)
+ : type(BLOCKED_FRAME),
+ blocked_frame(frame) {
+}
+
+QuicFecData::QuicFecData() : fec_group(0) {}
+
+ostream& operator<<(ostream& os, const QuicStopWaitingFrame& sent_info) {
os << "entropy_hash: " << static_cast<int>(sent_info.entropy_hash)
<< " least_unacked: " << sent_info.least_unacked;
return os;
@@ -253,21 +386,125 @@ ostream& operator<<(ostream& os, const SentPacketInfo& sent_info) {
ostream& operator<<(ostream& os, const ReceivedPacketInfo& received_info) {
os << "entropy_hash: " << static_cast<int>(received_info.entropy_hash)
+ << " is_truncated: " << received_info.is_truncated
<< " largest_observed: " << received_info.largest_observed
+ << " delta_time_largest_observed: "
+ << received_info.delta_time_largest_observed.ToMicroseconds()
<< " missing_packets: [ ";
for (SequenceNumberSet::const_iterator it =
received_info.missing_packets.begin();
it != received_info.missing_packets.end(); ++it) {
os << *it << " ";
}
- os << " ] ";
+ os << " ] revived_packets: [ ";
+ for (SequenceNumberSet::const_iterator it =
+ received_info.revived_packets.begin();
+ it != received_info.revived_packets.end(); ++it) {
+ os << *it << " ";
+ }
+ os << " ]";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicFrame& frame) {
+ switch (frame.type) {
+ case PADDING_FRAME: {
+ os << "type { PADDING_FRAME } ";
+ break;
+ }
+ case RST_STREAM_FRAME: {
+ os << "type { " << RST_STREAM_FRAME << " } " << *(frame.rst_stream_frame);
+ break;
+ }
+ case CONNECTION_CLOSE_FRAME: {
+ os << "type { CONNECTION_CLOSE_FRAME } "
+ << *(frame.connection_close_frame);
+ break;
+ }
+ case GOAWAY_FRAME: {
+ os << "type { GOAWAY_FRAME } " << *(frame.goaway_frame);
+ break;
+ }
+ case WINDOW_UPDATE_FRAME: {
+ os << "type { WINDOW_UPDATE_FRAME } " << *(frame.window_update_frame);
+ break;
+ }
+ case BLOCKED_FRAME: {
+ os << "type { BLOCKED_FRAME } " << *(frame.blocked_frame);
+ break;
+ }
+ case STREAM_FRAME: {
+ os << "type { STREAM_FRAME } " << *(frame.stream_frame);
+ break;
+ }
+ case ACK_FRAME: {
+ os << "type { ACK_FRAME } " << *(frame.ack_frame);
+ break;
+ }
+ case CONGESTION_FEEDBACK_FRAME: {
+ os << "type { CONGESTION_FEEDBACK_FRAME } "
+ << *(frame.congestion_feedback_frame);
+ break;
+ }
+ case STOP_WAITING_FRAME: {
+ os << "type { STOP_WAITING_FRAME } " << *(frame.stop_waiting_frame);
+ break;
+ }
+ default: {
+ LOG(ERROR) << "Unknown frame type: " << frame.type;
+ break;
+ }
+ }
return os;
}
-QuicCongestionFeedbackFrame::QuicCongestionFeedbackFrame() {
+ostream& operator<<(ostream& os, const QuicRstStreamFrame& rst_frame) {
+ os << "stream_id { " << rst_frame.stream_id << " } "
+ << "error_code { " << rst_frame.error_code << " } "
+ << "error_details { " << rst_frame.error_details << " }\n";
+ return os;
}
-QuicCongestionFeedbackFrame::~QuicCongestionFeedbackFrame() {
+ostream& operator<<(ostream& os,
+ const QuicConnectionCloseFrame& connection_close_frame) {
+ os << "error_code { " << connection_close_frame.error_code << " } "
+ << "error_details { " << connection_close_frame.error_details << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicGoAwayFrame& goaway_frame) {
+ os << "error_code { " << goaway_frame.error_code << " } "
+ << "last_good_stream_id { " << goaway_frame.last_good_stream_id << " } "
+ << "reason_phrase { " << goaway_frame.reason_phrase << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os,
+ const QuicWindowUpdateFrame& window_update_frame) {
+ os << "stream_id { " << window_update_frame.stream_id << " } "
+ << "byte_offset { " << window_update_frame.byte_offset << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicBlockedFrame& blocked_frame) {
+ os << "stream_id { " << blocked_frame.stream_id << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicStreamFrame& stream_frame) {
+ os << "stream_id { " << stream_frame.stream_id << " } "
+ << "fin { " << stream_frame.fin << " } "
+ << "offset { " << stream_frame.offset << " } "
+ << "data { "
+ << QuicUtils::StringToHexASCIIDump(*(stream_frame.GetDataAsString()))
+ << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicAckFrame& ack_frame) {
+ os << "sent info { " << ack_frame.sent_info << " } "
+ << "received info { " << ack_frame.received_info << " }\n";
+ return os;
}
ostream& operator<<(ostream& os,
@@ -277,8 +514,6 @@ ostream& operator<<(ostream& os,
case kInterArrival: {
const CongestionFeedbackMessageInterArrival& inter_arrival =
congestion_frame.inter_arrival;
- os << " accumulated_number_of_lost_packets: "
- << inter_arrival.accumulated_number_of_lost_packets;
os << " received packets: [ ";
for (TimeMap::const_iterator it =
inter_arrival.received_packet_times.begin();
@@ -295,30 +530,25 @@ ostream& operator<<(ostream& os,
}
case kTCP: {
const CongestionFeedbackMessageTCP& tcp = congestion_frame.tcp;
- os << " accumulated_number_of_lost_packets: "
- << congestion_frame.tcp.accumulated_number_of_lost_packets;
os << " receive_window: " << tcp.receive_window;
break;
}
+ case kTCPBBR: {
+ LOG(DFATAL) << "TCPBBR is not yet supported.";
+ break;
+ }
}
return os;
}
-ostream& operator<<(ostream& os, const QuicAckFrame& ack_frame) {
- os << "sent info { " << ack_frame.sent_info << " } "
- << "received info { " << ack_frame.received_info << " }\n";
- return os;
-}
-
CongestionFeedbackMessageFixRate::CongestionFeedbackMessageFixRate()
: bitrate(QuicBandwidth::Zero()) {
}
-CongestionFeedbackMessageInterArrival::
-CongestionFeedbackMessageInterArrival() {}
-
-CongestionFeedbackMessageInterArrival::
-~CongestionFeedbackMessageInterArrival() {}
+QuicGoAwayFrame::QuicGoAwayFrame()
+ : error_code(QUIC_NO_ERROR),
+ last_good_stream_id(0) {
+}
QuicGoAwayFrame::QuicGoAwayFrame(QuicErrorCode error_code,
QuicStreamId last_good_stream_id,
@@ -329,7 +559,20 @@ QuicGoAwayFrame::QuicGoAwayFrame(QuicErrorCode error_code,
DCHECK_LE(error_code, numeric_limits<uint8>::max());
}
-QuicFecData::QuicFecData() {}
+QuicData::QuicData(const char* buffer,
+ size_t length)
+ : buffer_(buffer),
+ length_(length),
+ owns_buffer_(false) {
+}
+
+QuicData::QuicData(char* buffer,
+ size_t length,
+ bool owns_buffer)
+ : buffer_(buffer),
+ length_(length),
+ owns_buffer_(owns_buffer) {
+}
QuicData::~QuicData() {
if (owns_buffer_) {
@@ -337,9 +580,43 @@ QuicData::~QuicData() {
}
}
+QuicWindowUpdateFrame::QuicWindowUpdateFrame(QuicStreamId stream_id,
+ QuicStreamOffset byte_offset)
+ : stream_id(stream_id),
+ byte_offset(byte_offset) {}
+
+QuicBlockedFrame::QuicBlockedFrame(QuicStreamId stream_id)
+ : stream_id(stream_id) {}
+
+QuicPacket::QuicPacket(char* buffer,
+ size_t length,
+ bool owns_buffer,
+ QuicConnectionIdLength connection_id_length,
+ bool includes_version,
+ QuicSequenceNumberLength sequence_number_length,
+ bool is_fec_packet)
+ : QuicData(buffer, length, owns_buffer),
+ buffer_(buffer),
+ is_fec_packet_(is_fec_packet),
+ connection_id_length_(connection_id_length),
+ includes_version_(includes_version),
+ sequence_number_length_(sequence_number_length) {
+}
+
+QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer,
+ size_t length)
+ : QuicData(buffer, length) {
+}
+
+QuicEncryptedPacket::QuicEncryptedPacket(char* buffer,
+ size_t length,
+ bool owns_buffer)
+ : QuicData(buffer, length, owns_buffer) {
+}
+
StringPiece QuicPacket::FecProtectedData() const {
const size_t start_of_fec = GetStartOfFecProtectedData(
- guid_length_, includes_version_, sequence_number_length_);
+ connection_id_length_, includes_version_, sequence_number_length_);
return StringPiece(data() + start_of_fec, length() - start_of_fec);
}
@@ -347,12 +624,12 @@ StringPiece QuicPacket::AssociatedData() const {
return StringPiece(
data() + kStartOfHashData,
GetStartOfEncryptedData(
- guid_length_, includes_version_, sequence_number_length_) -
+ connection_id_length_, includes_version_, sequence_number_length_) -
kStartOfHashData);
}
StringPiece QuicPacket::BeforePlaintext() const {
- return StringPiece(data(), GetStartOfEncryptedData(guid_length_,
+ return StringPiece(data(), GetStartOfEncryptedData(connection_id_length_,
includes_version_,
sequence_number_length_));
}
@@ -360,7 +637,7 @@ StringPiece QuicPacket::BeforePlaintext() const {
StringPiece QuicPacket::Plaintext() const {
const size_t start_of_encrypted_data =
GetStartOfEncryptedData(
- guid_length_, includes_version_, sequence_number_length_);
+ connection_id_length_, includes_version_, sequence_number_length_);
return StringPiece(data() + start_of_encrypted_data,
length() - start_of_encrypted_data);
}
@@ -384,6 +661,12 @@ RetransmittableFrames::~RetransmittableFrames() {
case CONGESTION_FEEDBACK_FRAME:
delete it->congestion_feedback_frame;
break;
+ case STOP_WAITING_FRAME:
+ delete it->stop_waiting_frame;
+ break;
+ case PING_FRAME:
+ delete it->ping_frame;
+ break;
case RST_STREAM_FRAME:
delete it->rst_stream_frame;
break;
@@ -393,6 +676,12 @@ RetransmittableFrames::~RetransmittableFrames() {
case GOAWAY_FRAME:
delete it->goaway_frame;
break;
+ case WINDOW_UPDATE_FRAME:
+ delete it->window_update_frame;
+ break;
+ case BLOCKED_FRAME:
+ delete it->blocked_frame;
+ break;
case NUM_FRAME_TYPES:
DCHECK(false) << "Cannot delete type: " << it->type;
}
@@ -419,6 +708,16 @@ const QuicFrame& RetransmittableFrames::AddNonStreamFrame(
return frames_.back();
}
+IsHandshake RetransmittableFrames::HasCryptoHandshake() const {
+ for (size_t i = 0; i < frames().size(); ++i) {
+ if (frames()[i].type == STREAM_FRAME &&
+ frames()[i].stream_frame->stream_id == kCryptoStreamId) {
+ return IS_HANDSHAKE;
+ }
+ }
+ return NOT_HANDSHAKE;
+}
+
void RetransmittableFrames::set_encryption_level(EncryptionLevel level) {
encryption_level_ = level;
}
@@ -449,10 +748,46 @@ ostream& operator<<(ostream& os, const QuicEncryptedPacket& s) {
return os;
}
-ostream& operator<<(ostream& os, const QuicConsumedData& s) {
- os << "bytes_consumed: " << s.bytes_consumed
- << " fin_consumed: " << s.fin_consumed;
- return os;
+TransmissionInfo::TransmissionInfo()
+ : retransmittable_frames(NULL),
+ sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER),
+ sent_time(QuicTime::Zero()),
+ bytes_sent(0),
+ nack_count(0),
+ transmission_type(NOT_RETRANSMISSION),
+ all_transmissions(NULL),
+ in_flight(false) {}
+
+TransmissionInfo::TransmissionInfo(
+ RetransmittableFrames* retransmittable_frames,
+ QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length)
+ : retransmittable_frames(retransmittable_frames),
+ sequence_number_length(sequence_number_length),
+ sent_time(QuicTime::Zero()),
+ bytes_sent(0),
+ nack_count(0),
+ transmission_type(NOT_RETRANSMISSION),
+ all_transmissions(new SequenceNumberSet),
+ in_flight(false) {
+ all_transmissions->insert(sequence_number);
+}
+
+TransmissionInfo::TransmissionInfo(
+ RetransmittableFrames* retransmittable_frames,
+ QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length,
+ TransmissionType transmission_type,
+ SequenceNumberSet* all_transmissions)
+ : retransmittable_frames(retransmittable_frames),
+ sequence_number_length(sequence_number_length),
+ sent_time(QuicTime::Zero()),
+ bytes_sent(0),
+ nack_count(0),
+ transmission_type(transmission_type),
+ all_transmissions(all_transmissions),
+ in_flight(false) {
+ all_transmissions->insert(sequence_number);
}
} // namespace net
diff --git a/chromium/net/quic/quic_protocol.h b/chromium/net/quic/quic_protocol.h
index e97e2fc77aa..e84d338f4ad 100644
--- a/chromium/net/quic/quic_protocol.h
+++ b/chromium/net/quic/quic_protocol.h
@@ -19,6 +19,7 @@
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "net/base/int128.h"
+#include "net/base/ip_endpoint.h"
#include "net/base/net_export.h"
#include "net/quic/iovector.h"
#include "net/quic/quic_bandwidth.h"
@@ -32,7 +33,7 @@ class QuicAckNotifier;
class QuicPacket;
struct QuicPacketHeader;
-typedef uint64 QuicGuid;
+typedef uint64 QuicConnectionId;
typedef uint32 QuicStreamId;
typedef uint64 QuicStreamOffset;
typedef uint64 QuicPacketSequenceNumber;
@@ -43,6 +44,9 @@ typedef uint32 QuicHeaderId;
// QuicTag is the type of a tag in the wire protocol.
typedef uint32 QuicTag;
typedef std::vector<QuicTag> QuicTagVector;
+typedef std::map<QuicTag, std::string> QuicTagValueMap;
+// TODO(rtenneti): Didn't use SpdyPriority because SpdyPriority is uint8 and
+// QuicPriority is uint32. Use SpdyPriority when we change the QUIC_VERSION.
typedef uint32 QuicPriority;
// TODO(rch): Consider Quic specific names for these constants.
@@ -53,19 +57,22 @@ const QuicByteCount kDefaultMaxPacketSize = 1200;
// additional 8 bytes. This is a total overhead of 48 bytes. Ethernet's
// max packet size is 1500 bytes, 1500 - 48 = 1452.
const QuicByteCount kMaxPacketSize = 1452;
+// Default maximum packet size used in Linux TCP implementations.
+const QuicByteCount kDefaultTCPMSS = 1460;
// Maximum size of the initial congestion window in packets.
const size_t kDefaultInitialWindow = 10;
-// TODO(ianswett): Temporarily changed to 10 due to a large number of clients
-// mistakenly negotiating 100 initially and suffering the consequences.
-const size_t kMaxInitialWindow = 10;
+const uint32 kMaxInitialWindow = 100;
+
+// Default size of initial flow control window, for both stream and session.
+const uint32 kDefaultFlowControlSendWindow = 16 * 1024; // 16 KB
// Maximum size of the congestion window, in packets, for TCP congestion control
// algorithms.
const size_t kMaxTcpCongestionWindow = 200;
// Don't allow a client to suggest an RTT longer than 15 seconds.
-const size_t kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond;
+const uint32 kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond;
// Maximum number of open streams per connection.
const size_t kDefaultMaxStreamsPerConnection = 100;
@@ -78,8 +85,6 @@ const size_t kQuicVersionSize = 4;
const size_t kPrivateFlagsSize = 1;
// Number of bytes reserved for FEC group in the packet header.
const size_t kFecGroupSize = 1;
-// Number of bytes reserved for the nonce proof in public reset packet.
-const size_t kPublicResetNonceSize = 8;
// Signifies that the QuicPacket will contain version of the protocol.
const bool kIncludeVersion = true;
@@ -88,20 +93,25 @@ const bool kIncludeVersion = true;
const size_t kStartOfHashData = 0;
// Limit on the delta between stream IDs.
-const QuicStreamId kMaxStreamIdDelta = 100;
+const QuicStreamId kMaxStreamIdDelta = 200;
// Limit on the delta between header IDs.
-const QuicHeaderId kMaxHeaderIdDelta = 100;
+const QuicHeaderId kMaxHeaderIdDelta = 200;
// Reserved ID for the crypto stream.
-// TODO(rch): ensure that this is not usable by any other streams.
const QuicStreamId kCryptoStreamId = 1;
+// Reserved ID for the headers stream.
+const QuicStreamId kHeadersStreamId = 3;
+
// This is the default network timeout a for connection till the crypto
// handshake succeeds and the negotiated timeout from the handshake is received.
const int64 kDefaultInitialTimeoutSecs = 120; // 2 mins.
const int64 kDefaultTimeoutSecs = 60 * 10; // 10 minutes.
const int64 kDefaultMaxTimeForCryptoHandshakeSecs = 5; // 5 secs.
+// Default ping timeout.
+const int64 kPingTimeoutSecs = 15; // 15 secs.
+
// We define an unsigned 16-bit floating point value, inspired by IEEE floats
// (http://en.wikipedia.org/wiki/Half_precision_floating-point_format),
// with 5-bit exponent (bias 1), 11-bit mantissa (effective 12 with hidden
@@ -121,8 +131,13 @@ const uint64 kUFloat16MaxValue = // 0x3FFC0000000
enum TransmissionType {
NOT_RETRANSMISSION,
- NACK_RETRANSMISSION,
- RTO_RETRANSMISSION,
+ FIRST_TRANSMISSION_TYPE = NOT_RETRANSMISSION,
+ HANDSHAKE_RETRANSMISSION, // Retransmits due to handshake timeouts.
+ ALL_UNACKED_RETRANSMISSION, // Retransmits of all unacked packets.
+ LOSS_RETRANSMISSION, // Retransmits due to loss detection.
+ RTO_RETRANSMISSION, // Retransmits due to retransmit time out.
+ TLP_RETRANSMISSION, // Tail loss probes.
+ LAST_TRANSMISSION_TYPE = TLP_RETRANSMISSION,
};
enum RetransmissionType {
@@ -140,22 +155,43 @@ enum IsHandshake {
IS_HANDSHAKE
};
+// Indicates FEC protection level for data being written.
+enum FecProtection {
+ MUST_FEC_PROTECT, // Callee must FEC protect this data.
+ MAY_FEC_PROTECT // Callee does not have to but may FEC protect this data.
+};
+
+// Indicates FEC policy.
+enum FecPolicy {
+ FEC_PROTECT_ALWAYS, // All data in the stream should be FEC protected.
+ FEC_PROTECT_OPTIONAL // Data in the stream does not need FEC protection.
+};
+
enum QuicFrameType {
+ // Regular frame types. The values set here cannot change without the
+ // introduction of a new QUIC version.
PADDING_FRAME = 0,
- RST_STREAM_FRAME,
- CONNECTION_CLOSE_FRAME,
- GOAWAY_FRAME,
+ RST_STREAM_FRAME = 1,
+ CONNECTION_CLOSE_FRAME = 2,
+ GOAWAY_FRAME = 3,
+ WINDOW_UPDATE_FRAME = 4,
+ BLOCKED_FRAME = 5,
+ STOP_WAITING_FRAME = 6,
+ PING_FRAME = 7,
+
+ // STREAM, ACK, and CONGESTION_FEEDBACK frames are special frames. They are
+ // encoded differently on the wire and their values do not need to be stable.
STREAM_FRAME,
ACK_FRAME,
CONGESTION_FEEDBACK_FRAME,
NUM_FRAME_TYPES
};
-enum QuicGuidLength {
- PACKET_0BYTE_GUID = 0,
- PACKET_1BYTE_GUID = 1,
- PACKET_4BYTE_GUID = 4,
- PACKET_8BYTE_GUID = 8
+enum QuicConnectionIdLength {
+ PACKET_0BYTE_CONNECTION_ID = 0,
+ PACKET_1BYTE_CONNECTION_ID = 1,
+ PACKET_4BYTE_CONNECTION_ID = 4,
+ PACKET_8BYTE_CONNECTION_ID = 8
};
enum InFecGroup {
@@ -188,15 +224,15 @@ enum QuicPacketPublicFlags {
// Bit 1: Is this packet a public reset packet?
PACKET_PUBLIC_FLAGS_RST = 1 << 1,
- // Bits 2 and 3 specify the length of the GUID as follows:
+ // Bits 2 and 3 specify the length of the ConnectionId as follows:
// ----00--: 0 bytes
// ----01--: 1 byte
// ----10--: 4 bytes
// ----11--: 8 bytes
- PACKET_PUBLIC_FLAGS_0BYTE_GUID = 0,
- PACKET_PUBLIC_FLAGS_1BYTE_GUID = 1 << 2,
- PACKET_PUBLIC_FLAGS_4BYTE_GUID = 1 << 3,
- PACKET_PUBLIC_FLAGS_8BYTE_GUID = 1 << 3 | 1 << 2,
+ PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID = 0,
+ PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID = 1 << 2,
+ PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID = 1 << 3,
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID = 1 << 3 | 1 << 2,
// Bits 4 and 5 describe the packet sequence number length as follows:
// --00----: 1 byte
@@ -239,7 +275,12 @@ enum QuicVersion {
// Special case to indicate unknown/unsupported QUIC version.
QUIC_VERSION_UNSUPPORTED = 0,
- QUIC_VERSION_12 = 12, // Current version.
+ QUIC_VERSION_15 = 15,
+ QUIC_VERSION_16 = 16,
+ QUIC_VERSION_17 = 17,
+ QUIC_VERSION_18 = 18,
+ QUIC_VERSION_19 = 19,
+ QUIC_VERSION_20 = 20, // Current version.
};
// This vector contains QUIC versions which we currently support.
@@ -249,7 +290,12 @@ enum QuicVersion {
//
// IMPORTANT: if you are addding to this list, follow the instructions at
// http://sites/quic/adding-and-removing-versions
-static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_12};
+static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_20,
+ QUIC_VERSION_19,
+ QUIC_VERSION_18,
+ QUIC_VERSION_17,
+ QUIC_VERSION_16,
+ QUIC_VERSION_15};
typedef std::vector<QuicVersion> QuicVersionVector;
@@ -286,26 +332,26 @@ NET_EXPORT_PRIVATE std::string QuicVersionVectorToString(
// MakeQuicTag('C', 'H', 'L', 'O');
NET_EXPORT_PRIVATE QuicTag MakeQuicTag(char a, char b, char c, char d);
+// Returns true if the tag vector contains the specified tag.
+bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag);
+
// Size in bytes of the data or fec packet header.
-NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(QuicPacketHeader header);
+NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(const QuicPacketHeader& header);
NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length,
InFecGroup is_in_fec_group);
-// Size in bytes of the public reset packet.
-NET_EXPORT_PRIVATE size_t GetPublicResetPacketSize();
-
// Index of the first byte in a QUIC packet of FEC protected data.
NET_EXPORT_PRIVATE size_t GetStartOfFecProtectedData(
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length);
// Index of the first byte in a QUIC packet of encrypted data.
NET_EXPORT_PRIVATE size_t GetStartOfEncryptedData(
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool include_version,
QuicSequenceNumberLength sequence_number_length);
@@ -325,11 +371,20 @@ enum QuicRstStreamErrorCode {
QUIC_STREAM_PEER_GOING_AWAY,
// The stream has been cancelled.
QUIC_STREAM_CANCELLED,
+ // Sending a RST to allow for proper flow control accounting.
+ QUIC_RST_FLOW_CONTROL_ACCOUNTING,
// No error. Used as bound while iterating.
QUIC_STREAM_LAST_ERROR,
};
+// Because receiving an unknown QuicRstStreamErrorCode results in connection
+// teardown, we use this to make sure any errors predating a given version are
+// downgraded to the most appropriate existing error.
+NET_EXPORT_PRIVATE QuicRstStreamErrorCode AdjustErrorForVersion(
+ QuicRstStreamErrorCode error_code,
+ QuicVersion version);
+
// These values must remain stable as they are uploaded to UMA histograms.
// To add a new error code, use the current value of QUIC_LAST_ERROR and
// increment QUIC_LAST_ERROR.
@@ -350,12 +405,20 @@ enum QuicErrorCode {
QUIC_INVALID_FEC_DATA = 5,
// STREAM frame data is malformed.
QUIC_INVALID_STREAM_DATA = 46,
+ // STREAM frame data is not encrypted.
+ QUIC_UNENCRYPTED_STREAM_DATA = 61,
// RST_STREAM frame data is malformed.
QUIC_INVALID_RST_STREAM_DATA = 6,
// CONNECTION_CLOSE frame data is malformed.
QUIC_INVALID_CONNECTION_CLOSE_DATA = 7,
// GOAWAY frame data is malformed.
QUIC_INVALID_GOAWAY_DATA = 8,
+ // WINDOW_UPDATE frame data is malformed.
+ QUIC_INVALID_WINDOW_UPDATE_DATA = 57,
+ // BLOCKED frame data is malformed.
+ QUIC_INVALID_BLOCKED_DATA = 58,
+ // STOP_WAITING frame data is malformed.
+ QUIC_INVALID_STOP_WAITING_DATA = 60,
// ACK frame data is malformed.
QUIC_INVALID_ACK_DATA = 9,
// CONGESTION_FEEDBACK frame data is malformed.
@@ -384,8 +447,9 @@ enum QuicErrorCode {
QUIC_PUBLIC_RESET = 19,
// Invalid protocol version.
QUIC_INVALID_VERSION = 20,
- // Stream reset before headers decompressed.
- QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED = 21,
+
+ // deprecated: QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED = 21
+
// The Header ID for a stream was too far from the previous.
QUIC_INVALID_HEADER_ID = 22,
// Negotiable parameter received during handshake had invalid value.
@@ -402,7 +466,16 @@ enum QuicErrorCode {
QUIC_PACKET_READ_ERROR = 51,
// We received a STREAM_FRAME with no data and no fin flag set.
QUIC_INVALID_STREAM_FRAME = 50,
-
+ // We received invalid data on the headers stream.
+ QUIC_INVALID_HEADERS_STREAM_DATA = 56,
+ // The peer received too much data, violating flow control.
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA = 59,
+ // The peer sent too much data, violating flow control.
+ QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA = 63,
+ // The peer received an invalid flow control window.
+ QUIC_FLOW_CONTROL_INVALID_WINDOW = 64,
+ // The connection has been IP pooled into an existing connection.
+ QUIC_CONNECTION_IP_POOLED = 62,
// Crypto errors.
@@ -458,7 +531,7 @@ enum QuicErrorCode {
QUIC_VERSION_NEGOTIATION_MISMATCH = 55,
// No error. Used as bound while iterating.
- QUIC_LAST_ERROR = 56,
+ QUIC_LAST_ERROR = 65,
};
struct NET_EXPORT_PRIVATE QuicPacketPublicHeader {
@@ -466,9 +539,10 @@ struct NET_EXPORT_PRIVATE QuicPacketPublicHeader {
explicit QuicPacketPublicHeader(const QuicPacketPublicHeader& other);
~QuicPacketPublicHeader();
- // Universal header. All QuicPacket headers will have a guid and public flags.
- QuicGuid guid;
- QuicGuidLength guid_length;
+ // Universal header. All QuicPacket headers will have a connection_id and
+ // public flags.
+ QuicConnectionId connection_id;
+ QuicConnectionIdLength connection_id_length;
bool reset_flag;
bool version_flag;
QuicSequenceNumberLength sequence_number_length;
@@ -493,12 +567,13 @@ struct NET_EXPORT_PRIVATE QuicPacketHeader {
};
struct NET_EXPORT_PRIVATE QuicPublicResetPacket {
- QuicPublicResetPacket() {}
- explicit QuicPublicResetPacket(const QuicPacketPublicHeader& header)
- : public_header(header) {}
+ QuicPublicResetPacket();
+ explicit QuicPublicResetPacket(const QuicPacketPublicHeader& header);
+
QuicPacketPublicHeader public_header;
- QuicPacketSequenceNumber rejected_sequence_number;
QuicPublicResetNonceProof nonce_proof;
+ QuicPacketSequenceNumber rejected_sequence_number;
+ IPEndPoint client_address;
};
enum QuicVersionNegotiationState {
@@ -521,6 +596,11 @@ typedef QuicPacketPublicHeader QuicVersionNegotiationPacket;
struct NET_EXPORT_PRIVATE QuicPaddingFrame {
};
+// A ping frame contains no payload, though it is retransmittable,
+// and ACK'd just like other normal frames.
+struct NET_EXPORT_PRIVATE QuicPingFrame {
+};
+
struct NET_EXPORT_PRIVATE QuicStreamFrame {
QuicStreamFrame();
QuicStreamFrame(const QuicStreamFrame& frame);
@@ -529,6 +609,9 @@ struct NET_EXPORT_PRIVATE QuicStreamFrame {
QuicStreamOffset offset,
IOVector data);
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicStreamFrame& s);
+
// Returns a copy of the IOVector |data| as a heap-allocated string.
// Caller must take ownership of the returned string.
std::string* GetDataAsString() const;
@@ -552,6 +635,7 @@ typedef std::map<QuicPacketSequenceNumber, QuicTime> TimeMap;
struct NET_EXPORT_PRIVATE ReceivedPacketInfo {
ReceivedPacketInfo();
~ReceivedPacketInfo();
+
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
std::ostream& os, const ReceivedPacketInfo& s);
@@ -580,6 +664,10 @@ struct NET_EXPORT_PRIVATE ReceivedPacketInfo {
// Whether the ack had to be truncated when sent.
bool is_truncated;
+
+ // Packets which have been revived via FEC.
+ // All of these must also be in missing_packets.
+ SequenceNumberSet revived_packets;
};
// True if the sequence number is greater than largest_observed or is listed
@@ -595,11 +683,12 @@ void NET_EXPORT_PRIVATE InsertMissingPacketsBetween(
QuicPacketSequenceNumber lower,
QuicPacketSequenceNumber higher);
-struct NET_EXPORT_PRIVATE SentPacketInfo {
- SentPacketInfo();
- ~SentPacketInfo();
+struct NET_EXPORT_PRIVATE QuicStopWaitingFrame {
+ QuicStopWaitingFrame();
+ ~QuicStopWaitingFrame();
+
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
- std::ostream& os, const SentPacketInfo& s);
+ std::ostream& os, const QuicStopWaitingFrame& s);
// Entropy hash of all packets up to, but not including, the least unacked
// packet.
@@ -609,17 +698,12 @@ struct NET_EXPORT_PRIVATE SentPacketInfo {
};
struct NET_EXPORT_PRIVATE QuicAckFrame {
- QuicAckFrame() {}
- // Testing convenience method to construct a QuicAckFrame with all packets
- // from least_unacked to largest_observed acked.
- QuicAckFrame(QuicPacketSequenceNumber largest_observed,
- QuicTime largest_observed_receive_time,
- QuicPacketSequenceNumber least_unacked);
+ QuicAckFrame();
NET_EXPORT_PRIVATE friend std::ostream& operator<<(
std::ostream& os, const QuicAckFrame& s);
- SentPacketInfo sent_info;
+ QuicStopWaitingFrame sent_info;
ReceivedPacketInfo received_info;
};
@@ -630,17 +714,24 @@ enum CongestionFeedbackType {
kTCP, // Used to mimic TCP.
kInterArrival, // Use additional inter arrival information.
kFixRate, // Provided for testing.
+ kTCPBBR, // BBR implementation based on TCP congestion feedback.
+};
+
+enum LossDetectionType {
+ kNack, // Used to mimic TCP's loss detection.
+ kTime, // Time based loss detection.
};
struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTCP {
- uint16 accumulated_number_of_lost_packets;
+ CongestionFeedbackMessageTCP();
+
QuicByteCount receive_window;
};
struct NET_EXPORT_PRIVATE CongestionFeedbackMessageInterArrival {
CongestionFeedbackMessageInterArrival();
~CongestionFeedbackMessageInterArrival();
- uint16 accumulated_number_of_lost_packets;
+
// The set of received packets since the last feedback was sent, along with
// their arrival times.
TimeMap received_packet_times;
@@ -667,33 +758,86 @@ struct NET_EXPORT_PRIVATE QuicCongestionFeedbackFrame {
};
struct NET_EXPORT_PRIVATE QuicRstStreamFrame {
- QuicRstStreamFrame() {}
- QuicRstStreamFrame(QuicStreamId stream_id, QuicRstStreamErrorCode error_code)
- : stream_id(stream_id), error_code(error_code) {
- DCHECK_LE(error_code, std::numeric_limits<uint8>::max());
- }
+ QuicRstStreamFrame();
+ QuicRstStreamFrame(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicStreamOffset bytes_written);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicRstStreamFrame& r);
QuicStreamId stream_id;
QuicRstStreamErrorCode error_code;
std::string error_details;
+
+ // Used to update flow control windows. On termination of a stream, both
+ // endpoints must inform the peer of the number of bytes they have sent on
+ // that stream. This can be done through normal termination (data packet with
+ // FIN) or through a RST.
+ QuicStreamOffset byte_offset;
};
struct NET_EXPORT_PRIVATE QuicConnectionCloseFrame {
+ QuicConnectionCloseFrame();
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicConnectionCloseFrame& c);
+
QuicErrorCode error_code;
std::string error_details;
};
struct NET_EXPORT_PRIVATE QuicGoAwayFrame {
- QuicGoAwayFrame() {}
+ QuicGoAwayFrame();
QuicGoAwayFrame(QuicErrorCode error_code,
QuicStreamId last_good_stream_id,
const std::string& reason);
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicGoAwayFrame& g);
+
QuicErrorCode error_code;
QuicStreamId last_good_stream_id;
std::string reason_phrase;
};
+// Flow control updates per-stream and at the connection levoel.
+// Based on SPDY's WINDOW_UPDATE frame, but uses an absolute byte offset rather
+// than a window delta.
+// TODO(rjshade): A possible future optimization is to make stream_id and
+// byte_offset variable length, similar to stream frames.
+struct NET_EXPORT_PRIVATE QuicWindowUpdateFrame {
+ QuicWindowUpdateFrame() {}
+ QuicWindowUpdateFrame(QuicStreamId stream_id, QuicStreamOffset byte_offset);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicWindowUpdateFrame& w);
+
+ // The stream this frame applies to. 0 is a special case meaning the overall
+ // connection rather than a specific stream.
+ QuicStreamId stream_id;
+
+ // Byte offset in the stream or connection. The receiver of this frame must
+ // not send data which would result in this offset being exceeded.
+ QuicStreamOffset byte_offset;
+};
+
+// The BLOCKED frame is used to indicate to the remote endpoint that this
+// endpoint believes itself to be flow-control blocked but otherwise ready to
+// send data. The BLOCKED frame is purely advisory and optional.
+// Based on SPDY's BLOCKED frame (undocumented as of 2014-01-28).
+struct NET_EXPORT_PRIVATE QuicBlockedFrame {
+ QuicBlockedFrame() {}
+ explicit QuicBlockedFrame(QuicStreamId stream_id);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicBlockedFrame& b);
+
+ // The stream this frame applies to. 0 is a special case meaning the overall
+ // connection rather than a specific stream.
+ QuicStreamId stream_id;
+};
+
// EncryptionLevel enumerates the stages of encryption that a QUIC connection
// progresses through. When retransmitting a packet, the encryption level needs
// to be specified so that it is retransmitted at a level which the peer can
@@ -707,35 +851,21 @@ enum EncryptionLevel {
};
struct NET_EXPORT_PRIVATE QuicFrame {
- QuicFrame() {}
- explicit QuicFrame(QuicPaddingFrame* padding_frame)
- : type(PADDING_FRAME),
- padding_frame(padding_frame) {
- }
- explicit QuicFrame(QuicStreamFrame* stream_frame)
- : type(STREAM_FRAME),
- stream_frame(stream_frame) {
- }
- explicit QuicFrame(QuicAckFrame* frame)
- : type(ACK_FRAME),
- ack_frame(frame) {
- }
- explicit QuicFrame(QuicCongestionFeedbackFrame* frame)
- : type(CONGESTION_FEEDBACK_FRAME),
- congestion_feedback_frame(frame) {
- }
- explicit QuicFrame(QuicRstStreamFrame* frame)
- : type(RST_STREAM_FRAME),
- rst_stream_frame(frame) {
- }
- explicit QuicFrame(QuicConnectionCloseFrame* frame)
- : type(CONNECTION_CLOSE_FRAME),
- connection_close_frame(frame) {
- }
- explicit QuicFrame(QuicGoAwayFrame* frame)
- : type(GOAWAY_FRAME),
- goaway_frame(frame) {
- }
+ QuicFrame();
+ explicit QuicFrame(QuicPaddingFrame* padding_frame);
+ explicit QuicFrame(QuicStreamFrame* stream_frame);
+ explicit QuicFrame(QuicAckFrame* frame);
+ explicit QuicFrame(QuicCongestionFeedbackFrame* frame);
+ explicit QuicFrame(QuicRstStreamFrame* frame);
+ explicit QuicFrame(QuicConnectionCloseFrame* frame);
+ explicit QuicFrame(QuicStopWaitingFrame* frame);
+ explicit QuicFrame(QuicPingFrame* frame);
+ explicit QuicFrame(QuicGoAwayFrame* frame);
+ explicit QuicFrame(QuicWindowUpdateFrame* frame);
+ explicit QuicFrame(QuicBlockedFrame* frame);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicFrame& frame);
QuicFrameType type;
union {
@@ -743,9 +873,13 @@ struct NET_EXPORT_PRIVATE QuicFrame {
QuicStreamFrame* stream_frame;
QuicAckFrame* ack_frame;
QuicCongestionFeedbackFrame* congestion_feedback_frame;
+ QuicStopWaitingFrame* stop_waiting_frame;
+ QuicPingFrame* ping_frame;
QuicRstStreamFrame* rst_stream_frame;
QuicConnectionCloseFrame* connection_close_frame;
QuicGoAwayFrame* goaway_frame;
+ QuicWindowUpdateFrame* window_update_frame;
+ QuicBlockedFrame* blocked_frame;
};
};
@@ -763,16 +897,8 @@ struct NET_EXPORT_PRIVATE QuicFecData {
class NET_EXPORT_PRIVATE QuicData {
public:
- QuicData(const char* buffer, size_t length)
- : buffer_(buffer),
- length_(length),
- owns_buffer_(false) {}
-
- QuicData(char* buffer, size_t length, bool owns_buffer)
- : buffer_(buffer),
- length_(length),
- owns_buffer_(owns_buffer) {}
-
+ QuicData(const char* buffer, size_t length);
+ QuicData(char* buffer, size_t length, bool owns_buffer);
virtual ~QuicData();
base::StringPiece AsStringPiece() const {
@@ -796,10 +922,10 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData {
char* buffer,
size_t length,
bool owns_buffer,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool includes_version,
QuicSequenceNumberLength sequence_number_length) {
- return new QuicPacket(buffer, length, owns_buffer, guid_length,
+ return new QuicPacket(buffer, length, owns_buffer, connection_id_length,
includes_version, sequence_number_length, false);
}
@@ -807,10 +933,10 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData {
char* buffer,
size_t length,
bool owns_buffer,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool includes_version,
QuicSequenceNumberLength sequence_number_length) {
- return new QuicPacket(buffer, length, owns_buffer, guid_length,
+ return new QuicPacket(buffer, length, owns_buffer, connection_id_length,
includes_version, sequence_number_length, true);
}
@@ -827,20 +953,14 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData {
QuicPacket(char* buffer,
size_t length,
bool owns_buffer,
- QuicGuidLength guid_length,
+ QuicConnectionIdLength connection_id_length,
bool includes_version,
QuicSequenceNumberLength sequence_number_length,
- bool is_fec_packet)
- : QuicData(buffer, length, owns_buffer),
- buffer_(buffer),
- is_fec_packet_(is_fec_packet),
- guid_length_(guid_length),
- includes_version_(includes_version),
- sequence_number_length_(sequence_number_length) {}
+ bool is_fec_packet);
char* buffer_;
const bool is_fec_packet_;
- const QuicGuidLength guid_length_;
+ const QuicConnectionIdLength connection_id_length_;
const bool includes_version_;
const QuicSequenceNumberLength sequence_number_length_;
@@ -849,11 +969,8 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData {
class NET_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData {
public:
- QuicEncryptedPacket(const char* buffer, size_t length)
- : QuicData(buffer, length) {}
-
- QuicEncryptedPacket(char* buffer, size_t length, bool owns_buffer)
- : QuicData(buffer, length, owns_buffer) {}
+ QuicEncryptedPacket(const char* buffer, size_t length);
+ QuicEncryptedPacket(char* buffer, size_t length, bool owns_buffer);
// Clones the packet into a new packet which owns the buffer.
QuicEncryptedPacket* Clone() const;
@@ -882,6 +999,8 @@ class NET_EXPORT_PRIVATE RetransmittableFrames {
const QuicFrame& AddNonStreamFrame(const QuicFrame& frame);
const QuicFrames& frames() const { return frames_; }
+ IsHandshake HasCryptoHandshake() const;
+
void set_encryption_level(EncryptionLevel level);
EncryptionLevel encryption_level() const {
return encryption_level_;
@@ -914,43 +1033,38 @@ struct NET_EXPORT_PRIVATE SerializedPacket {
std::set<QuicAckNotifier*> notifiers;
};
-// A struct for functions which consume data payloads and fins.
-struct QuicConsumedData {
- QuicConsumedData(size_t bytes_consumed, bool fin_consumed)
- : bytes_consumed(bytes_consumed),
- fin_consumed(fin_consumed) {}
- // By default, gtest prints the raw bytes of an object. The bool data
- // member causes this object to have padding bytes, which causes the
- // default gtest object printer to read uninitialize memory. So we need
- // to teach gtest how to print this object.
- NET_EXPORT_PRIVATE friend std::ostream& operator<<(
- std::ostream& os, const QuicConsumedData& s);
-
- // How many bytes were consumed.
- size_t bytes_consumed;
+struct NET_EXPORT_PRIVATE TransmissionInfo {
+ // Used by STL when assigning into a map.
+ TransmissionInfo();
- // True if an incoming fin was consumed.
- bool fin_consumed;
-};
-
-enum WriteStatus {
- WRITE_STATUS_OK,
- WRITE_STATUS_BLOCKED,
- WRITE_STATUS_ERROR,
-};
+ // Constructs a Transmission with a new all_tranmissions set
+ // containing |sequence_number|.
+ TransmissionInfo(RetransmittableFrames* retransmittable_frames,
+ QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length);
-// A struct used to return the result of write calls including either the number
-// of bytes written or the error code, depending upon the status.
-struct NET_EXPORT_PRIVATE WriteResult {
- WriteResult(WriteStatus status, int bytes_written_or_error_code) :
- status(status), bytes_written(bytes_written_or_error_code) {
- }
+ // Constructs a Transmission with the specified |all_tranmissions| set
+ // and inserts |sequence_number| into it.
+ TransmissionInfo(RetransmittableFrames* retransmittable_frames,
+ QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length,
+ TransmissionType transmission_type,
+ SequenceNumberSet* all_transmissions);
- WriteStatus status;
- union {
- int bytes_written; // only valid when status is OK
- int error_code; // only valid when status is ERROR
- };
+ RetransmittableFrames* retransmittable_frames;
+ QuicSequenceNumberLength sequence_number_length;
+ // Zero when the packet is serialized, non-zero once it's sent.
+ QuicTime sent_time;
+ // Zero when the packet is serialized, non-zero once it's sent.
+ QuicByteCount bytes_sent;
+ size_t nack_count;
+ // Reason why this packet was transmitted.
+ TransmissionType transmission_type;
+ // Stores the sequence numbers of all transmissions of this packet.
+ // Can never be null.
+ SequenceNumberSet* all_transmissions;
+ // In flight packets have not been abandoned or lost.
+ bool in_flight;
};
} // namespace net
diff --git a/chromium/net/quic/quic_protocol_test.cc b/chromium/net/quic/quic_protocol_test.cc
index bf1fece981c..b1eae45d251 100644
--- a/chromium/net/quic/quic_protocol_test.cc
+++ b/chromium/net/quic/quic_protocol_test.cc
@@ -11,6 +11,19 @@ namespace net {
namespace test {
namespace {
+TEST(QuicProtocolTest, AdjustErrorForVersion) {
+ ASSERT_EQ(8, QUIC_STREAM_LAST_ERROR)
+ << "Any additions to QuicRstStreamErrorCode require an addition to "
+ << "AdjustErrorForVersion and this associated test.";
+
+ EXPECT_EQ(QUIC_STREAM_NO_ERROR,
+ AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING,
+ QUIC_VERSION_17));
+ EXPECT_EQ(QUIC_RST_FLOW_CONTROL_ACCOUNTING, AdjustErrorForVersion(
+ QUIC_RST_FLOW_CONTROL_ACCOUNTING,
+ static_cast<QuicVersion>(QUIC_VERSION_17 + 1)));
+}
+
TEST(QuicProtocolTest, MakeQuicTag) {
QuicTag tag = MakeQuicTag('A', 'B', 'C', 'D');
char bytes[4];
@@ -56,8 +69,8 @@ TEST(QuicProtocolTest, QuicVersionToQuicTag) {
#endif
// Explicitly test a specific version.
- EXPECT_EQ(MakeQuicTag('Q', '0', '1', '2'),
- QuicVersionToQuicTag(QUIC_VERSION_12));
+ EXPECT_EQ(MakeQuicTag('Q', '0', '1', '6'),
+ QuicVersionToQuicTag(QUIC_VERSION_16));
// Loop over all supported versions and make sure that we never hit the
// default case (i.e. all supported versions should be successfully converted
@@ -95,8 +108,8 @@ TEST(QuicProtocolTest, QuicTagToQuicVersion) {
#endif
// Explicitly test specific versions.
- EXPECT_EQ(QUIC_VERSION_12,
- QuicTagToQuicVersion(MakeQuicTag('Q', '0', '1', '2')));
+ EXPECT_EQ(QUIC_VERSION_16,
+ QuicTagToQuicVersion(MakeQuicTag('Q', '0', '1', '6')));
for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
QuicVersion version = kSupportedQuicVersions[i];
@@ -127,23 +140,23 @@ TEST(QuicProtocolTest, QuicTagToQuicVersionUnsupported) {
}
TEST(QuicProtocolTest, QuicVersionToString) {
- EXPECT_EQ("QUIC_VERSION_12", QuicVersionToString(QUIC_VERSION_12));
+ EXPECT_EQ("QUIC_VERSION_16", QuicVersionToString(QUIC_VERSION_16));
EXPECT_EQ("QUIC_VERSION_UNSUPPORTED",
QuicVersionToString(QUIC_VERSION_UNSUPPORTED));
- QuicVersion single_version[] = {QUIC_VERSION_12};
+ QuicVersion single_version[] = {QUIC_VERSION_16};
QuicVersionVector versions_vector;
for (size_t i = 0; i < arraysize(single_version); ++i) {
versions_vector.push_back(single_version[i]);
}
- EXPECT_EQ("QUIC_VERSION_12", QuicVersionVectorToString(versions_vector));
+ EXPECT_EQ("QUIC_VERSION_16", QuicVersionVectorToString(versions_vector));
- QuicVersion multiple_versions[] = {QUIC_VERSION_UNSUPPORTED, QUIC_VERSION_12};
+ QuicVersion multiple_versions[] = {QUIC_VERSION_UNSUPPORTED, QUIC_VERSION_16};
versions_vector.clear();
for (size_t i = 0; i < arraysize(multiple_versions); ++i) {
versions_vector.push_back(multiple_versions[i]);
}
- EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_12",
+ EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_16",
QuicVersionVectorToString(versions_vector));
// Make sure that all supported versions are present in QuicVersionToString.
diff --git a/chromium/net/quic/quic_received_packet_manager.cc b/chromium/net/quic/quic_received_packet_manager.cc
index b24ac7c671e..3b47687beb6 100644
--- a/chromium/net/quic/quic_received_packet_manager.cc
+++ b/chromium/net/quic/quic_received_packet_manager.cc
@@ -7,6 +7,7 @@
#include "base/logging.h"
#include "base/stl_util.h"
#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_connection_stats.h"
using std::make_pair;
using std::max;
@@ -25,15 +26,115 @@ const size_t kMaxPacketsAfterNewMissing = 4;
}
+QuicReceivedPacketManager::EntropyTracker::EntropyTracker()
+ : packets_entropy_hash_(0),
+ first_gap_(1),
+ largest_observed_(0) {
+}
+
+QuicReceivedPacketManager::EntropyTracker::~EntropyTracker() {}
+
+QuicPacketEntropyHash QuicReceivedPacketManager::EntropyTracker::EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const {
+ DCHECK_LE(sequence_number, largest_observed_);
+ if (sequence_number == largest_observed_) {
+ return packets_entropy_hash_;
+ }
+
+ DCHECK_GE(sequence_number, first_gap_);
+ ReceivedEntropyMap::const_iterator it =
+ packets_entropy_.upper_bound(sequence_number);
+ // When this map is empty we should only query entropy for
+ // largest_observed_, since no other entropy can be correctly
+ // calculated, because we're not storing the entropy for any prior packets.
+ // TODO(rtenneti): add support for LOG_IF_EVERY_N_SEC to chromium.
+ // LOG_IF_EVERY_N_SEC(DFATAL, it == packets_entropy_.end(), 10)
+ LOG_IF(DFATAL, it == packets_entropy_.end())
+ << "EntropyHash may be unknown. largest_received: "
+ << largest_observed_
+ << " sequence_number: " << sequence_number;
+
+ // TODO(satyamshekhar): Make this O(1).
+ QuicPacketEntropyHash hash = packets_entropy_hash_;
+ for (; it != packets_entropy_.end(); ++it) {
+ hash ^= it->second;
+ }
+ return hash;
+}
+
+void QuicReceivedPacketManager::EntropyTracker::RecordPacketEntropyHash(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ if (sequence_number < first_gap_) {
+ DVLOG(1) << "Ignoring received packet entropy for sequence_number:"
+ << sequence_number << " less than largest_peer_sequence_number:"
+ << first_gap_;
+ return;
+ }
+
+ if (sequence_number > largest_observed_) {
+ largest_observed_ = sequence_number;
+ }
+
+ packets_entropy_hash_ ^= entropy_hash;
+ DVLOG(2) << "setting cumulative received entropy hash to: "
+ << static_cast<int>(packets_entropy_hash_)
+ << " updated with sequence number " << sequence_number
+ << " entropy hash: " << static_cast<int>(entropy_hash);
+
+ packets_entropy_.insert(make_pair(sequence_number, entropy_hash));
+ AdvanceFirstGapAndGarbageCollectEntropyMap();
+}
+
+void QuicReceivedPacketManager::EntropyTracker::SetCumulativeEntropyUpTo(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ DCHECK_LE(sequence_number, largest_observed_);
+ if (sequence_number < first_gap_) {
+ DVLOG(1) << "Ignoring set entropy at:" << sequence_number
+ << " less than first_gap_:" << first_gap_;
+ return;
+ }
+ // Compute the current entropy based on the hash.
+ packets_entropy_hash_ = entropy_hash;
+ ReceivedEntropyMap::iterator it =
+ packets_entropy_.lower_bound(sequence_number);
+ // TODO(satyamshekhar): Make this O(1).
+ for (; it != packets_entropy_.end(); ++it) {
+ packets_entropy_hash_ ^= it->second;
+ }
+ // Update first_gap_ and discard old entropies.
+ first_gap_ = sequence_number;
+ packets_entropy_.erase(
+ packets_entropy_.begin(),
+ packets_entropy_.lower_bound(sequence_number));
+
+ // Garbage collect entries from the beginning of the map.
+ AdvanceFirstGapAndGarbageCollectEntropyMap();
+}
+
+void QuicReceivedPacketManager::EntropyTracker::
+AdvanceFirstGapAndGarbageCollectEntropyMap() {
+ while (!packets_entropy_.empty()) {
+ ReceivedEntropyMap::iterator it = packets_entropy_.begin();
+ if (it->first != first_gap_) {
+ DCHECK_GT(it->first, first_gap_);
+ break;
+ }
+ packets_entropy_.erase(it);
+ ++first_gap_;
+ }
+}
+
QuicReceivedPacketManager::QuicReceivedPacketManager(
- CongestionFeedbackType congestion_type)
- : packets_entropy_hash_(0),
- largest_sequence_number_(0),
- peer_largest_observed_packet_(0),
+ CongestionFeedbackType congestion_type,
+ QuicConnectionStats* stats)
+ : peer_largest_observed_packet_(0),
least_packet_awaited_by_peer_(1),
peer_least_packet_awaiting_ack_(0),
time_largest_observed_(QuicTime::Zero()),
- receive_algorithm_(ReceiveAlgorithmInterface::Create(congestion_type)) {
+ receive_algorithm_(ReceiveAlgorithmInterface::Create(congestion_type)),
+ stats_(stats) {
received_info_.largest_observed = 0;
received_info_.entropy_hash = 0;
}
@@ -43,33 +144,46 @@ QuicReceivedPacketManager::~QuicReceivedPacketManager() {}
void QuicReceivedPacketManager::RecordPacketReceived(
QuicByteCount bytes,
const QuicPacketHeader& header,
- QuicTime receipt_time,
- bool revived) {
+ QuicTime receipt_time) {
QuicPacketSequenceNumber sequence_number = header.packet_sequence_number;
DCHECK(IsAwaitingPacket(sequence_number));
InsertMissingPacketsBetween(
&received_info_,
max(received_info_.largest_observed + 1, peer_least_packet_awaiting_ack_),
- header.packet_sequence_number);
+ sequence_number);
- if (received_info_.largest_observed > header.packet_sequence_number) {
+ if (received_info_.largest_observed > sequence_number) {
// We've gotten one of the out of order packets - remove it from our
// "missing packets" list.
DVLOG(1) << "Removing " << sequence_number << " from missing list";
received_info_.missing_packets.erase(sequence_number);
+
+ // Record how out of order stats.
+ ++stats_->packets_reordered;
+ uint32 sequence_gap = received_info_.largest_observed - sequence_number;
+ stats_->max_sequence_reordering =
+ max(stats_->max_sequence_reordering, sequence_gap);
+ uint32 reordering_time_us =
+ receipt_time.Subtract(time_largest_observed_).ToMicroseconds();
+ stats_->max_time_reordering_us = max(stats_->max_time_reordering_us,
+ reordering_time_us);
}
- if (header.packet_sequence_number > received_info_.largest_observed) {
- received_info_.largest_observed = header.packet_sequence_number;
+ if (sequence_number > received_info_.largest_observed) {
+ received_info_.largest_observed = sequence_number;
time_largest_observed_ = receipt_time;
}
- RecordPacketEntropyHash(sequence_number, header.entropy_hash);
+ entropy_tracker_.RecordPacketEntropyHash(sequence_number,
+ header.entropy_hash);
- // Don't update the receive algorithm for revived packets.
- if (!revived) {
- receive_algorithm_->RecordIncomingPacket(
- bytes, sequence_number, receipt_time, revived);
- }
+ receive_algorithm_->RecordIncomingPacket(
+ bytes, sequence_number, receipt_time);
+}
+
+void QuicReceivedPacketManager::RecordPacketRevived(
+ QuicPacketSequenceNumber sequence_number) {
+ LOG_IF(DFATAL, !IsAwaitingPacket(sequence_number));
+ received_info_.revived_packets.insert(sequence_number);
}
bool QuicReceivedPacketManager::IsMissing(
@@ -104,23 +218,6 @@ void QuicReceivedPacketManager::UpdateReceivedPacketInfo(
approximate_now.Subtract(time_largest_observed_);
}
-void QuicReceivedPacketManager::RecordPacketEntropyHash(
- QuicPacketSequenceNumber sequence_number,
- QuicPacketEntropyHash entropy_hash) {
- if (sequence_number < largest_sequence_number_) {
- DVLOG(1) << "Ignoring received packet entropy for sequence_number:"
- << sequence_number << " less than largest_peer_sequence_number:"
- << largest_sequence_number_;
- return;
- }
- packets_entropy_.insert(make_pair(sequence_number, entropy_hash));
- packets_entropy_hash_ ^= entropy_hash;
- DVLOG(2) << "setting cumulative received entropy hash to: "
- << static_cast<int>(packets_entropy_hash_)
- << " updated with sequence number " << sequence_number
- << " entropy hash: " << static_cast<int>(entropy_hash);
-}
-
bool QuicReceivedPacketManager::GenerateCongestionFeedback(
QuicCongestionFeedbackFrame* feedback) {
return receive_algorithm_->GenerateCongestionFeedback(feedback);
@@ -128,74 +225,27 @@ bool QuicReceivedPacketManager::GenerateCongestionFeedback(
QuicPacketEntropyHash QuicReceivedPacketManager::EntropyHash(
QuicPacketSequenceNumber sequence_number) const {
- DCHECK_LE(sequence_number, received_info_.largest_observed);
- DCHECK_GE(sequence_number, largest_sequence_number_);
- if (sequence_number == received_info_.largest_observed) {
- return packets_entropy_hash_;
- }
-
- ReceivedEntropyMap::const_iterator it =
- packets_entropy_.upper_bound(sequence_number);
- // When this map is empty we should only query entropy for
- // received_info_.largest_observed, since no other entropy can be correctly
- // calculated, because we're not storing the entropy for any prior packets.
- // TODO(rtenneti): add support for LOG_IF_EVERY_N_SEC to chromium.
- // LOG_IF_EVERY_N_SEC(DFATAL, it == packets_entropy_.end(), 10)
- LOG_IF(DFATAL, it == packets_entropy_.end())
- << "EntropyHash may be unknown. largest_received: "
- << received_info_.largest_observed
- << " sequence_number: " << sequence_number;
-
- // TODO(satyamshekhar): Make this O(1).
- QuicPacketEntropyHash hash = packets_entropy_hash_;
- for (; it != packets_entropy_.end(); ++it) {
- hash ^= it->second;
- }
- return hash;
-}
-
-void QuicReceivedPacketManager::RecalculateEntropyHash(
- QuicPacketSequenceNumber peer_least_unacked,
- QuicPacketEntropyHash entropy_hash) {
- DCHECK_LE(peer_least_unacked, received_info_.largest_observed);
- if (peer_least_unacked < largest_sequence_number_) {
- DVLOG(1) << "Ignoring received peer_least_unacked:" << peer_least_unacked
- << " less than largest_peer_sequence_number:"
- << largest_sequence_number_;
- return;
- }
- largest_sequence_number_ = peer_least_unacked;
- packets_entropy_hash_ = entropy_hash;
- ReceivedEntropyMap::iterator it =
- packets_entropy_.lower_bound(peer_least_unacked);
- // TODO(satyamshekhar): Make this O(1).
- for (; it != packets_entropy_.end(); ++it) {
- packets_entropy_hash_ ^= it->second;
- }
- // Discard entropies before least unacked.
- packets_entropy_.erase(
- packets_entropy_.begin(),
- packets_entropy_.lower_bound(
- min(peer_least_unacked, received_info_.largest_observed)));
+ return entropy_tracker_.EntropyHash(sequence_number);
}
void QuicReceivedPacketManager::UpdatePacketInformationReceivedByPeer(
- const QuicAckFrame& incoming_ack) {
+ const ReceivedPacketInfo& received_info) {
// ValidateAck should fail if largest_observed ever shrinks.
- DCHECK_LE(peer_largest_observed_packet_,
- incoming_ack.received_info.largest_observed);
- peer_largest_observed_packet_ = incoming_ack.received_info.largest_observed;
+ DCHECK_LE(peer_largest_observed_packet_, received_info.largest_observed);
+ peer_largest_observed_packet_ = received_info.largest_observed;
- if (incoming_ack.received_info.missing_packets.empty()) {
+ if (received_info.missing_packets.empty()) {
least_packet_awaited_by_peer_ = peer_largest_observed_packet_ + 1;
} else {
- least_packet_awaited_by_peer_ =
- *(incoming_ack.received_info.missing_packets.begin());
+ least_packet_awaited_by_peer_ = *(received_info.missing_packets.begin());
}
}
bool QuicReceivedPacketManager::DontWaitForPacketsBefore(
QuicPacketSequenceNumber least_unacked) {
+ received_info_.revived_packets.erase(
+ received_info_.revived_packets.begin(),
+ received_info_.revived_packets.lower_bound(least_unacked));
size_t missing_packets_count = received_info_.missing_packets.size();
received_info_.missing_packets.erase(
received_info_.missing_packets.begin(),
@@ -204,22 +254,19 @@ bool QuicReceivedPacketManager::DontWaitForPacketsBefore(
}
void QuicReceivedPacketManager::UpdatePacketInformationSentByPeer(
- const QuicAckFrame& incoming_ack) {
+ const QuicStopWaitingFrame& stop_waiting) {
// ValidateAck() should fail if peer_least_packet_awaiting_ack_ shrinks.
- DCHECK_LE(peer_least_packet_awaiting_ack_,
- incoming_ack.sent_info.least_unacked);
- if (incoming_ack.sent_info.least_unacked > peer_least_packet_awaiting_ack_) {
- bool missed_packets =
- DontWaitForPacketsBefore(incoming_ack.sent_info.least_unacked);
- if (missed_packets || incoming_ack.sent_info.least_unacked >
- received_info_.largest_observed + 1) {
+ DCHECK_LE(peer_least_packet_awaiting_ack_, stop_waiting.least_unacked);
+ if (stop_waiting.least_unacked > peer_least_packet_awaiting_ack_) {
+ bool missed_packets = DontWaitForPacketsBefore(stop_waiting.least_unacked);
+ if (missed_packets) {
DVLOG(1) << "Updating entropy hashed since we missed packets";
// There were some missing packets that we won't ever get now. Recalculate
// the received entropy hash.
- RecalculateEntropyHash(incoming_ack.sent_info.least_unacked,
- incoming_ack.sent_info.entropy_hash);
+ entropy_tracker_.SetCumulativeEntropyUpTo(stop_waiting.least_unacked,
+ stop_waiting.entropy_hash);
}
- peer_least_packet_awaiting_ack_ = incoming_ack.sent_info.least_unacked;
+ peer_least_packet_awaiting_ack_ = stop_waiting.least_unacked;
}
DCHECK(received_info_.missing_packets.empty() ||
*received_info_.missing_packets.begin() >=
diff --git a/chromium/net/quic/quic_received_packet_manager.h b/chromium/net/quic/quic_received_packet_manager.h
index 9e2fb59c241..1b283316e33 100644
--- a/chromium/net/quic/quic_received_packet_manager.h
+++ b/chromium/net/quic/quic_received_packet_manager.h
@@ -15,29 +15,98 @@
namespace net {
namespace test {
+class EntropyTrackerPeer;
class QuicConnectionPeer;
class QuicReceivedPacketManagerPeer;
} // namespace test
+struct QuicConnectionStats;
+
// Records all received packets by a connection and tracks their entropy.
// Also calculates the correct entropy for the framer when it truncates an ack
// frame being serialized.
class NET_EXPORT_PRIVATE QuicReceivedPacketManager :
public QuicReceivedEntropyHashCalculatorInterface {
public:
- explicit QuicReceivedPacketManager(CongestionFeedbackType congestion_type);
+ class NET_EXPORT_PRIVATE EntropyTracker {
+ public:
+ EntropyTracker();
+ ~EntropyTracker();
+
+ // Compute the XOR of the entropy of all received packets up to
+ // and including sequence_number.
+ // Requires that either:
+ // sequence_number == largest_observed_
+ // or:
+ // sequence_number > first_gap_ &&
+ // sequence_number < largest_observed_ &&
+ // sequence_number in packets_entropy_
+ QuicPacketEntropyHash EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Record the received entropy hash against |sequence_number|.
+ // Performs garbage collection to advance first_gap_ if
+ // sequence_number == first_gap_.
+ void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash);
+
+ // Sets the entropy hash up to but not including a sequence number based
+ // on the hash provided by a StopWaiting frame. Clears older packet
+ // entropy entries and performs garbage collection up to the first gap.
+ void SetCumulativeEntropyUpTo(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash);
+
+ private:
+ friend class test::EntropyTrackerPeer;
+
+ typedef std::map<QuicPacketSequenceNumber,
+ QuicPacketEntropyHash> ReceivedEntropyMap;
+
+ // Recomputes first_gap_ and removes packets_entropy_ entries that are no
+ // longer needed to compute EntropyHash.
+ void AdvanceFirstGapAndGarbageCollectEntropyMap();
+
+ // TODO(satyamshekhar): Can be optimized using an interval set like data
+ // structure.
+ // Map of received sequence numbers to their corresponding entropy.
+ // Stores an entry for every received packet whose sequence_number is larger
+ // than first_gap_. Packets without the entropy bit set have an entropy
+ // value of 0.
+ // TODO(ianswett): When the entropy flag is off, the entropy
+ // should not be 0.
+ ReceivedEntropyMap packets_entropy_;
+
+ // Cumulative hash of entropy of all received packets.
+ QuicPacketEntropyHash packets_entropy_hash_;
+
+ // Sequence number of the first packet that we do not know the entropy of.
+ // If there are no gaps in the received packet sequence,
+ // packets_entropy_ will be empty and first_gap_ will be equal to
+ // 'largest_observed_ + 1' since that's the first packet for which
+ // entropy is unknown. If there are gaps, packets_entropy_ will
+ // contain entries for all received packets with sequence_number >
+ // first_gap_.
+ QuicPacketSequenceNumber first_gap_;
+
+ // Sequence number of the largest observed packet.
+ QuicPacketSequenceNumber largest_observed_;
+
+ DISALLOW_COPY_AND_ASSIGN(EntropyTracker);
+ };
+
+ explicit QuicReceivedPacketManager(CongestionFeedbackType congestion_type,
+ QuicConnectionStats* stats);
virtual ~QuicReceivedPacketManager();
// Updates the internal state concerning which packets have been received.
// bytes: the packet size in bytes including Quic Headers.
// header: the packet header.
// timestamp: the arrival time of the packet.
- // revived: true if the packet was lost and then recovered with help of a
- // FEC packet.
void RecordPacketReceived(QuicByteCount bytes,
const QuicPacketHeader& header,
- QuicTime receipt_time,
- bool revived);
+ QuicTime receipt_time);
+
+ void RecordPacketRevived(QuicPacketSequenceNumber sequence_number);
// Checks whether |sequence_number| is missing and less than largest observed.
bool IsMissing(QuicPacketSequenceNumber sequence_number);
@@ -62,12 +131,12 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager :
virtual QuicPacketEntropyHash EntropyHash(
QuicPacketSequenceNumber sequence_number) const OVERRIDE;
- // These two are called by OnAckFrame.
- //
- // Updates internal state based on |incoming_ack.received_info|.
- void UpdatePacketInformationReceivedByPeer(const QuicAckFrame& incoming_ack);
- // Updates internal state based on |incoming_ack.sent_info|.
- void UpdatePacketInformationSentByPeer(const QuicAckFrame& incoming_ack);
+ // Updates internal state based on |received_info|.
+ void UpdatePacketInformationReceivedByPeer(
+ const ReceivedPacketInfo& received_nfo);
+ // Updates internal state based on |stop_waiting|.
+ void UpdatePacketInformationSentByPeer(
+ const QuicStopWaitingFrame& stop_waiting);
// Returns whether the peer is missing packets.
bool HasMissingPackets();
@@ -92,40 +161,14 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager :
friend class test::QuicConnectionPeer;
friend class test::QuicReceivedPacketManagerPeer;
- typedef std::map<QuicPacketSequenceNumber,
- QuicPacketEntropyHash> ReceivedEntropyMap;
-
- // Record the received entropy hash against |sequence_number|.
- void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number,
- QuicPacketEntropyHash entropy_hash);
-
- // Recalculate the entropy hash and clears old packet entropies,
- // now that the sender sent us the |entropy_hash| for packets up to,
- // but not including, |peer_least_unacked|.
- void RecalculateEntropyHash(QuicPacketSequenceNumber peer_least_unacked,
- QuicPacketEntropyHash entropy_hash);
-
// Deletes all missing packets before least unacked. The connection won't
// process any packets with sequence number before |least_unacked| that it
// received after this call. Returns true if there were missing packets before
// |least_unacked| unacked, false otherwise.
bool DontWaitForPacketsBefore(QuicPacketSequenceNumber least_unacked);
- // TODO(satyamshekhar): Can be optimized using an interval set like data
- // structure.
- // Map of received sequence numbers to their corresponding entropy.
- // Every received packet has an entry, and packets without the entropy bit set
- // have an entropy value of 0.
- // TODO(ianswett): When the entropy flag is off, the entropy should not be 0.
- ReceivedEntropyMap packets_entropy_;
-
- // Cumulative hash of entropy of all received packets.
- QuicPacketEntropyHash packets_entropy_hash_;
-
- // The largest sequence number cleared by RecalculateEntropyHash.
- // Received entropy cannot be calculated for numbers less than it.
- QuicPacketSequenceNumber largest_sequence_number_;
-
+ // Tracks entropy hashes of received packets.
+ EntropyTracker entropy_tracker_;
// Track some peer state so we can do less bookkeeping.
// Largest sequence number that the peer has observed. Mostly received,
@@ -146,6 +189,10 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager :
QuicTime time_largest_observed_;
scoped_ptr<ReceiveAlgorithmInterface> receive_algorithm_;
+
+ QuicConnectionStats* stats_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicReceivedPacketManager);
};
} // namespace net
diff --git a/chromium/net/quic/quic_received_packet_manager_test.cc b/chromium/net/quic/quic_received_packet_manager_test.cc
index 9d11129f0ff..b5793092980 100644
--- a/chromium/net/quic/quic_received_packet_manager_test.cc
+++ b/chromium/net/quic/quic_received_packet_manager_test.cc
@@ -7,6 +7,7 @@
#include <algorithm>
#include <vector>
+#include "net/quic/quic_connection_stats.h"
#include "net/quic/test_tools/quic_received_packet_manager_peer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -17,20 +18,191 @@ using std::vector;
namespace net {
namespace test {
+
+class EntropyTrackerPeer {
+ public:
+ static QuicPacketSequenceNumber first_gap(
+ const QuicReceivedPacketManager::EntropyTracker& tracker) {
+ return tracker.first_gap_;
+ }
+ static QuicPacketSequenceNumber largest_observed(
+ const QuicReceivedPacketManager::EntropyTracker& tracker) {
+ return tracker.largest_observed_;
+ }
+ static int packets_entropy_size(
+ const QuicReceivedPacketManager::EntropyTracker& tracker) {
+ return tracker.packets_entropy_.size();
+ }
+ static bool IsTrackingPacket(
+ const QuicReceivedPacketManager::EntropyTracker& tracker,
+ QuicPacketSequenceNumber sequence_number) {
+ return tracker.packets_entropy_.find(sequence_number) !=
+ tracker.packets_entropy_.end();
+ }
+};
+
namespace {
+// Entropy of individual packets is not tracked if there are no gaps.
+TEST(EntropyTrackerTest, NoGaps) {
+ QuicReceivedPacketManager::EntropyTracker tracker;
+
+ tracker.RecordPacketEntropyHash(1, 23);
+ tracker.RecordPacketEntropyHash(2, 42);
+
+ EXPECT_EQ(23 ^ 42, tracker.EntropyHash(2));
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+
+ EXPECT_EQ(2u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2));
+}
+
+// Entropy of individual packets is tracked as long as there are gaps.
+// Filling the first gap results in entropy getting garbage collected.
+TEST(EntropyTrackerTest, FillGaps) {
+ QuicReceivedPacketManager::EntropyTracker tracker;
+
+ tracker.RecordPacketEntropyHash(2, 5);
+ tracker.RecordPacketEntropyHash(5, 17);
+ tracker.RecordPacketEntropyHash(6, 23);
+ tracker.RecordPacketEntropyHash(9, 42);
+
+ EXPECT_EQ(1u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(4, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(5, tracker.EntropyHash(2));
+ EXPECT_EQ(5 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(5 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill the gap at 1.
+ tracker.RecordPacketEntropyHash(1, 2);
+
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(3, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(2 ^ 5 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill the gap at 4.
+ tracker.RecordPacketEntropyHash(4, 2);
+
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(4, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(5, tracker.EntropyHash(4));
+ EXPECT_EQ(5 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(5 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 3));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 4));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill the gap at 3. Entropy for packets 3 to 6 are forgotten.
+ tracker.RecordPacketEntropyHash(3, 2);
+
+ EXPECT_EQ(7u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(1, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 3));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 4));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill in the rest.
+ tracker.RecordPacketEntropyHash(7, 2);
+ tracker.RecordPacketEntropyHash(8, 2);
+
+ EXPECT_EQ(10u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+}
+
+TEST(EntropyTrackerTest, SetCumulativeEntropyUpTo) {
+ QuicReceivedPacketManager::EntropyTracker tracker;
+
+ tracker.RecordPacketEntropyHash(2, 5);
+ tracker.RecordPacketEntropyHash(5, 17);
+ tracker.RecordPacketEntropyHash(6, 23);
+ tracker.RecordPacketEntropyHash(9, 42);
+
+ EXPECT_EQ(1u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(4, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ // Inform the tracker about value of the hash at a gap.
+ tracker.SetCumulativeEntropyUpTo(3, 7);
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(3, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(7 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(7 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(7 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ // Inform the tracker about value of the hash at a known location.
+ tracker.SetCumulativeEntropyUpTo(6, 1);
+ EXPECT_EQ(7u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(1, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(1 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ // Inform the tracker about value of the hash at the last location.
+ tracker.SetCumulativeEntropyUpTo(9, 21);
+ EXPECT_EQ(10u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(42 ^ 21, tracker.EntropyHash(9));
+}
+
class QuicReceivedPacketManagerTest : public ::testing::Test {
protected:
- QuicReceivedPacketManagerTest() : received_manager_(kTCP) { }
+ QuicReceivedPacketManagerTest() : received_manager_(kTCP, &stats_) {}
- void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number,
- QuicPacketEntropyHash entropy_hash) {
+ void RecordPacketReceipt(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ RecordPacketReceipt(sequence_number, entropy_hash, QuicTime::Zero());
+ }
+
+ void RecordPacketReceipt(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash,
+ QuicTime receipt_time) {
QuicPacketHeader header;
header.packet_sequence_number = sequence_number;
header.entropy_hash = entropy_hash;
- received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero(), false);
+ received_manager_.RecordPacketReceived(0u, header, receipt_time);
}
+ QuicConnectionStats stats_;
QuicReceivedPacketManager received_manager_;
};
@@ -43,8 +215,7 @@ TEST_F(QuicReceivedPacketManagerTest, ReceivedPacketEntropyHash) {
entropies.push_back(make_pair(8, 34));
for (size_t i = 0; i < entropies.size(); ++i) {
- RecordPacketEntropyHash(entropies[i].first,
- entropies[i].second);
+ RecordPacketReceipt(entropies[i].first, entropies[i].second);
}
sort(entropies.begin(), entropies.end());
@@ -56,58 +227,68 @@ TEST_F(QuicReceivedPacketManagerTest, ReceivedPacketEntropyHash) {
hash ^= entropies[index].second;
++index;
}
+ if (i < 3) continue;
EXPECT_EQ(hash, received_manager_.EntropyHash(i));
}
+ // Reorder by 5 when 2 is received after 7.
+ EXPECT_EQ(5u, stats_.max_sequence_reordering);
+ EXPECT_EQ(0u, stats_.max_time_reordering_us);
+ EXPECT_EQ(2u, stats_.packets_reordered);
}
TEST_F(QuicReceivedPacketManagerTest, EntropyHashBelowLeastObserved) {
EXPECT_EQ(0, received_manager_.EntropyHash(0));
- RecordPacketEntropyHash(4, 5);
+ RecordPacketReceipt(4, 5);
EXPECT_EQ(0, received_manager_.EntropyHash(3));
}
TEST_F(QuicReceivedPacketManagerTest, EntropyHashAboveLargestObserved) {
EXPECT_EQ(0, received_manager_.EntropyHash(0));
- RecordPacketEntropyHash(4, 5);
+ RecordPacketReceipt(4, 5);
EXPECT_EQ(0, received_manager_.EntropyHash(3));
}
-TEST_F(QuicReceivedPacketManagerTest, RecalculateEntropyHash) {
+TEST_F(QuicReceivedPacketManagerTest, SetCumulativeEntropyUpTo) {
vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies;
entropies.push_back(make_pair(1, 12));
entropies.push_back(make_pair(2, 1));
entropies.push_back(make_pair(3, 33));
entropies.push_back(make_pair(4, 3));
- entropies.push_back(make_pair(5, 34));
- entropies.push_back(make_pair(6, 29));
+ entropies.push_back(make_pair(6, 34));
+ entropies.push_back(make_pair(7, 29));
QuicPacketEntropyHash entropy_hash = 0;
for (size_t i = 0; i < entropies.size(); ++i) {
- RecordPacketEntropyHash(entropies[i].first, entropies[i].second);
+ RecordPacketReceipt(entropies[i].first, entropies[i].second);
entropy_hash ^= entropies[i].second;
}
- EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6));
+ EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7));
- // Now set the entropy hash up to 4 to be 100.
+ // Now set the entropy hash up to 5 to be 100.
entropy_hash ^= 100;
- for (size_t i = 0; i < 3; ++i) {
+ for (size_t i = 0; i < 4; ++i) {
entropy_hash ^= entropies[i].second;
}
- QuicReceivedPacketManagerPeer::RecalculateEntropyHash(
- &received_manager_, 4, 100);
- EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6));
+ QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo(
+ &received_manager_, 5, 100);
+ EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7));
- QuicReceivedPacketManagerPeer::RecalculateEntropyHash(
+ QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo(
&received_manager_, 1, 50);
- EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6));
+ EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7));
+
+ // No reordering.
+ EXPECT_EQ(0u, stats_.max_sequence_reordering);
+ EXPECT_EQ(0u, stats_.max_time_reordering_us);
+ EXPECT_EQ(0u, stats_.packets_reordered);
}
TEST_F(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) {
QuicPacketHeader header;
header.packet_sequence_number = 2u;
- received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero(), false);
+ received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero());
header.packet_sequence_number = 7u;
- received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero(), false);
+ received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero());
EXPECT_TRUE(received_manager_.IsAwaitingPacket(3u));
EXPECT_TRUE(received_manager_.IsAwaitingPacket(6u));
EXPECT_TRUE(QuicReceivedPacketManagerPeer::DontWaitForPacketsBefore(
@@ -120,7 +301,7 @@ TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedPacketInfo) {
QuicPacketHeader header;
header.packet_sequence_number = 2u;
QuicTime two_ms = QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(2));
- received_manager_.RecordPacketReceived(0u, header, two_ms, false);
+ received_manager_.RecordPacketReceived(0u, header, two_ms);
ReceivedPacketInfo info;
received_manager_.UpdateReceivedPacketInfo(&info, QuicTime::Zero());
@@ -136,6 +317,17 @@ TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedPacketInfo) {
info.delta_time_largest_observed);
}
+TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedConnectionStats) {
+ RecordPacketReceipt(1, 0);
+ RecordPacketReceipt(6, 0);
+ RecordPacketReceipt(
+ 2, 0, QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1)));
+
+ EXPECT_EQ(4u, stats_.max_sequence_reordering);
+ EXPECT_EQ(1000u, stats_.max_time_reordering_us);
+ EXPECT_EQ(1u, stats_.packets_reordered);
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_reliable_client_stream.cc b/chromium/net/quic/quic_reliable_client_stream.cc
index af12b8b222b..4eb74d6a31d 100644
--- a/chromium/net/quic/quic_reliable_client_stream.cc
+++ b/chromium/net/quic/quic_reliable_client_stream.cc
@@ -7,7 +7,7 @@
#include "base/callback_helpers.h"
#include "net/base/net_errors.h"
#include "net/quic/quic_session.h"
-#include "net/spdy/write_blocked_list.h"
+#include "net/quic/quic_write_blocked_list.h"
namespace net {
@@ -59,7 +59,7 @@ QuicPriority QuicReliableClientStream::EffectivePriority() const {
if (delegate_ && delegate_->HasSendHeadersComplete()) {
return QuicDataStream::EffectivePriority();
}
- return kHighestPriority;
+ return QuicWriteBlockedList::kHighestPriority;
}
int QuicReliableClientStream::WriteStreamData(
@@ -69,7 +69,7 @@ int QuicReliableClientStream::WriteStreamData(
// We should not have data buffered.
DCHECK(!HasBufferedData());
// Writes the data, or buffers it.
- WriteOrBufferData(data, fin);
+ WriteOrBufferData(data, fin, NULL);
if (!HasBufferedData()) {
return OK;
}
@@ -93,9 +93,7 @@ void QuicReliableClientStream::OnError(int error) {
}
bool QuicReliableClientStream::CanWrite(const CompletionCallback& callback) {
- bool can_write = session()->connection()->CanWrite(
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- id() == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE);
+ bool can_write = session()->connection()->CanWrite(HAS_RETRANSMITTABLE_DATA);
if (!can_write) {
session()->MarkWriteBlocked(id(), EffectivePriority());
DCHECK(callback_.is_null());
diff --git a/chromium/net/quic/quic_reliable_client_stream_test.cc b/chromium/net/quic/quic_reliable_client_stream_test.cc
index 081186ec846..caa697aa484 100644
--- a/chromium/net/quic/quic_reliable_client_stream_test.cc
+++ b/chromium/net/quic/quic_reliable_client_stream_test.cc
@@ -12,6 +12,7 @@
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
+using testing::AnyNumber;
using testing::Return;
using testing::StrEq;
using testing::_;
@@ -20,7 +21,7 @@ namespace net {
namespace test {
namespace {
-const QuicGuid kStreamId = 3;
+const QuicConnectionId kStreamId = 3;
class MockDelegate : public QuicReliableClientStream::Delegate {
public:
@@ -37,12 +38,14 @@ class MockDelegate : public QuicReliableClientStream::Delegate {
DISALLOW_COPY_AND_ASSIGN(MockDelegate);
};
-class QuicReliableClientStreamTest : public ::testing::Test {
+class QuicReliableClientStreamTest
+ : public ::testing::TestWithParam<QuicVersion> {
public:
QuicReliableClientStreamTest()
- : session_(new MockConnection(false)),
- stream_(kStreamId, &session_, BoundNetLog()) {
- stream_.SetDelegate(&delegate_);
+ : session_(new MockConnection(false, SupportedVersions(GetParam()))) {
+ stream_ = new QuicReliableClientStream(kStreamId, &session_, BoundNetLog());
+ session_.ActivateStream(stream_);
+ stream_->SetDelegate(&delegate_);
}
void InitializeHeaders() {
@@ -77,37 +80,39 @@ class QuicReliableClientStreamTest : public ::testing::Test {
testing::StrictMock<MockDelegate> delegate_;
MockSession session_;
- QuicReliableClientStream stream_;
+ QuicReliableClientStream* stream_;
QuicCryptoClientConfig crypto_config_;
SpdyHeaderBlock headers_;
};
-TEST_F(QuicReliableClientStreamTest, OnFinRead) {
+INSTANTIATE_TEST_CASE_P(Version, QuicReliableClientStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicReliableClientStreamTest, OnFinRead) {
InitializeHeaders();
- QuicSpdyCompressor compressor;
- string compressed_headers = compressor.CompressHeaders(headers_);
- QuicStreamFrame frame1(kStreamId, false, 0, MakeIOVector(compressed_headers));
string uncompressed_headers =
SpdyUtils::SerializeUncompressedHeaders(headers_);
EXPECT_CALL(delegate_, OnDataReceived(StrEq(uncompressed_headers.data()),
uncompressed_headers.size()));
- stream_.OnStreamFrame(frame1);
+ QuicStreamOffset offset = 0;
+ stream_->OnStreamHeaders(uncompressed_headers);
+ stream_->OnStreamHeadersComplete(false, uncompressed_headers.length());
IOVector iov;
- QuicStreamFrame frame2(kStreamId, true, compressed_headers.length(), iov);
+ QuicStreamFrame frame2(kStreamId, true, offset, iov);
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
- stream_.OnStreamFrame(frame2);
+ stream_->OnStreamFrame(frame2);
}
-TEST_F(QuicReliableClientStreamTest, ProcessData) {
+TEST_P(QuicReliableClientStreamTest, ProcessData) {
const char data[] = "hello world!";
EXPECT_CALL(delegate_, OnDataReceived(StrEq(data), arraysize(data)));
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
- EXPECT_EQ(arraysize(data), stream_.ProcessData(data, arraysize(data)));
+ EXPECT_EQ(arraysize(data), stream_->ProcessData(data, arraysize(data)));
}
-TEST_F(QuicReliableClientStreamTest, ProcessDataWithError) {
+TEST_P(QuicReliableClientStreamTest, ProcessDataWithError) {
const char data[] = "hello world!";
EXPECT_CALL(delegate_,
OnDataReceived(StrEq(data),
@@ -115,50 +120,50 @@ TEST_F(QuicReliableClientStreamTest, ProcessDataWithError) {
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
- EXPECT_EQ(0u, stream_.ProcessData(data, arraysize(data)));
+ EXPECT_EQ(0u, stream_->ProcessData(data, arraysize(data)));
}
-TEST_F(QuicReliableClientStreamTest, OnError) {
+TEST_P(QuicReliableClientStreamTest, OnError) {
EXPECT_CALL(delegate_, OnError(ERR_INTERNET_DISCONNECTED));
- stream_.OnError(ERR_INTERNET_DISCONNECTED);
- EXPECT_FALSE(stream_.GetDelegate());
+ stream_->OnError(ERR_INTERNET_DISCONNECTED);
+ EXPECT_FALSE(stream_->GetDelegate());
}
-TEST_F(QuicReliableClientStreamTest, WriteStreamData) {
+TEST_P(QuicReliableClientStreamTest, WriteStreamData) {
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
const char kData1[] = "hello world";
const size_t kDataLen = arraysize(kData1);
// All data written.
- EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _, _)).WillOnce(
+ EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen, true)));
TestCompletionCallback callback;
- EXPECT_EQ(OK, stream_.WriteStreamData(base::StringPiece(kData1, kDataLen),
- true, callback.callback()));
+ EXPECT_EQ(OK, stream_->WriteStreamData(base::StringPiece(kData1, kDataLen),
+ true, callback.callback()));
}
-TEST_F(QuicReliableClientStreamTest, WriteStreamDataAsync) {
- EXPECT_CALL(delegate_, HasSendHeadersComplete());
+TEST_P(QuicReliableClientStreamTest, WriteStreamDataAsync) {
+ EXPECT_CALL(delegate_, HasSendHeadersComplete()).Times(AnyNumber());
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
const char kData1[] = "hello world";
const size_t kDataLen = arraysize(kData1);
// No data written.
- EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _, _)).WillOnce(
+ EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce(
Return(QuicConsumedData(0, false)));
TestCompletionCallback callback;
EXPECT_EQ(ERR_IO_PENDING,
- stream_.WriteStreamData(base::StringPiece(kData1, kDataLen),
- true, callback.callback()));
+ stream_->WriteStreamData(base::StringPiece(kData1, kDataLen),
+ true, callback.callback()));
ASSERT_FALSE(callback.have_result());
// All data written.
- EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _, _)).WillOnce(
+ EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen, true)));
- stream_.OnCanWrite();
+ stream_->OnCanWrite();
ASSERT_TRUE(callback.have_result());
EXPECT_EQ(OK, callback.WaitForResult());
}
diff --git a/chromium/net/quic/quic_sent_entropy_manager.h b/chromium/net/quic/quic_sent_entropy_manager.h
index a101e738d06..c89d97730de 100644
--- a/chromium/net/quic/quic_sent_entropy_manager.h
+++ b/chromium/net/quic/quic_sent_entropy_manager.h
@@ -51,6 +51,8 @@ class NET_EXPORT_PRIVATE QuicSentEntropyManager {
// Cumulative hash of entropy of all sent packets.
QuicPacketEntropyHash packets_entropy_hash_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSentEntropyManager);
};
} // namespace net
diff --git a/chromium/net/quic/quic_sent_packet_manager.cc b/chromium/net/quic/quic_sent_packet_manager.cc
index bda30c72a1f..806297e3575 100644
--- a/chromium/net/quic/quic_sent_packet_manager.cc
+++ b/chromium/net/quic/quic_sent_packet_manager.cc
@@ -4,38 +4,23 @@
#include "net/quic/quic_sent_packet_manager.h"
+#include <algorithm>
+
#include "base/logging.h"
#include "base/stl_util.h"
#include "net/quic/congestion_control/pacing_sender.h"
+#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/quic_ack_notifier_manager.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_utils_chromium.h"
using std::make_pair;
+using std::max;
using std::min;
-// TODO(rtenneti): Remove this.
-// Do not flip this flag until the flakiness of the
-// net/tools/quic/end_to_end_test is fixed.
-// If true, then QUIC connections will track the retransmission history of a
-// packet so that an ack of a previous transmission will ack the data of all
-// other transmissions.
-bool FLAGS_track_retransmission_history = false;
-
-// A test-only flag to prevent the RTO from backing off when multiple sequential
-// tail drops occur.
-bool FLAGS_limit_rto_increase_for_tests = false;
-
-// Do not remove this flag until the Finch-trials described in b/11706275
-// are complete.
-// If true, QUIC connections will support the use of a pacing algorithm when
-// sending packets, in an attempt to reduce packet loss. The client must also
-// request pacing for the server to enable it.
-bool FLAGS_enable_quic_pacing = false;
-
namespace net {
namespace {
-static const int kBitrateSmoothingPeriodMs = 1000;
-static const int kHistoryPeriodMs = 5000;
-
static const int kDefaultRetransmissionTimeMs = 500;
// TCP RFC calls for 1 second RTO however Linux differs from this default and
// define the minimum RTO to 200ms, we will use the same until we have data to
@@ -44,272 +29,282 @@ static const int kMinRetransmissionTimeMs = 200;
static const int kMaxRetransmissionTimeMs = 60000;
static const size_t kMaxRetransmissions = 10;
-// We only retransmit 2 packets per ack.
-static const size_t kMaxRetransmissionsPerAck = 2;
+// Only exponentially back off the handshake timer 5 times due to a timeout.
+static const size_t kMaxHandshakeRetransmissionBackoffs = 5;
+static const size_t kMinHandshakeTimeoutMs = 10;
+
+// Sends up to two tail loss probes before firing an RTO,
+// per draft RFC draft-dukkipati-tcpm-tcp-loss-probe.
+static const size_t kDefaultMaxTailLossProbes = 2;
+static const int64 kMinTailLossProbeTimeoutMs = 10;
-// TCP retransmits after 3 nacks.
-static const size_t kNumberOfNacksBeforeRetransmission = 3;
+// Number of samples before we force a new recent min rtt to be captured.
+static const size_t kNumMinRttSamplesAfterQuiescence = 2;
+
+bool HasCryptoHandshake(const TransmissionInfo& transmission_info) {
+ if (transmission_info.retransmittable_frames == NULL) {
+ return false;
+ }
+ return transmission_info.retransmittable_frames->HasCryptoHandshake() ==
+ IS_HANDSHAKE;
+}
-COMPILE_ASSERT(kHistoryPeriodMs >= kBitrateSmoothingPeriodMs,
- history_must_be_longer_or_equal_to_the_smoothing_period);
} // namespace
#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
-QuicSentPacketManager::HelperInterface::~HelperInterface() {
-}
-
QuicSentPacketManager::QuicSentPacketManager(bool is_server,
- HelperInterface* helper,
const QuicClock* clock,
- CongestionFeedbackType type)
- : is_server_(is_server),
- helper_(helper),
+ QuicConnectionStats* stats,
+ CongestionFeedbackType type,
+ LossDetectionType loss_type)
+ : unacked_packets_(),
+ is_server_(is_server),
clock_(clock),
- send_algorithm_(SendAlgorithmInterface::Create(clock, type)),
- rtt_sample_(QuicTime::Delta::Infinite()),
+ stats_(stats),
+ debug_delegate_(NULL),
+ send_algorithm_(
+ SendAlgorithmInterface::Create(clock, &rtt_stats_, type, stats)),
+ loss_algorithm_(LossDetectionInterface::Create(loss_type)),
+ largest_observed_(0),
consecutive_rto_count_(0),
+ consecutive_tlp_count_(0),
+ consecutive_crypto_retransmission_count_(0),
+ pending_tlp_transmission_(false),
+ max_tail_loss_probes_(kDefaultMaxTailLossProbes),
using_pacing_(false) {
}
QuicSentPacketManager::~QuicSentPacketManager() {
- for (UnackedPacketMap::iterator it = unacked_packets_.begin();
- it != unacked_packets_.end(); ++it) {
- delete it->second.retransmittable_frames;
- // Only delete previous_transmissions once, for the newest packet.
- if (it->second.previous_transmissions != NULL &&
- it->first == *it->second.previous_transmissions->rbegin()) {
- delete it->second.previous_transmissions;
- }
- }
- STLDeleteValues(&packet_history_map_);
}
void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) {
- if (config.initial_round_trip_time_us() > 0 &&
- rtt_sample_.IsInfinite()) {
- // The initial rtt should already be set on the client side.
- DVLOG_IF(1, !is_server_)
- << "Client did not set an initial RTT, but did negotiate one.";
- rtt_sample_ =
- QuicTime::Delta::FromMicroseconds(config.initial_round_trip_time_us());
- }
- if (config.congestion_control() == kPACE) {
+ if (config.HasReceivedInitialRoundTripTimeUs() &&
+ config.ReceivedInitialRoundTripTimeUs() > 0) {
+ rtt_stats_.set_initial_rtt_us(min(kMaxInitialRoundTripTimeUs,
+ config.ReceivedInitialRoundTripTimeUs()));
+ }
+ // TODO(ianswett): BBR is currently a server only feature.
+ if (config.HasReceivedCongestionOptions() &&
+ ContainsQuicTag(config.ReceivedCongestionOptions(), kTBBR)) {
+ send_algorithm_.reset(
+ SendAlgorithmInterface::Create(clock_, &rtt_stats_, kTCPBBR, stats_));
+ }
+ if (config.congestion_feedback() == kPACE) {
MaybeEnablePacing();
}
+ if (config.HasReceivedLossDetection() &&
+ config.ReceivedLossDetection() == kTIME) {
+ loss_algorithm_.reset(LossDetectionInterface::Create(kTime));
+ }
send_algorithm_->SetFromConfig(config, is_server_);
}
-void QuicSentPacketManager::SetMaxPacketSize(QuicByteCount max_packet_size) {
- send_algorithm_->SetMaxPacketSize(max_packet_size);
-}
-
+// TODO(ianswett): Combine this method with OnPacketSent once packets are always
+// sent in order and the connection tracks RetransmittableFrames for longer.
void QuicSentPacketManager::OnSerializedPacket(
const SerializedPacket& serialized_packet) {
- if (serialized_packet.retransmittable_frames == NULL &&
- !serialized_packet.packet->is_fec_packet()) {
- // Don't track ack/congestion feedback packets.
- return;
+ if (serialized_packet.retransmittable_frames) {
+ ack_notifier_manager_.OnSerializedPacket(serialized_packet);
}
- ack_notifier_manager_.OnSerializedPacket(serialized_packet);
-
- DCHECK(unacked_packets_.empty() ||
- unacked_packets_.rbegin()->first < serialized_packet.sequence_number);
- unacked_packets_[serialized_packet.sequence_number] =
- TransmissionInfo(serialized_packet.retransmittable_frames,
- serialized_packet.sequence_number_length);
+ unacked_packets_.AddPacket(serialized_packet);
}
void QuicSentPacketManager::OnRetransmittedPacket(
QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number) {
- DCHECK(ContainsKey(unacked_packets_, old_sequence_number));
- DCHECK(ContainsKey(pending_retransmissions_, old_sequence_number));
- DCHECK(unacked_packets_.empty() ||
- unacked_packets_.rbegin()->first < new_sequence_number);
-
- pending_retransmissions_.erase(old_sequence_number);
-
- UnackedPacketMap::iterator unacked_it =
- unacked_packets_.find(old_sequence_number);
- RetransmittableFrames* frames = unacked_it->second.retransmittable_frames;
- DCHECK(frames);
+ TransmissionType transmission_type;
+ PendingRetransmissionMap::iterator it =
+ pending_retransmissions_.find(old_sequence_number);
+ if (it != pending_retransmissions_.end()) {
+ transmission_type = it->second;
+ pending_retransmissions_.erase(it);
+ } else {
+ DLOG(DFATAL) << "Expected sequence number to be in "
+ "pending_retransmissions_. sequence_number: " << old_sequence_number;
+ transmission_type = NOT_RETRANSMISSION;
+ }
// A notifier may be waiting to hear about ACKs for the original sequence
// number. Inform them that the sequence number has changed.
ack_notifier_manager_.UpdateSequenceNumber(old_sequence_number,
new_sequence_number);
- // We keep the old packet in the unacked packet list until it, or one of
- // the retransmissions of it are acked.
- unacked_it->second.retransmittable_frames = NULL;
- unacked_packets_[new_sequence_number] =
- TransmissionInfo(frames, GetSequenceNumberLength(old_sequence_number));
-
- // Keep track of all sequence numbers that this packet
- // has been transmitted as.
- SequenceNumberSet* previous_transmissions =
- unacked_it->second.previous_transmissions;
- if (previous_transmissions == NULL) {
- // This is the first retransmission of this packet, so create a new entry.
- previous_transmissions = new SequenceNumberSet;
- unacked_it->second.previous_transmissions = previous_transmissions;
- previous_transmissions->insert(old_sequence_number);
- }
- previous_transmissions->insert(new_sequence_number);
- unacked_packets_[new_sequence_number].previous_transmissions =
- previous_transmissions;
-
- DCHECK(HasRetransmittableFrames(new_sequence_number));
-}
-
-bool QuicSentPacketManager::OnIncomingAck(
- const ReceivedPacketInfo& received_info, QuicTime ack_receive_time) {
- // Determine if the least unacked sequence number is being acked.
- QuicPacketSequenceNumber least_unacked_sent_before =
- GetLeastUnackedSentPacket();
- bool new_least_unacked = !IsAwaitingPacket(received_info,
- least_unacked_sent_before);
+ unacked_packets_.OnRetransmittedPacket(old_sequence_number,
+ new_sequence_number,
+ transmission_type);
+}
- HandleAckForSentPackets(received_info);
+void QuicSentPacketManager::OnIncomingAck(
+ const ReceivedPacketInfo& received_info,
+ QuicTime ack_receive_time) {
+ QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight();
- SequenceNumberSet retransmission_packets =
- OnIncomingAckFrame(received_info, ack_receive_time);
+ // We rely on delta_time_largest_observed to compute an RTT estimate, so
+ // we only update rtt when the largest observed gets acked.
+ bool largest_observed_acked = MaybeUpdateRTT(received_info, ack_receive_time);
+ if (largest_observed_ < received_info.largest_observed) {
+ largest_observed_ = received_info.largest_observed;
+ unacked_packets_.IncreaseLargestObserved(largest_observed_);
+ }
+ HandleAckForSentPackets(received_info);
+ InvokeLossDetection(ack_receive_time);
+ MaybeInvokeCongestionEvent(largest_observed_acked, bytes_in_flight);
- for (SequenceNumberSet::const_iterator it = retransmission_packets.begin();
- it != retransmission_packets.end(); ++it) {
- DCHECK(!ContainsKey(pending_packets_, *it));
- MarkForRetransmission(*it, NACK_RETRANSMISSION);
+ // If we have received a truncated ack, then we need to clear out some
+ // previous transmissions to allow the peer to actually ACK new packets.
+ if (received_info.is_truncated) {
+ unacked_packets_.ClearPreviousRetransmissions(
+ received_info.missing_packets.size() / 2);
}
- if (new_least_unacked) {
+ // Anytime we are making forward progress and have a new RTT estimate, reset
+ // the backoff counters.
+ if (largest_observed_acked) {
+ // Reset all retransmit counters any time a new packet is acked.
consecutive_rto_count_ = 0;
+ consecutive_tlp_count_ = 0;
+ consecutive_crypto_retransmission_count_ = 0;
}
-
- return new_least_unacked;
}
-void QuicSentPacketManager::DiscardUnackedPacket(
- QuicPacketSequenceNumber sequence_number) {
- MarkPacketReceivedByPeer(sequence_number);
+void QuicSentPacketManager::MaybeInvokeCongestionEvent(
+ bool rtt_updated, QuicByteCount bytes_in_flight) {
+ if (rtt_updated || !packets_acked_.empty() ||
+ !packets_lost_.empty()) {
+ send_algorithm_->OnCongestionEvent(
+ rtt_updated, bytes_in_flight, packets_acked_, packets_lost_);
+ packets_acked_.clear();
+ packets_lost_.clear();
+ }
}
void QuicSentPacketManager::HandleAckForSentPackets(
const ReceivedPacketInfo& received_info) {
// Go through the packets we have not received an ack for and see if this
// incoming_ack shows they've been seen by the peer.
- UnackedPacketMap::iterator it = unacked_packets_.begin();
+ QuicTime::Delta delta_largest_observed =
+ received_info.delta_time_largest_observed;
+ QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
while (it != unacked_packets_.end()) {
QuicPacketSequenceNumber sequence_number = it->first;
if (sequence_number > received_info.largest_observed) {
- // These are very new sequence_numbers.
+ // These packets are still in flight.
break;
}
if (IsAwaitingPacket(received_info, sequence_number)) {
+ // Consider it multiple nacks when there is a gap between the missing
+ // packet and the largest observed, since the purpose of a nack
+ // threshold is to tolerate re-ordering. This handles both StretchAcks
+ // and Forward Acks.
+ // The nack count only increases when the largest observed increases.
+ size_t min_nacks = received_info.largest_observed - sequence_number;
+ // Truncated acks can nack the largest observed, so use a min of 1.
+ if (min_nacks == 0) {
+ min_nacks = 1;
+ }
+ unacked_packets_.NackPacket(sequence_number, min_nacks);
++it;
continue;
}
-
// Packet was acked, so remove it from our unacked packet list.
- DVLOG(1) << ENDPOINT <<"Got an ack for packet " << sequence_number;
+ DVLOG(1) << ENDPOINT << "Got an ack for packet " << sequence_number;
// If data is associated with the most recent transmission of this
// packet, then inform the caller.
- it = MarkPacketReceivedByPeer(sequence_number);
-
- // The AckNotifierManager is informed of every ACKed sequence number.
- ack_notifier_manager_.OnPacketAcked(sequence_number);
- }
-
- // If we have received a truncated ack, then we need to
- // clear out some previous transmissions to allow the peer
- // to actually ACK new packets.
- if (received_info.is_truncated) {
- ClearPreviousRetransmissions(received_info.missing_packets.size() / 2);
- }
-}
-
-void QuicSentPacketManager::ClearPreviousRetransmissions(size_t num_to_clear) {
- UnackedPacketMap::iterator it = unacked_packets_.begin();
- while (it != unacked_packets_.end() && num_to_clear > 0) {
- QuicPacketSequenceNumber sequence_number = it->first;
- // If this is not a previous transmission then there is no point
- // in clearing out any further packets, because it will not affect
- // the high water mark.
- SequenceNumberSet* previous_transmissions =
- it->second.previous_transmissions;
- if (previous_transmissions == NULL) {
- break;
- }
- QuicPacketSequenceNumber newest_transmission =
- *previous_transmissions->rbegin();
- if (sequence_number == newest_transmission) {
- break;
+ if (it->second.in_flight) {
+ packets_acked_[sequence_number] = it->second;
}
+ it = MarkPacketHandled(it, delta_largest_observed);
+ }
- DCHECK(it->second.retransmittable_frames == NULL);
- previous_transmissions->erase(sequence_number);
- if (previous_transmissions->size() == 1) {
- unacked_packets_[newest_transmission].previous_transmissions = NULL;
- delete previous_transmissions;
- }
- unacked_packets_.erase(it++);
- --num_to_clear;
+ // Discard any retransmittable frames associated with revived packets.
+ for (SequenceNumberSet::const_iterator revived_it =
+ received_info.revived_packets.begin();
+ revived_it != received_info.revived_packets.end(); ++revived_it) {
+ MarkPacketRevived(*revived_it, delta_largest_observed);
}
}
bool QuicSentPacketManager::HasRetransmittableFrames(
QuicPacketSequenceNumber sequence_number) const {
- if (!ContainsKey(unacked_packets_, sequence_number)) {
- return false;
- }
-
- return unacked_packets_.find(
- sequence_number)->second.retransmittable_frames != NULL;
+ return unacked_packets_.HasRetransmittableFrames(sequence_number);
}
void QuicSentPacketManager::RetransmitUnackedPackets(
RetransmissionType retransmission_type) {
- if (unacked_packets_.empty()) {
- return;
+ QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end()) {
+ const RetransmittableFrames* frames = it->second.retransmittable_frames;
+ // TODO(ianswett): Consider adding a new retransmission type which removes
+ // all these old packets from unacked and retransmits them as new sequence
+ // numbers with no connection to the previous ones.
+ if (frames != NULL && (retransmission_type == ALL_PACKETS ||
+ frames->encryption_level() == ENCRYPTION_INITIAL)) {
+ MarkForRetransmission(it->first, ALL_UNACKED_RETRANSMISSION);
+ }
+ ++it;
}
+}
- for (UnackedPacketMap::const_iterator unacked_it = unacked_packets_.begin();
- unacked_it != unacked_packets_.end(); ++unacked_it) {
- const RetransmittableFrames* frames =
- unacked_it->second.retransmittable_frames;
- if (frames == NULL) {
- continue;
- }
- if (retransmission_type == ALL_PACKETS ||
- frames->encryption_level() == ENCRYPTION_INITIAL) {
- // TODO(satyamshekhar): Think about congestion control here.
- // Specifically, about the retransmission count of packets being sent
- // proactively to achieve 0 (minimal) RTT.
- OnPacketAbandoned(unacked_it->first);
- if (!MarkForRetransmission(unacked_it->first, NACK_RETRANSMISSION)) {
- DiscardUnackedPacket(unacked_it->first);
- }
+void QuicSentPacketManager::NeuterUnencryptedPackets() {
+ QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end()) {
+ const RetransmittableFrames* frames = it->second.retransmittable_frames;
+ QuicPacketSequenceNumber sequence_number = it->first;
+ ++it;
+ if (frames != NULL && frames->encryption_level() == ENCRYPTION_NONE) {
+ // Once you're forward secure, no unencrypted packets will be sent, crypto
+ // or otherwise. Unencrypted packets are neutered and abandoned, to ensure
+ // they are not retransmitted or considered lost from a congestion control
+ // perspective.
+ pending_retransmissions_.erase(sequence_number);
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ // RemoveRetransmittibility is safe because only the newest sequence
+ // number can have frames.
+ unacked_packets_.RemoveRetransmittability(sequence_number);
}
}
}
-bool QuicSentPacketManager::MarkForRetransmission(
+void QuicSentPacketManager::MarkForRetransmission(
QuicPacketSequenceNumber sequence_number,
TransmissionType transmission_type) {
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
- if (!HasRetransmittableFrames(sequence_number)) {
- return false;
- }
- // If it's already in the retransmission map, don't add it again, just let
- // the prior retransmission request win out.
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ LOG_IF(DFATAL, transmission_info.retransmittable_frames == NULL);
+ if (transmission_type != TLP_RETRANSMISSION) {
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ }
+ // TODO(ianswett): Currently the RTO can fire while there are pending NACK
+ // retransmissions for the same data, which is not ideal.
if (ContainsKey(pending_retransmissions_, sequence_number)) {
- return true;
+ return;
}
pending_retransmissions_[sequence_number] = transmission_type;
- return true;
+}
+
+void QuicSentPacketManager::RecordSpuriousRetransmissions(
+ const SequenceNumberSet& all_transmissions,
+ QuicPacketSequenceNumber acked_sequence_number) {
+ for (SequenceNumberSet::const_iterator
+ it = all_transmissions.upper_bound(acked_sequence_number),
+ end = all_transmissions.end();
+ it != end;
+ ++it) {
+ const TransmissionInfo& retransmit_info =
+ unacked_packets_.GetTransmissionInfo(*it);
+
+ stats_->bytes_spuriously_retransmitted += retransmit_info.bytes_sent;
+ ++stats_->packets_spuriously_retransmitted;
+ if (debug_delegate_ != NULL) {
+ debug_delegate_->OnSpuriousPacketRetransmition(
+ retransmit_info.transmission_type,
+ retransmit_info.bytes_sent);
+ }
+ }
}
bool QuicSentPacketManager::HasPendingRetransmissions() const {
@@ -321,353 +316,347 @@ QuicSentPacketManager::PendingRetransmission
DCHECK(!pending_retransmissions_.empty());
QuicPacketSequenceNumber sequence_number =
pending_retransmissions_.begin()->first;
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
- const RetransmittableFrames* retransmittable_frames =
- unacked_packets_[sequence_number].retransmittable_frames;
- DCHECK(retransmittable_frames);
+ TransmissionType transmission_type = pending_retransmissions_.begin()->second;
+ if (unacked_packets_.HasPendingCryptoPackets()) {
+ // Ensure crypto packets are retransmitted before other packets.
+ PendingRetransmissionMap::const_iterator it =
+ pending_retransmissions_.begin();
+ do {
+ if (HasCryptoHandshake(unacked_packets_.GetTransmissionInfo(it->first))) {
+ sequence_number = it->first;
+ transmission_type = it->second;
+ break;
+ }
+ ++it;
+ } while (it != pending_retransmissions_.end());
+ }
+ DCHECK(unacked_packets_.IsUnacked(sequence_number)) << sequence_number;
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ DCHECK(transmission_info.retransmittable_frames);
return PendingRetransmission(sequence_number,
- pending_retransmissions_.begin()->second,
- *retransmittable_frames,
- GetSequenceNumberLength(sequence_number));
+ transmission_type,
+ *transmission_info.retransmittable_frames,
+ transmission_info.sequence_number_length);
}
-bool QuicSentPacketManager::IsPreviousTransmission(
- QuicPacketSequenceNumber sequence_number) const {
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
-
- UnackedPacketMap::const_iterator it = unacked_packets_.find(sequence_number);
- if (it->second.previous_transmissions == NULL) {
- return false;
+void QuicSentPacketManager::MarkPacketRevived(
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed) {
+ if (!unacked_packets_.IsUnacked(sequence_number)) {
+ return;
}
- SequenceNumberSet* previous_transmissions = it->second.previous_transmissions;
- DCHECK(!previous_transmissions->empty());
- return *previous_transmissions->rbegin() != sequence_number;
-}
-
-QuicSentPacketManager::UnackedPacketMap::iterator
-QuicSentPacketManager::MarkPacketReceivedByPeer(
- QuicPacketSequenceNumber sequence_number) {
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
-
- // If this packet has never been retransmitted, then simply drop it.
- UnackedPacketMap::const_iterator previous_it =
- unacked_packets_.find(sequence_number);
- if (previous_it->second.previous_transmissions == NULL) {
- UnackedPacketMap::iterator next_unacked =
- unacked_packets_.find(sequence_number);
- ++next_unacked;
- DiscardPacket(sequence_number);
- return next_unacked;
- }
-
- SequenceNumberSet* previous_transmissions =
- previous_it->second.previous_transmissions;
- DCHECK(!previous_transmissions->empty());
- SequenceNumberSet::reverse_iterator previous_transmissions_it =
- previous_transmissions->rbegin();
- QuicPacketSequenceNumber newest_transmission = *previous_transmissions_it;
- if (newest_transmission == sequence_number) {
- DiscardPacket(newest_transmission);
- } else {
- // If we have received an ack for a previous transmission of a packet,
- // we want to keep the "new" transmission of the packet unacked,
- // but prevent the data from being retransmitted.
- delete unacked_packets_[newest_transmission].retransmittable_frames;
- unacked_packets_[newest_transmission].retransmittable_frames = NULL;
- unacked_packets_[newest_transmission].previous_transmissions = NULL;
- pending_retransmissions_.erase(newest_transmission);
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ QuicPacketSequenceNumber newest_transmission =
+ *transmission_info.all_transmissions->rbegin();
+ // This packet has been revived at the receiver. If we were going to
+ // retransmit it, do not retransmit it anymore.
+ pending_retransmissions_.erase(newest_transmission);
+
+ // The AckNotifierManager needs to be notified for revived packets,
+ // since it indicates the packet arrived from the appliction's perspective.
+ if (transmission_info.retransmittable_frames) {
+ ack_notifier_manager_.OnPacketAcked(
+ newest_transmission, delta_largest_observed);
+ }
+
+ unacked_packets_.RemoveRetransmittability(sequence_number);
+}
+
+QuicUnackedPacketMap::const_iterator QuicSentPacketManager::MarkPacketHandled(
+ QuicUnackedPacketMap::const_iterator it,
+ QuicTime::Delta delta_largest_observed) {
+ LOG_IF(DFATAL, it == unacked_packets_.end())
+ << "MarkPacketHandled must be passed a valid iterator entry.";
+ const QuicPacketSequenceNumber sequence_number = it->first;
+ const TransmissionInfo& transmission_info = it->second;
+
+ QuicPacketSequenceNumber newest_transmission =
+ *transmission_info.all_transmissions->rbegin();
+ // Remove the most recent packet, if it is pending retransmission.
+ pending_retransmissions_.erase(newest_transmission);
+
+ // Notify observers about the ACKed packet.
+ {
+ // The AckNotifierManager needs to be notified about the most recent
+ // transmission, since that's the one only one it tracks.
+ ack_notifier_manager_.OnPacketAcked(newest_transmission,
+ delta_largest_observed);
+ if (newest_transmission != sequence_number) {
+ RecordSpuriousRetransmissions(*transmission_info.all_transmissions,
+ sequence_number);
+ }
}
- // Clear out information all previous transmissions.
- ++previous_transmissions_it;
- while (previous_transmissions_it != previous_transmissions->rend()) {
- QuicPacketSequenceNumber previous_transmission = *previous_transmissions_it;
- ++previous_transmissions_it;
- DiscardPacket(previous_transmission);
- }
+ // Two cases for MarkPacketHandled:
+ // 1) Handle the most recent or a crypto packet, so remove all transmissions.
+ // 2) Handle old transmission, keep all other pending transmissions,
+ // but disassociate them from one another.
- delete previous_transmissions;
+ // If it's a crypto handshake packet, discard it and all retransmissions,
+ // since they won't be acked now that one has been processed.
+ // TODO(ianswett): Instead of handling all crypto packets in a special way,
+ // only handle NULL encrypted packets in a special way.
+ if (HasCryptoHandshake(
+ unacked_packets_.GetTransmissionInfo(newest_transmission))) {
+ unacked_packets_.RemoveFromInFlight(newest_transmission);
+ }
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ unacked_packets_.RemoveRetransmittability(sequence_number);
- UnackedPacketMap::iterator next_unacked = unacked_packets_.begin();
+ QuicUnackedPacketMap::const_iterator next_unacked = unacked_packets_.begin();
while (next_unacked != unacked_packets_.end() &&
- next_unacked->first < sequence_number) {
+ next_unacked->first <= sequence_number) {
++next_unacked;
}
return next_unacked;
}
-void QuicSentPacketManager::DiscardPacket(
- QuicPacketSequenceNumber sequence_number) {
- UnackedPacketMap::iterator unacked_it =
- unacked_packets_.find(sequence_number);
- // Packet was not meant to be retransmitted.
- if (unacked_it == unacked_packets_.end()) {
- return;
- }
-
- // Delete the retransmittable frames.
- delete unacked_it->second.retransmittable_frames;
- unacked_packets_.erase(unacked_it);
- pending_retransmissions_.erase(sequence_number);
- return;
-}
-
bool QuicSentPacketManager::IsUnacked(
QuicPacketSequenceNumber sequence_number) const {
- return ContainsKey(unacked_packets_, sequence_number);
-}
-
-QuicSequenceNumberLength QuicSentPacketManager::GetSequenceNumberLength(
- QuicPacketSequenceNumber sequence_number) const {
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
-
- return unacked_packets_.find(sequence_number)->second.sequence_number_length;
+ return unacked_packets_.IsUnacked(sequence_number);
}
bool QuicSentPacketManager::HasUnackedPackets() const {
- return !unacked_packets_.empty();
-}
-
-size_t QuicSentPacketManager::GetNumRetransmittablePackets() const {
- size_t num_unacked_packets = 0;
- for (UnackedPacketMap::const_iterator it = unacked_packets_.begin();
- it != unacked_packets_.end(); ++it) {
- QuicPacketSequenceNumber sequence_number = it->first;
- if (HasRetransmittableFrames(sequence_number)) {
- ++num_unacked_packets;
- }
- }
- return num_unacked_packets;
+ return unacked_packets_.HasUnackedPackets();
}
QuicPacketSequenceNumber
QuicSentPacketManager::GetLeastUnackedSentPacket() const {
- if (unacked_packets_.empty()) {
- // If there are no unacked packets, set the least unacked packet to
- // the sequence number of the next packet sent.
- return helper_->GetNextPacketSequenceNumber();
- }
-
- return unacked_packets_.begin()->first;
+ return unacked_packets_.GetLeastUnackedSentPacket();
}
-SequenceNumberSet QuicSentPacketManager::GetUnackedPackets() const {
- SequenceNumberSet unacked_packets;
- for (UnackedPacketMap::const_iterator it = unacked_packets_.begin();
- it != unacked_packets_.end(); ++it) {
- unacked_packets.insert(it->first);
- }
- return unacked_packets;
-}
-
-void QuicSentPacketManager::OnPacketSent(
+bool QuicSentPacketManager::OnPacketSent(
QuicPacketSequenceNumber sequence_number,
QuicTime sent_time,
QuicByteCount bytes,
TransmissionType transmission_type,
HasRetransmittableData has_retransmittable_data) {
DCHECK_LT(0u, sequence_number);
- DCHECK(!ContainsKey(pending_packets_, sequence_number));
- if (ContainsKey(unacked_packets_, sequence_number)) {
- unacked_packets_[sequence_number].sent_time = sent_time;
+ LOG_IF(DFATAL, bytes == 0) << "Cannot send empty packets.";
+ pending_tlp_transmission_ = false;
+ // In rare circumstances, the packet could be serialized, sent, and then acked
+ // before OnPacketSent is called.
+ if (!unacked_packets_.IsUnacked(sequence_number)) {
+ return false;
}
- // Only track packets the send algorithm wants us to track.
- if (!send_algorithm_->OnPacketSent(sent_time, sequence_number, bytes,
- transmission_type,
- has_retransmittable_data)) {
- return;
+ if (unacked_packets_.bytes_in_flight() == 0) {
+ // TODO(ianswett): Consider being less aggressive to force a new
+ // recent_min_rtt, likely by not discarding a relatively new sample.
+ DVLOG(1) << "Sampling a new recent min rtt within 2 samples. currently:"
+ << rtt_stats_.recent_min_rtt().ToMilliseconds() << "ms";
+ rtt_stats_.SampleNewRecentMinRtt(kNumMinRttSamplesAfterQuiescence);
}
- packet_history_map_[sequence_number] = new SendAlgorithmInterface::SentPacket(
- bytes, sent_time, has_retransmittable_data);
- pending_packets_.insert(sequence_number);
- CleanupPacketHistory();
+
+ // Only track packets as in flight that the send algorithm wants us to track.
+ const bool in_flight =
+ send_algorithm_->OnPacketSent(sent_time,
+ unacked_packets_.bytes_in_flight(),
+ sequence_number,
+ bytes,
+ has_retransmittable_data);
+ unacked_packets_.SetSent(sequence_number, sent_time, bytes, in_flight);
+
+ // Reset the retransmission timer anytime a pending packet is sent.
+ return in_flight;
}
void QuicSentPacketManager::OnRetransmissionTimeout() {
- // Abandon all pending packets to ensure the congestion window
- // opens up before we attempt to retransmit packets.
- QuicTime::Delta retransmission_delay = GetRetransmissionDelay();
- QuicTime max_send_time =
- clock_->ApproximateNow().Subtract(retransmission_delay);
- for (SequenceNumberSet::iterator it = pending_packets_.begin();
- it != pending_packets_.end();) {
- QuicPacketSequenceNumber sequence_number = *it;
- DCHECK(ContainsKey(packet_history_map_, sequence_number));
- DCHECK(ContainsKey(unacked_packets_, sequence_number));
- const TransmissionInfo& transmission_info =
- unacked_packets_.find(sequence_number)->second;
- // Abandon retransmittable packet and old non-retransmittable packets.
- if (transmission_info.retransmittable_frames ||
- transmission_info.sent_time <= max_send_time) {
- pending_packets_.erase(it++);
- send_algorithm_->OnPacketAbandoned(
- sequence_number, packet_history_map_[sequence_number]->bytes_sent());
- } else {
- ++it;
+ DCHECK(unacked_packets_.HasInFlightPackets());
+ DCHECK(!pending_tlp_transmission_);
+ // Handshake retransmission, timer based loss detection, TLP, and RTO are
+ // implemented with a single alarm. The handshake alarm is set when the
+ // handshake has not completed, the loss alarm is set when the loss detection
+ // algorithm says to, and the TLP and RTO alarms are set after that.
+ // The TLP alarm is always set to run for under an RTO.
+ switch (GetRetransmissionMode()) {
+ case HANDSHAKE_MODE:
+ ++stats_->crypto_retransmit_count;
+ RetransmitCryptoPackets();
+ return;
+ case LOSS_MODE: {
+ ++stats_->loss_timeout_count;
+ QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight();
+ InvokeLossDetection(clock_->Now());
+ MaybeInvokeCongestionEvent(false, bytes_in_flight);
+ return;
}
+ case TLP_MODE:
+ // If no tail loss probe can be sent, because there are no retransmittable
+ // packets, execute a conventional RTO to abandon old packets.
+ ++stats_->tlp_count;
+ ++consecutive_tlp_count_;
+ pending_tlp_transmission_ = true;
+ // TLPs prefer sending new data instead of retransmitting data, so
+ // give the connection a chance to write before completing the TLP.
+ return;
+ case RTO_MODE:
+ ++stats_->rto_count;
+ RetransmitAllPackets();
+ return;
}
+}
- // Attempt to send all the unacked packets when the RTO fires, let the
- // congestion manager decide how many to send immediately and the remaining
- // packets will be queued for future sending.
- DVLOG(1) << "OnRetransmissionTimeout() fired with "
- << unacked_packets_.size() << " unacked packets.";
+void QuicSentPacketManager::RetransmitCryptoPackets() {
+ DCHECK_EQ(HANDSHAKE_MODE, GetRetransmissionMode());
+ // TODO(ianswett): Typical TCP implementations only retransmit 5 times.
+ consecutive_crypto_retransmission_count_ =
+ min(kMaxHandshakeRetransmissionBackoffs,
+ consecutive_crypto_retransmission_count_ + 1);
+ bool packet_retransmitted = false;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ const RetransmittableFrames* frames = it->second.retransmittable_frames;
+ // Only retransmit frames which are in flight, and therefore have been sent.
+ if (!it->second.in_flight || frames == NULL ||
+ frames->HasCryptoHandshake() != IS_HANDSHAKE) {
+ continue;
+ }
+ packet_retransmitted = true;
+ MarkForRetransmission(sequence_number, HANDSHAKE_RETRANSMISSION);
+ }
+ DCHECK(packet_retransmitted) << "No crypto packets found to retransmit.";
+}
- // Retransmit any packet with retransmittable frames.
- bool packets_retransmitted = false;
- for (UnackedPacketMap::const_iterator it = unacked_packets_.begin();
+bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() {
+ if (!pending_tlp_transmission_) {
+ return false;
+ }
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
it != unacked_packets_.end(); ++it) {
- if (it->second.retransmittable_frames != NULL) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ const RetransmittableFrames* frames = it->second.retransmittable_frames;
+ // Only retransmit frames which are in flight, and therefore have been sent.
+ if (!it->second.in_flight || frames == NULL) {
+ continue;
+ }
+ DCHECK_NE(IS_HANDSHAKE, frames->HasCryptoHandshake());
+ MarkForRetransmission(sequence_number, TLP_RETRANSMISSION);
+ return true;
+ }
+ DLOG(FATAL)
+ << "No retransmittable packets, so RetransmitOldestPacket failed.";
+ return false;
+}
+
+void QuicSentPacketManager::RetransmitAllPackets() {
+ DVLOG(1) << "RetransmitAllPackets() called with "
+ << unacked_packets_.GetNumUnackedPackets() << " unacked packets.";
+ // Request retransmission of all retransmittable packets when the RTO
+ // fires, and let the congestion manager decide how many to send
+ // immediately and the remaining packets will be queued.
+ // Abandon any non-retransmittable packets that are sufficiently old.
+ bool packets_retransmitted = false;
+ QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end()) {
+ const RetransmittableFrames* frames = it->second.retransmittable_frames;
+ QuicPacketSequenceNumber sequence_number = it->first;
+ ++it;
+ if (frames != NULL) {
packets_retransmitted = true;
- MarkForRetransmission(it->first, RTO_RETRANSMISSION);
+ MarkForRetransmission(sequence_number, RTO_RETRANSMISSION);
+ } else {
+ unacked_packets_.RemoveFromInFlight(sequence_number);
}
}
- // Only inform the sent packet manager of an RTO if data was retransmitted.
+ send_algorithm_->OnRetransmissionTimeout(packets_retransmitted);
if (packets_retransmitted) {
++consecutive_rto_count_;
- send_algorithm_->OnRetransmissionTimeout();
}
}
-void QuicSentPacketManager::OnPacketAbandoned(
- QuicPacketSequenceNumber sequence_number) {
- SequenceNumberSet::iterator it = pending_packets_.find(sequence_number);
- if (it != pending_packets_.end()) {
- DCHECK(ContainsKey(packet_history_map_, sequence_number));
- send_algorithm_->OnPacketAbandoned(
- sequence_number, packet_history_map_[sequence_number]->bytes_sent());
- pending_packets_.erase(it);
+QuicSentPacketManager::RetransmissionTimeoutMode
+ QuicSentPacketManager::GetRetransmissionMode() const {
+ DCHECK(unacked_packets_.HasInFlightPackets());
+ if (unacked_packets_.HasPendingCryptoPackets()) {
+ return HANDSHAKE_MODE;
+ }
+ if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) {
+ return LOSS_MODE;
+ }
+ if (consecutive_tlp_count_ < max_tail_loss_probes_) {
+ if (unacked_packets_.HasUnackedRetransmittableFrames()) {
+ return TLP_MODE;
+ }
}
+ return RTO_MODE;
}
void QuicSentPacketManager::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame,
const QuicTime& feedback_receive_time) {
send_algorithm_->OnIncomingQuicCongestionFeedbackFrame(
- frame, feedback_receive_time, packet_history_map_);
+ frame, feedback_receive_time);
}
-SequenceNumberSet QuicSentPacketManager::OnIncomingAckFrame(
- const ReceivedPacketInfo& received_info,
- const QuicTime& ack_receive_time) {
- MaybeUpdateRTT(received_info, ack_receive_time);
-
- // We want to.
- // * Get all packets lower(including) than largest_observed
- // from pending_packets_.
- // * Remove all packets no longer being waited for(ie: acked).
- // * Send each ACK in the list to send_algorithm_.
- SequenceNumberSet::iterator it = pending_packets_.begin();
- SequenceNumberSet::iterator it_upper =
- pending_packets_.upper_bound(received_info.largest_observed);
-
- SequenceNumberSet retransmission_packets;
- SequenceNumberSet lost_packets;
- while (it != it_upper) {
- QuicPacketSequenceNumber sequence_number = *it;
- const SendAlgorithmInterface::SentPacket* sent_packet =
- packet_history_map_[sequence_number];
- if (!IsAwaitingPacket(received_info, sequence_number)) {
- // Not missing, hence implicitly acked.
- size_t bytes_sent = sent_packet->bytes_sent();
- send_algorithm_->OnPacketAcked(sequence_number, bytes_sent, rtt_sample_);
- pending_packets_.erase(it++); // Must be incremented post to work.
- continue;
- }
-
- // The peer got packets after this sequence number. This is an explicit
- // nack.
- DVLOG(1) << "still missing packet " << sequence_number;
- DCHECK(ContainsKey(packet_history_map_, sequence_number));
- // Consider it multiple nacks when there is a gap between the missing packet
- // and the largest observed, since the purpose of a nack threshold is to
- // tolerate re-ordering. This handles both StretchAcks and Forward Acks.
- // TODO(ianswett): This relies heavily on sequential reception of packets,
- // and makes an assumption that the congestion control uses TCP style nacks.
- size_t min_nacks = received_info.largest_observed - sequence_number;
- packet_history_map_[sequence_number]->Nack(min_nacks);
-
- size_t num_nacks_needed = kNumberOfNacksBeforeRetransmission;
- // Check for early retransmit(RFC5827) when the last packet gets acked and
- // the there are fewer than 4 pending packets.
- if (pending_packets_.size() <= kNumberOfNacksBeforeRetransmission &&
- sent_packet->has_retransmittable_data() == HAS_RETRANSMITTABLE_DATA &&
- *pending_packets_.rbegin() == received_info.largest_observed) {
- num_nacks_needed = received_info.largest_observed - sequence_number;
- }
-
- if (sent_packet->nack_count() < num_nacks_needed) {
- ++it;
- continue;
- }
-
- // If the number of retransmissions has maxed out, don't lose or retransmit
- // any more packets.
- if (retransmission_packets.size() >= kMaxRetransmissionsPerAck) {
- ++it;
- continue;
- }
-
- lost_packets.insert(sequence_number);
- if (sent_packet->has_retransmittable_data() == HAS_RETRANSMITTABLE_DATA) {
- retransmission_packets.insert(sequence_number);
- }
-
- ++it;
- }
- // Abandon packets after the loop over pending packets, because otherwise it
- // changes the early retransmit logic and iteration.
+void QuicSentPacketManager::InvokeLossDetection(QuicTime time) {
+ SequenceNumberSet lost_packets =
+ loss_algorithm_->DetectLostPackets(unacked_packets_,
+ time,
+ largest_observed_,
+ rtt_stats_);
for (SequenceNumberSet::const_iterator it = lost_packets.begin();
it != lost_packets.end(); ++it) {
- // TODO(ianswett): OnPacketLost is also called from TCPCubicSender when
- // an FEC packet is lost, but FEC loss information should be shared among
- // congestion managers. Additionally, if it's expected the FEC packet may
- // repair the loss, it should be recorded as a loss to the congestion
- // manager, but not retransmitted until it's known whether the FEC packet
- // arrived.
- send_algorithm_->OnPacketLost(*it, ack_receive_time);
- OnPacketAbandoned(*it);
+ QuicPacketSequenceNumber sequence_number = *it;
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ // TODO(ianswett): If it's expected the FEC packet may repair the loss, it
+ // should be recorded as a loss to the send algorithm, but not retransmitted
+ // until it's known whether the FEC packet arrived.
+ ++stats_->packets_lost;
+ packets_lost_[sequence_number] = transmission_info;
+ DVLOG(1) << ENDPOINT << "Lost packet " << sequence_number;
+
+ if (transmission_info.retransmittable_frames != NULL) {
+ MarkForRetransmission(sequence_number, LOSS_RETRANSMISSION);
+ } else {
+ // Since we will not retransmit this, we need to remove it from
+ // unacked_packets_. This is either the current transmission of
+ // a packet whose previous transmission has been acked, a packet that has
+ // been TLP retransmitted, or an FEC packet.
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ }
}
-
- return retransmission_packets;
}
-void QuicSentPacketManager::MaybeUpdateRTT(
+bool QuicSentPacketManager::MaybeUpdateRTT(
const ReceivedPacketInfo& received_info,
const QuicTime& ack_receive_time) {
+ if (!unacked_packets_.IsUnacked(received_info.largest_observed)) {
+ return false;
+ }
// We calculate the RTT based on the highest ACKed sequence number, the lower
// sequence numbers will include the ACK aggregation delay.
- SendAlgorithmInterface::SentPacketsMap::iterator history_it =
- packet_history_map_.find(received_info.largest_observed);
- // TODO(satyamshekhar): largest_observed might be missing.
- if (history_it == packet_history_map_.end()) {
- return;
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(received_info.largest_observed);
+ // Don't update the RTT if it hasn't been sent.
+ if (transmission_info.sent_time == QuicTime::Zero()) {
+ return false;
}
- QuicTime::Delta send_delta = ack_receive_time.Subtract(
- history_it->second->send_timestamp());
- if (send_delta > received_info.delta_time_largest_observed) {
- rtt_sample_ = send_delta.Subtract(
- received_info.delta_time_largest_observed);
- } else if (rtt_sample_.IsInfinite()) {
- // Even though we received information from the peer suggesting
- // an invalid (negative) RTT, we can use the send delta as an
- // approximation until we get a better estimate.
- rtt_sample_ = send_delta;
- }
+ QuicTime::Delta send_delta =
+ ack_receive_time.Subtract(transmission_info.sent_time);
+ rtt_stats_.UpdateRtt(
+ send_delta, received_info.delta_time_largest_observed, ack_receive_time);
+ return true;
}
QuicTime::Delta QuicSentPacketManager::TimeUntilSend(
QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake) {
- return send_algorithm_->TimeUntilSend(now, transmission_type, retransmittable,
- handshake);
+ HasRetransmittableData retransmittable) {
+ // The TLP logic is entirely contained within QuicSentPacketManager, so the
+ // send algorithm does not need to be consulted.
+ if (pending_tlp_transmission_) {
+ return QuicTime::Delta::Zero();
+ }
+ return send_algorithm_->TimeUntilSend(
+ now, unacked_packets_.bytes_in_flight(), retransmittable);
}
// Ensures that the Delayed Ack timer is always set to a value lesser
@@ -682,52 +671,91 @@ QuicTime::Delta QuicSentPacketManager::TimeUntilSend(
// different MinRTO, we may get spurious retransmissions. May not have
// any benefits, but if the delayed ack becomes a significant source
// of (likely, tail) latency, then consider such a mechanism.
-
-const QuicTime::Delta QuicSentPacketManager::DelayedAckTime() {
+const QuicTime::Delta QuicSentPacketManager::DelayedAckTime() const {
return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2);
}
-const QuicTime::Delta QuicSentPacketManager::GetRetransmissionDelay() const {
- size_t number_retransmissions = consecutive_rto_count_;
- if (FLAGS_limit_rto_increase_for_tests) {
- const size_t kTailDropWindowSize = 5;
- const size_t kTailDropMaxRetransmissions = 4;
- if (pending_packets_.size() <= kTailDropWindowSize) {
- // Avoid exponential backoff of RTO when there are only a few packets
- // outstanding. This helps avoid the situation where fake packet loss
- // causes a packet and it's retransmission to be dropped causing
- // test timouts.
- if (number_retransmissions <= kTailDropMaxRetransmissions) {
- number_retransmissions = 0;
- } else {
- number_retransmissions -= kTailDropMaxRetransmissions;
- }
+const QuicTime QuicSentPacketManager::GetRetransmissionTime() const {
+ // Don't set the timer if there are no packets in flight or we've already
+ // queued a tlp transmission and it hasn't been sent yet.
+ if (!unacked_packets_.HasInFlightPackets() || pending_tlp_transmission_) {
+ return QuicTime::Zero();
+ }
+ switch (GetRetransmissionMode()) {
+ case HANDSHAKE_MODE:
+ return clock_->ApproximateNow().Add(GetCryptoRetransmissionDelay());
+ case LOSS_MODE:
+ return loss_algorithm_->GetLossTimeout();
+ case TLP_MODE: {
+ // TODO(ianswett): When CWND is available, it would be preferable to
+ // set the timer based on the earliest retransmittable packet.
+ // Base the updated timer on the send time of the last packet.
+ const QuicTime sent_time = unacked_packets_.GetLastPacketSentTime();
+ const QuicTime tlp_time = sent_time.Add(GetTailLossProbeDelay());
+ // Ensure the TLP timer never gets set to a time in the past.
+ return QuicTime::Max(clock_->ApproximateNow(), tlp_time);
}
+ case RTO_MODE: {
+ // The RTO is based on the first outstanding packet.
+ const QuicTime sent_time =
+ unacked_packets_.GetFirstInFlightPacketSentTime();
+ QuicTime rto_timeout = sent_time.Add(GetRetransmissionDelay());
+ // Always wait at least 1.5 * RTT from now.
+ QuicTime min_timeout = clock_->ApproximateNow().Add(
+ rtt_stats_.SmoothedRtt().Multiply(1.5));
+
+ return QuicTime::Max(min_timeout, rto_timeout);
+ }
+ }
+ DCHECK(false);
+ return QuicTime::Zero();
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetCryptoRetransmissionDelay()
+ const {
+ // This is equivalent to the TailLossProbeDelay, but slightly more aggressive
+ // because crypto handshake messages don't incur a delayed ack time.
+ int64 delay_ms = max<int64>(kMinHandshakeTimeoutMs,
+ 1.5 * rtt_stats_.SmoothedRtt().ToMilliseconds());
+ return QuicTime::Delta::FromMilliseconds(
+ delay_ms << consecutive_crypto_retransmission_count_);
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetTailLossProbeDelay() const {
+ QuicTime::Delta srtt = rtt_stats_.SmoothedRtt();
+ if (!unacked_packets_.HasMultipleInFlightPackets()) {
+ return QuicTime::Delta::Max(
+ srtt.Multiply(1.5).Add(DelayedAckTime()), srtt.Multiply(2));
}
+ return QuicTime::Delta::FromMilliseconds(
+ max(kMinTailLossProbeTimeoutMs,
+ static_cast<int64>(2 * srtt.ToMilliseconds())));
+}
+const QuicTime::Delta QuicSentPacketManager::GetRetransmissionDelay() const {
QuicTime::Delta retransmission_delay = send_algorithm_->RetransmissionDelay();
+ // TODO(rch): This code should move to |send_algorithm_|.
if (retransmission_delay.IsZero()) {
// We are in the initial state, use default timeout values.
retransmission_delay =
QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs);
+ } else if (retransmission_delay.ToMilliseconds() < kMinRetransmissionTimeMs) {
+ retransmission_delay =
+ QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs);
}
+
// Calculate exponential back off.
- retransmission_delay = QuicTime::Delta::FromMilliseconds(
- retransmission_delay.ToMilliseconds() * static_cast<size_t>(
- (1 << min<size_t>(number_retransmissions, kMaxRetransmissions))));
+ retransmission_delay = retransmission_delay.Multiply(
+ 1 << min<size_t>(consecutive_rto_count_, kMaxRetransmissions));
- // TODO(rch): This code should move to |send_algorithm_|.
- if (retransmission_delay.ToMilliseconds() < kMinRetransmissionTimeMs) {
- return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs);
- }
if (retransmission_delay.ToMilliseconds() > kMaxRetransmissionTimeMs) {
return QuicTime::Delta::FromMilliseconds(kMaxRetransmissionTimeMs);
}
return retransmission_delay;
}
-const QuicTime::Delta QuicSentPacketManager::SmoothedRtt() const {
- return send_algorithm_->SmoothedRtt();
+const RttStats* QuicSentPacketManager::GetRttStats() const {
+ return &rtt_stats_;
}
QuicBandwidth QuicSentPacketManager::BandwidthEstimate() const {
@@ -738,27 +766,6 @@ QuicByteCount QuicSentPacketManager::GetCongestionWindow() const {
return send_algorithm_->GetCongestionWindow();
}
-void QuicSentPacketManager::CleanupPacketHistory() {
- const QuicTime::Delta kHistoryPeriod =
- QuicTime::Delta::FromMilliseconds(kHistoryPeriodMs);
- QuicTime now = clock_->ApproximateNow();
-
- SendAlgorithmInterface::SentPacketsMap::iterator history_it =
- packet_history_map_.begin();
- for (; history_it != packet_history_map_.end(); ++history_it) {
- if (now.Subtract(history_it->second->send_timestamp()) <= kHistoryPeriod) {
- return;
- }
- // Don't remove packets which have not been acked.
- if (ContainsKey(pending_packets_, history_it->first)) {
- continue;
- }
- delete history_it->second;
- packet_history_map_.erase(history_it);
- history_it = packet_history_map_.begin();
- }
-}
-
void QuicSentPacketManager::MaybeEnablePacing() {
if (!FLAGS_enable_quic_pacing) {
return;
@@ -768,10 +775,11 @@ void QuicSentPacketManager::MaybeEnablePacing() {
return;
}
+ // Set up a pacing sender with a 5 millisecond alarm granularity.
using_pacing_ = true;
send_algorithm_.reset(
new PacingSender(send_algorithm_.release(),
- QuicTime::Delta::FromMicroseconds(1)));
+ QuicTime::Delta::FromMilliseconds(5)));
}
} // namespace net
diff --git a/chromium/net/quic/quic_sent_packet_manager.h b/chromium/net/quic/quic_sent_packet_manager.h
index 71c61e93c03..2e75786119a 100644
--- a/chromium/net/quic/quic_sent_packet_manager.h
+++ b/chromium/net/quic/quic_sent_packet_manager.h
@@ -14,14 +14,14 @@
#include <vector>
#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
#include "net/base/linked_hash_map.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/congestion_control/rtt_stats.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_ack_notifier_manager.h"
#include "net/quic/quic_protocol.h"
-
-NET_EXPORT_PRIVATE extern bool FLAGS_track_retransmission_history;
-NET_EXPORT_PRIVATE extern bool FLAGS_limit_rto_increase_for_tests;
-NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_pacing;
+#include "net/quic/quic_unacked_packet_map.h"
namespace net {
@@ -32,6 +32,7 @@ class QuicSentPacketManagerPeer;
class QuicClock;
class QuicConfig;
+struct QuicConnectionStats;
// Class which tracks the set of packets sent on a QUIC connection and contains
// a send algorithm to decide when to send new packets. It keeps track of any
@@ -40,6 +41,19 @@ class QuicConfig;
// previous transmission is acked, the data will not be retransmitted.
class NET_EXPORT_PRIVATE QuicSentPacketManager {
public:
+ // Interface which gets callbacks from the QuicSentPacketManager at
+ // interesting points. Implementations must not mutate the state of
+ // the packet manager or connection as a result of these callbacks.
+ class NET_EXPORT_PRIVATE DebugDelegate {
+ public:
+ virtual ~DebugDelegate() {}
+
+ // Called when a spurious retransmission is detected.
+ virtual void OnSpuriousPacketRetransmition(
+ TransmissionType transmission_type,
+ QuicByteCount byte_size) {}
+ };
+
// Struct to store the pending retransmission information.
struct PendingRetransmission {
PendingRetransmission(QuicPacketSequenceNumber sequence_number,
@@ -58,25 +72,15 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
QuicSequenceNumberLength sequence_number_length;
};
- // Interface which provides callbacks that the manager needs.
- class NET_EXPORT_PRIVATE HelperInterface {
- public:
- virtual ~HelperInterface();
-
- // Called to return the sequence number of the next packet to be sent.
- virtual QuicPacketSequenceNumber GetNextPacketSequenceNumber() = 0;
- };
-
QuicSentPacketManager(bool is_server,
- HelperInterface* helper,
const QuicClock* clock,
- CongestionFeedbackType congestion_type);
+ QuicConnectionStats* stats,
+ CongestionFeedbackType congestion_type,
+ LossDetectionType loss_type);
virtual ~QuicSentPacketManager();
virtual void SetFromConfig(const QuicConfig& config);
- virtual void SetMaxPacketSize(QuicByteCount max_packet_size);
-
// Called when a new packet is serialized. If the packet contains
// retransmittable data, it will be added to the unacked packet map.
void OnSerializedPacket(const SerializedPacket& serialized_packet);
@@ -87,22 +91,24 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
void OnRetransmittedPacket(QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number);
- // Processes the incoming ack and returns true if the retransmission or ack
- // alarm should be reset.
- bool OnIncomingAck(const ReceivedPacketInfo& received_info,
+ // Processes the incoming ack.
+ void OnIncomingAck(const ReceivedPacketInfo& received_info,
QuicTime ack_receive_time);
- // Discards any information for the packet corresponding to |sequence_number|.
- // If this packet has been retransmitted, information on those packets
- // will be discarded as well.
- void DiscardUnackedPacket(QuicPacketSequenceNumber sequence_number);
-
// Returns true if the non-FEC packet |sequence_number| is unacked.
bool IsUnacked(QuicPacketSequenceNumber sequence_number) const;
// Requests retransmission of all unacked packets of |retransmission_type|.
void RetransmitUnackedPackets(RetransmissionType retransmission_type);
+ // Retransmits the oldest pending packet there is still a tail loss probe
+ // pending. Invoked after OnRetransmissionTimeout.
+ bool MaybeRetransmitTailLossProbe();
+
+ // Removes the retransmittable frames from all unencrypted packets to ensure
+ // they don't get retransmitted.
+ void NeuterUnencryptedPackets();
+
// Returns true if the unacked packet |sequence_number| has retransmittable
// frames. This will only return false if the packet has been acked, if a
// previous transmission of this packet was ACK'd, or if this packet has been
@@ -117,39 +123,19 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
bool HasUnackedPackets() const;
- // Returns the number of unacked packets which have retransmittable frames.
- size_t GetNumRetransmittablePackets() const;
-
- // Returns the smallest sequence number of a sent packet which has not been
- // acked by the peer. Excludes any packets which have been retransmitted
- // with a new sequence number. If all packets have been acked, returns the
- // sequence number of the next packet that will be sent.
+ // Returns the smallest sequence number of a serialized packet which has not
+ // been acked by the peer. If there are no unacked packets, returns 0.
QuicPacketSequenceNumber GetLeastUnackedSentPacket() const;
- // Returns the set of sequence numbers of all unacked packets.
- // Test only.
- SequenceNumberSet GetUnackedPackets() const;
-
- // Returns true if |sequence_number| is a previous transmission of packet.
- bool IsPreviousTransmission(QuicPacketSequenceNumber sequence_number) const;
-
- // TODO(ianswett): Combine the congestion control related methods below with
- // some of the methods above and cleanup the resulting code.
- // Called when we have received an ack frame from peer.
- // Returns a set containing all the sequence numbers to be nack retransmitted
- // as a result of the ack.
- virtual SequenceNumberSet OnIncomingAckFrame(
- const ReceivedPacketInfo& received_info,
- const QuicTime& ack_receive_time);
-
// Called when a congestion feedback frame is received from peer.
virtual void OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame,
const QuicTime& feedback_receive_time);
// Called when we have sent bytes to the peer. This informs the manager both
- // the number of bytes sent and if they were retransmitted.
- virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number,
+ // the number of bytes sent and if they were retransmitted. Returns true if
+ // the sender should reset the retransmission timer.
+ virtual bool OnPacketSent(QuicPacketSequenceNumber sequence_number,
QuicTime sent_time,
QuicByteCount bytes,
TransmissionType transmission_type,
@@ -158,28 +144,23 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
// Called when the retransmission timer expires.
virtual void OnRetransmissionTimeout();
- // Called when a packet is timed out, such as an RTO. Removes the bytes from
- // the congestion manager, but does not change the congestion window size.
- virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number);
-
// Calculate the time until we can send the next packet to the wire.
// Note 1: When kUnknownWaitTime is returned, there is no need to poll
// TimeUntilSend again until we receive an OnIncomingAckFrame event.
// Note 2: Send algorithms may or may not use |retransmit| in their
// calculations.
virtual QuicTime::Delta TimeUntilSend(QuicTime now,
- TransmissionType transmission_type,
- HasRetransmittableData retransmittable,
- IsHandshake handshake);
+ HasRetransmittableData retransmittable);
// Returns amount of time for delayed ack timer.
- const QuicTime::Delta DelayedAckTime();
+ const QuicTime::Delta DelayedAckTime() const;
- // Returns the current RTO delay.
- const QuicTime::Delta GetRetransmissionDelay() const;
+ // Returns the current delay for the retransmission timer, which may send
+ // either a tail loss probe or do a full RTO. Returns QuicTime::Zero() if
+ // there are no retransmittable packets.
+ const QuicTime GetRetransmissionTime() const;
- // Returns the estimated smoothed RTT calculated by the congestion algorithm.
- const QuicTime::Delta SmoothedRtt() const;
+ const RttStats* GetRttStats() const;
// Returns the estimated bandwidth calculated by the congestion algorithm.
QuicBandwidth BandwidthEstimate() const;
@@ -195,73 +176,91 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
bool using_pacing() const { return using_pacing_; }
+ void set_debug_delegate(DebugDelegate* debug_delegate) {
+ debug_delegate_ = debug_delegate;
+ }
private:
friend class test::QuicConnectionPeer;
friend class test::QuicSentPacketManagerPeer;
- struct TransmissionInfo {
- TransmissionInfo()
- : retransmittable_frames(NULL),
- sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER),
- sent_time(QuicTime::Zero()),
- previous_transmissions(NULL) { }
- TransmissionInfo(RetransmittableFrames* retransmittable_frames,
- QuicSequenceNumberLength sequence_number_length)
- : retransmittable_frames(retransmittable_frames),
- sequence_number_length(sequence_number_length),
- sent_time(QuicTime::Zero()),
- previous_transmissions(NULL) {
- }
-
- RetransmittableFrames* retransmittable_frames;
- QuicSequenceNumberLength sequence_number_length;
- // Zero when the packet is serialized, non-zero once it's sent.
- QuicTime sent_time;
- // Stores all previous transmissions if the packet has been retransmitted,
- // and is NULL otherwise.
- SequenceNumberSet* previous_transmissions;
+ // The retransmission timer is a single timer which switches modes depending
+ // upon connection state.
+ enum RetransmissionTimeoutMode {
+ // A conventional TCP style RTO.
+ RTO_MODE,
+ // A tail loss probe. By default, QUIC sends up to two before RTOing.
+ TLP_MODE,
+ // Retransmission of handshake packets prior to handshake completion.
+ HANDSHAKE_MODE,
+ // Re-invoke the loss detection when a packet is not acked before the
+ // loss detection algorithm expects.
+ LOSS_MODE,
};
typedef linked_hash_map<QuicPacketSequenceNumber,
- TransmissionInfo> UnackedPacketMap;
- typedef linked_hash_map<QuicPacketSequenceNumber,
TransmissionType> PendingRetransmissionMap;
- typedef base::hash_map<QuicPacketSequenceNumber, SequenceNumberSet*>
- PreviousTransmissionMap;
// Process the incoming ack looking for newly ack'd data packets.
void HandleAckForSentPackets(const ReceivedPacketInfo& received_info);
+ // Returns the current retransmission mode.
+ RetransmissionTimeoutMode GetRetransmissionMode() const;
+
+ // Retransmits all crypto stream packets.
+ void RetransmitCryptoPackets();
+
+ // Retransmits all the packets and abandons by invoking a full RTO.
+ void RetransmitAllPackets();
+
+ // Returns the timer for retransmitting crypto handshake packets.
+ const QuicTime::Delta GetCryptoRetransmissionDelay() const;
+
+ // Returns the timer for a new tail loss probe.
+ const QuicTime::Delta GetTailLossProbeDelay() const;
+
+ // Returns the retransmission timeout, after which a full RTO occurs.
+ const QuicTime::Delta GetRetransmissionDelay() const;
+
// Update the RTT if the ack is for the largest acked sequence number.
- void MaybeUpdateRTT(const ReceivedPacketInfo& received_info,
+ // Returns true if the rtt was updated.
+ bool MaybeUpdateRTT(const ReceivedPacketInfo& received_info,
const QuicTime& ack_receive_time);
- // Marks |sequence_number| as having been seen by the peer. Returns an
- // iterator to the next remaining unacked packet.
- UnackedPacketMap::iterator MarkPacketReceivedByPeer(
- QuicPacketSequenceNumber sequence_number);
-
- // Simply removes the entries, if any, from the unacked packet map
- // and the retransmission map.
- void DiscardPacket(QuicPacketSequenceNumber sequence_number);
+ // Invokes the loss detection algorithm and loses and retransmits packets if
+ // necessary.
+ void InvokeLossDetection(QuicTime time);
+
+ // Invokes OnCongestionEvent if |rtt_updated| is true, there are pending acks,
+ // or pending losses. Clears pending acks and pending losses afterwards.
+ // |bytes_in_flight| is the number of bytes in flight before the losses or
+ // acks.
+ void MaybeInvokeCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight);
+
+ // Marks |sequence_number| as having been revived by the peer, but not
+ // received, so the packet remains pending if it is and the congestion control
+ // does not consider the packet acked.
+ void MarkPacketRevived(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed);
+
+ // Removes the retransmittability and pending properties from the packet at
+ // |it| due to receipt by the peer. Returns an iterator to the next remaining
+ // unacked packet.
+ QuicUnackedPacketMap::const_iterator MarkPacketHandled(
+ QuicUnackedPacketMap::const_iterator it,
+ QuicTime::Delta delta_largest_observed);
// Request that |sequence_number| be retransmitted after the other pending
- // retransmissions. Returns false if there are no retransmittable frames for
- // |sequence_number| and true if it will be retransmitted.
- bool MarkForRetransmission(QuicPacketSequenceNumber sequence_number,
+ // retransmissions. Does not add it to the retransmissions if it's already
+ // a pending retransmission.
+ void MarkForRetransmission(QuicPacketSequenceNumber sequence_number,
TransmissionType transmission_type);
- // Returns the length of the serialized sequence number for
- // the packet |sequence_number|.
- QuicSequenceNumberLength GetSequenceNumberLength(
- QuicPacketSequenceNumber sequence_number) const;
-
- // Clears up to |num_to_clear| previous transmissions in order to make room
- // in the ack frame for new acks.
- void ClearPreviousRetransmissions(size_t num_to_clear);
-
- void CleanupPacketHistory();
+ // Notify observers about spurious retransmits.
+ void RecordSpuriousRetransmissions(
+ const SequenceNumberSet& all_transmissions,
+ QuicPacketSequenceNumber acked_sequence_number);
// Newly serialized retransmittable and fec packets are added to this map,
// which contains owning pointers to any contained frames. If a packet is
@@ -271,7 +270,7 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
// If the old packet is acked before the new packet, then the old entry will
// be removed from the map and the new entry's retransmittable frames will be
// set to NULL.
- UnackedPacketMap unacked_packets_;
+ QuicUnackedPacketMap unacked_packets_;
// Pending retransmissions which have not been packetized and sent yet.
PendingRetransmissionMap pending_retransmissions_;
@@ -279,25 +278,35 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager {
// Tracks if the connection was created by the server.
bool is_server_;
- HelperInterface* helper_;
-
// An AckNotifier can register to be informed when ACKs have been received for
// all packets that a given block of data was sent in. The AckNotifierManager
// maintains the currently active notifiers.
AckNotifierManager ack_notifier_manager_;
const QuicClock* clock_;
+ QuicConnectionStats* stats_;
+ DebugDelegate* debug_delegate_;
+ RttStats rtt_stats_;
scoped_ptr<SendAlgorithmInterface> send_algorithm_;
- // Tracks the send time, size, and nack count of sent packets. Packets are
- // removed after 5 seconds and they've been removed from pending_packets_.
- SendAlgorithmInterface::SentPacketsMap packet_history_map_;
- // Packets that are outstanding and have not been abandoned or lost.
- SequenceNumberSet pending_packets_;
- QuicTime::Delta rtt_sample_; // RTT estimate from the most recent ACK.
+ scoped_ptr<LossDetectionInterface> loss_algorithm_;
+
+ QuicPacketSequenceNumber largest_observed_; // From the most recent ACK.
// Number of times the RTO timer has fired in a row without receiving an ack.
size_t consecutive_rto_count_;
+ // Number of times the tail loss probe has been sent.
+ size_t consecutive_tlp_count_;
+ // Number of times the crypto handshake has been retransmitted.
+ size_t consecutive_crypto_retransmission_count_;
+ // Whether a tlp packet can be sent even if the send algorithm says not to.
+ bool pending_tlp_transmission_;
+ // Maximum number of tail loss probes to send before firing an RTO.
+ size_t max_tail_loss_probes_;
bool using_pacing_;
+ // Sets of packets acked and lost as a result of the last congestion event.
+ SendAlgorithmInterface::CongestionMap packets_acked_;
+ SendAlgorithmInterface::CongestionMap packets_lost_;
+
DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManager);
};
diff --git a/chromium/net/quic/quic_sent_packet_manager_test.cc b/chromium/net/quic/quic_sent_packet_manager_test.cc
index 168cbf378cf..28f433216d9 100644
--- a/chromium/net/quic/quic_sent_packet_manager_test.cc
+++ b/chromium/net/quic/quic_sent_packet_manager_test.cc
@@ -5,42 +5,65 @@
#include "net/quic/quic_sent_packet_manager.h"
#include "base/stl_util.h"
+#include "net/quic/test_tools/quic_config_peer.h"
#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using std::vector;
-using testing::_;
+using testing::ElementsAre;
+using testing::Pair;
+using testing::Pointwise;
using testing::Return;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace test {
namespace {
-class MockHelper : public QuicSentPacketManager::HelperInterface {
+// Default packet length.
+const uint32 kDefaultLength = 1000;
+
+// Matcher to check the key of the key-value pair it receives as first argument
+// equals its second argument.
+MATCHER(KeyEq, "") {
+ return std::tr1::get<0>(arg).first == std::tr1::get<1>(arg);
+}
+
+class MockDebugDelegate : public QuicSentPacketManager::DebugDelegate {
public:
- MOCK_METHOD0(GetNextPacketSequenceNumber, QuicPacketSequenceNumber());
+ MOCK_METHOD2(OnSpuriousPacketRetransmition,
+ void(TransmissionType transmission_type,
+ QuicByteCount byte_size));
};
class QuicSentPacketManagerTest : public ::testing::TestWithParam<bool> {
protected:
QuicSentPacketManagerTest()
- : manager_(true, &helper_, &clock_, kFixRate),
+ : manager_(true, &clock_, &stats_, kFixRate, kNack),
send_algorithm_(new StrictMock<MockSendAlgorithm>) {
QuicSentPacketManagerPeer::SetSendAlgorithm(&manager_, send_algorithm_);
+ // Disable tail loss probes for most tests.
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 0);
+ // Advance the time 1s so the send times are never QuicTime::Zero.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
}
- ~QuicSentPacketManagerTest() {
+ virtual ~QuicSentPacketManagerTest() OVERRIDE {
STLDeleteElements(&packets_);
}
+ QuicByteCount BytesInFlight() {
+ return QuicSentPacketManagerPeer::GetBytesInFlight(&manager_);
+ }
void VerifyUnackedPackets(QuicPacketSequenceNumber* packets,
size_t num_packets) {
if (num_packets == 0) {
EXPECT_FALSE(manager_.HasUnackedPackets());
- EXPECT_EQ(0u, manager_.GetNumRetransmittablePackets());
+ EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ &manager_));
return;
}
@@ -53,102 +76,188 @@ class QuicSentPacketManagerTest : public ::testing::TestWithParam<bool> {
void VerifyRetransmittablePackets(QuicPacketSequenceNumber* packets,
size_t num_packets) {
- SequenceNumberSet unacked = manager_.GetUnackedPackets();
+ EXPECT_EQ(num_packets,
+ QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ &manager_));
for (size_t i = 0; i < num_packets; ++i) {
- EXPECT_TRUE(ContainsKey(unacked, packets[i])) << packets[i];
- }
- size_t num_retransmittable = 0;
- for (SequenceNumberSet::const_iterator it = unacked.begin();
- it != unacked.end(); ++it) {
- if (manager_.HasRetransmittableFrames(*it)) {
- ++num_retransmittable;
- }
+ EXPECT_TRUE(manager_.HasRetransmittableFrames(packets[i]))
+ << " packets[" << i << "]:" << packets[i];
}
- EXPECT_EQ(num_packets, manager_.GetNumRetransmittablePackets());
- EXPECT_EQ(num_packets, num_retransmittable);
}
- void VerifyAckedPackets(QuicPacketSequenceNumber* expected,
- size_t num_expected,
- const SequenceNumberSet& actual) {
- if (num_expected == 0) {
- EXPECT_TRUE(actual.empty());
- return;
- }
+ void ExpectAck(QuicPacketSequenceNumber largest_observed) {
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(
+ true, _, ElementsAre(Pair(largest_observed, _)), _));
+ }
+
+ void ExpectUpdatedRtt(QuicPacketSequenceNumber largest_observed) {
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(true, _, _, _));
+ }
+
+ void ExpectAckAndLoss(bool rtt_updated,
+ QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber lost_packet) {
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(
+ rtt_updated, _, ElementsAre(Pair(largest_observed, _)),
+ ElementsAre(Pair(lost_packet, _))));
+ }
- EXPECT_EQ(num_expected, actual.size());
- for (size_t i = 0; i < num_expected; ++i) {
- EXPECT_TRUE(ContainsKey(actual, expected[i])) << expected[i];
+ // |packets_acked| and |packets_lost| should be in sequence number order.
+ void ExpectAcksAndLosses(bool rtt_updated,
+ QuicPacketSequenceNumber* packets_acked,
+ size_t num_packets_acked,
+ QuicPacketSequenceNumber* packets_lost,
+ size_t num_packets_lost) {
+ vector<QuicPacketSequenceNumber> ack_vector;
+ for (size_t i = 0; i < num_packets_acked; ++i) {
+ ack_vector.push_back(packets_acked[i]);
+ }
+ vector<QuicPacketSequenceNumber> lost_vector;
+ for (size_t i = 0; i < num_packets_lost; ++i) {
+ lost_vector.push_back(packets_lost[i]);
}
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(rtt_updated, _,
+ Pointwise(KeyEq(), ack_vector),
+ Pointwise(KeyEq(), lost_vector)));
}
+ // Retransmits a packet as though it was a TLP retransmission, because TLP
+ // leaves the |old_sequence_number| pending.
+ // TODO(ianswett): Test with transmission types besides TLP.
void RetransmitPacket(QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number) {
QuicSentPacketManagerPeer::MarkForRetransmission(
- &manager_, old_sequence_number, NACK_RETRANSMISSION);
+ &manager_, old_sequence_number, TLP_RETRANSMISSION);
EXPECT_TRUE(manager_.HasPendingRetransmissions());
QuicSentPacketManager::PendingRetransmission next_retransmission =
manager_.NextPendingRetransmission();
EXPECT_EQ(old_sequence_number, next_retransmission.sequence_number);
- EXPECT_EQ(NACK_RETRANSMISSION, next_retransmission.transmission_type);
- manager_.OnRetransmittedPacket(old_sequence_number, new_sequence_number);
+ EXPECT_EQ(TLP_RETRANSMISSION,
+ next_retransmission.transmission_type);
+ manager_.OnRetransmittedPacket(old_sequence_number,
+ new_sequence_number);
EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(
&manager_, new_sequence_number));
}
- SerializedPacket CreatePacket(QuicPacketSequenceNumber sequence_number) {
+ void RetransmitAndSendPacket(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number) {
+ RetransmitPacket(old_sequence_number, new_sequence_number);
+
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), new_sequence_number,
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(true));
+ manager_.OnPacketSent(new_sequence_number,
+ clock_.Now(),
+ kDefaultLength,
+ LOSS_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+ }
+
+ SerializedPacket CreateDataPacket(QuicPacketSequenceNumber sequence_number) {
+ return CreatePacket(sequence_number, true);
+ }
+
+ SerializedPacket CreatePacket(QuicPacketSequenceNumber sequence_number,
+ bool retransmittable) {
packets_.push_back(QuicPacket::NewDataPacket(
- NULL, 0, false, PACKET_8BYTE_GUID, false,
+ NULL, kDefaultLength, false, PACKET_8BYTE_CONNECTION_ID, false,
PACKET_6BYTE_SEQUENCE_NUMBER));
- return SerializedPacket(sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER,
- packets_.back(), 0u, new RetransmittableFrames());
+ return SerializedPacket(
+ sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER,
+ packets_.back(), 0u,
+ retransmittable ? new RetransmittableFrames() : NULL);
}
SerializedPacket CreateFecPacket(QuicPacketSequenceNumber sequence_number) {
packets_.push_back(QuicPacket::NewFecPacket(
- NULL, 0, false, PACKET_8BYTE_GUID, false,
+ NULL, kDefaultLength, false, PACKET_8BYTE_CONNECTION_ID, false,
PACKET_6BYTE_SEQUENCE_NUMBER));
return SerializedPacket(sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER,
packets_.back(), 0u, NULL);
}
void SendDataPacket(QuicPacketSequenceNumber sequence_number) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, sequence_number, _, _, _))
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number, _, _))
+ .Times(1).WillOnce(Return(true));
+ SerializedPacket packet(CreateDataPacket(sequence_number));
+ manager_.OnSerializedPacket(packet);
+ manager_.OnPacketSent(sequence_number, clock_.Now(),
+ packet.packet->length(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+ }
+
+ void SendCryptoPacket(QuicPacketSequenceNumber sequence_number) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number,
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA))
.Times(1).WillOnce(Return(true));
- SerializedPacket packet(CreatePacket(sequence_number));
+ SerializedPacket packet(CreateDataPacket(sequence_number));
+ packet.retransmittable_frames->AddStreamFrame(
+ new QuicStreamFrame(1, false, 0, IOVector()));
+ packet.retransmittable_frames->set_encryption_level(ENCRYPTION_NONE);
manager_.OnSerializedPacket(packet);
manager_.OnPacketSent(sequence_number, clock_.ApproximateNow(),
packet.packet->length(), NOT_RETRANSMISSION,
HAS_RETRANSMITTABLE_DATA);
}
+ void SendFecPacket(QuicPacketSequenceNumber sequence_number) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number,
+ kDefaultLength, NO_RETRANSMITTABLE_DATA))
+ .Times(1).WillOnce(Return(true));
+ SerializedPacket packet(CreateFecPacket(sequence_number));
+ manager_.OnSerializedPacket(packet);
+ manager_.OnPacketSent(sequence_number, clock_.ApproximateNow(),
+ packet.packet->length(), NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA);
+ }
+
+ void SendAckPacket(QuicPacketSequenceNumber sequence_number) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number,
+ kDefaultLength, NO_RETRANSMITTABLE_DATA))
+ .Times(1).WillOnce(Return(false));
+ SerializedPacket packet(CreatePacket(sequence_number, false));
+ manager_.OnSerializedPacket(packet);
+ manager_.OnPacketSent(sequence_number, clock_.Now(),
+ packet.packet->length(), NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA);
+ }
+
// Based on QuicConnection's WritePendingRetransmissions.
void RetransmitNextPacket(
QuicPacketSequenceNumber retransmission_sequence_number) {
EXPECT_TRUE(manager_.HasPendingRetransmissions());
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, retransmission_sequence_number, _, _, _))
+ OnPacketSent(_, _, retransmission_sequence_number,
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA))
.Times(1).WillOnce(Return(true));
const QuicSentPacketManager::PendingRetransmission pending =
manager_.NextPendingRetransmission();
- manager_.OnRetransmittedPacket(
- pending.sequence_number, retransmission_sequence_number);
- manager_.OnPacketSent(retransmission_sequence_number,
- clock_.ApproximateNow(), 1000,
- pending.transmission_type, HAS_RETRANSMITTABLE_DATA);
+ manager_.OnRetransmittedPacket(pending.sequence_number,
+ retransmission_sequence_number);
+ manager_.OnPacketSent(retransmission_sequence_number, clock_.Now(),
+ kDefaultLength, pending.transmission_type,
+ HAS_RETRANSMITTABLE_DATA);
}
- testing::StrictMock<MockHelper> helper_;
QuicSentPacketManager manager_;
vector<QuicPacket*> packets_;
MockClock clock_;
+ QuicConnectionStats stats_;
MockSendAlgorithm* send_algorithm_;
};
TEST_F(QuicSentPacketManagerTest, IsUnacked) {
VerifyUnackedPackets(NULL, 0);
- SerializedPacket serialized_packet(CreatePacket(1));
+ SerializedPacket serialized_packet(CreateDataPacket(1));
manager_.OnSerializedPacket(serialized_packet);
@@ -159,9 +268,7 @@ TEST_F(QuicSentPacketManagerTest, IsUnacked) {
}
TEST_F(QuicSentPacketManagerTest, IsUnAckedRetransmit) {
- SerializedPacket serialized_packet(CreatePacket(1));
-
- manager_.OnSerializedPacket(serialized_packet);
+ SendDataPacket(1);
RetransmitPacket(1, 2);
EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(&manager_, 2));
@@ -172,34 +279,34 @@ TEST_F(QuicSentPacketManagerTest, IsUnAckedRetransmit) {
}
TEST_F(QuicSentPacketManagerTest, RetransmitThenAck) {
- SerializedPacket serialized_packet(CreatePacket(1));
-
- manager_.OnSerializedPacket(serialized_packet);
- RetransmitPacket(1, 2);
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
// Ack 2 but not 1.
ReceivedPacketInfo received_info;
received_info.largest_observed = 2;
received_info.missing_packets.insert(1);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ ExpectAck(2);
+ manager_.OnIncomingAck(received_info, clock_.Now());
- // No unacked packets remain.
- VerifyUnackedPackets(NULL, 0);
+ // Packet 1 is unacked, pending, but not retransmittable.
+ QuicPacketSequenceNumber unacked[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
VerifyRetransmittablePackets(NULL, 0);
}
TEST_F(QuicSentPacketManagerTest, RetransmitThenAckBeforeSend) {
- SerializedPacket serialized_packet(CreatePacket(1));
-
- manager_.OnSerializedPacket(serialized_packet);
+ SendDataPacket(1);
QuicSentPacketManagerPeer::MarkForRetransmission(
- &manager_, 1, NACK_RETRANSMISSION);
+ &manager_, 1, TLP_RETRANSMISSION);
EXPECT_TRUE(manager_.HasPendingRetransmissions());
// Ack 1.
ReceivedPacketInfo received_info;
received_info.largest_observed = 1;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ ExpectAck(1);
+ manager_.OnIncomingAck(received_info, clock_.Now());
// There should no longer be a pending retransmission.
EXPECT_FALSE(manager_.HasPendingRetransmissions());
@@ -207,211 +314,311 @@ TEST_F(QuicSentPacketManagerTest, RetransmitThenAckBeforeSend) {
// No unacked packets remain.
VerifyUnackedPackets(NULL, 0);
VerifyRetransmittablePackets(NULL, 0);
+ EXPECT_EQ(0u, stats_.packets_spuriously_retransmitted);
}
TEST_F(QuicSentPacketManagerTest, RetransmitThenAckPrevious) {
- SerializedPacket serialized_packet(CreatePacket(1));
-
- manager_.OnSerializedPacket(serialized_packet);
+ SendDataPacket(1);
RetransmitPacket(1, 2);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
+
+ // Ack 1 but not 2.
+ ExpectAck(1);
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 1;
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ // 2 should be unacked, since it may provide an RTT measurement.
+ QuicPacketSequenceNumber unacked[] = { 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ // Verify that the retransmission alarm would not fire,
+ // since there is no retransmittable data outstanding.
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+ EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitAndSendThenAckPrevious) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
// Ack 1 but not 2.
+ ExpectAck(1);
ReceivedPacketInfo received_info;
received_info.largest_observed = 1;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
// 2 remains unacked, but no packets have retransmittable data.
QuicPacketSequenceNumber unacked[] = { 2 };
VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
VerifyRetransmittablePackets(NULL, 0);
- // Verify that if the retransmission alarm does fire to abandon packet 2,
- // the sent packet manager is not notified, since there is no retransmittable
- // data outstanding.
- EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
- .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1)));
+ EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitThenAckPreviousThenNackRetransmit) {
+ SendDataPacket(1);
+ RetransmitPacket(1, 2);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2, _, _))
+ .WillOnce(Return(true));
+ manager_.OnPacketSent(2, clock_.ApproximateNow(), kDefaultLength,
+ LOSS_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
+
+ // First, ACK packet 1 which makes packet 2 non-retransmittable.
+ ExpectAck(1);
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 1;
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ SendDataPacket(3);
+ SendDataPacket(4);
+ SendDataPacket(5);
+ clock_.AdvanceTime(rtt);
+
+ // Next, NACK packet 2 three times.
+ received_info.largest_observed = 3;
+ received_info.missing_packets.insert(2);
+ ExpectAck(3);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ received_info.largest_observed = 4;
+ ExpectAck(4);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ received_info.largest_observed = 5;
+ ExpectAckAndLoss(true, 5, 2);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ // No packets remain unacked.
+ VerifyUnackedPackets(NULL, 0);
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ // Verify that the retransmission alarm would not fire,
+ // since there is no retransmittable data outstanding.
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitTwiceThenAckPreviousBeforeSend) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+
+ // Fire the RTO, which will mark 2 for retransmission (but will not send it).
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
manager_.OnRetransmissionTimeout();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+
+ // Ack 1 but not 2, before 2 is able to be sent.
+ // Since 1 has been retransmitted, it has already been lost, and so the
+ // send algorithm is not informed that it has been ACK'd.
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 1;
+ ExpectUpdatedRtt(1);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ // Since 2 was marked for retransmit, when 1 is acked, 2 is kept for RTT.
+ QuicPacketSequenceNumber unacked[] = { 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ // Verify that the retransmission alarm would not fire,
+ // since there is no retransmittable data outstanding.
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
}
TEST_F(QuicSentPacketManagerTest, RetransmitTwiceThenAckFirst) {
- SerializedPacket serialized_packet(CreatePacket(1));
+ StrictMock<MockDebugDelegate> debug_delegate;
+ EXPECT_CALL(debug_delegate, OnSpuriousPacketRetransmition(
+ TLP_RETRANSMISSION, kDefaultLength)).Times(2);
+ manager_.set_debug_delegate(&debug_delegate);
- manager_.OnSerializedPacket(serialized_packet);
- RetransmitPacket(1, 2);
- RetransmitPacket(2, 3);
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ RetransmitAndSendPacket(2, 3);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
// Ack 1 but not 2 or 3.
+ ExpectAck(1);
ReceivedPacketInfo received_info;
received_info.largest_observed = 1;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- // 3 remains unacked, but no packets have retransmittable data.
- QuicPacketSequenceNumber unacked[] = { 3 };
+ // 2 and 3 remain unacked, but no packets have retransmittable data.
+ QuicPacketSequenceNumber unacked[] = { 2, 3 };
VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
VerifyRetransmittablePackets(NULL, 0);
- // Verify that if the retransmission alarm does fire to abandon packet 3,
- // the sent packet manager is not notified, since there is no retransmittable
- // data outstanding.
- EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
- .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1)));
- manager_.OnRetransmissionTimeout();
-}
+ // Ensure packet 2 is lost when 4 is sent and 3 and 4 are acked.
+ SendDataPacket(4);
+ received_info.largest_observed = 4;
+ received_info.missing_packets.insert(2);
+ QuicPacketSequenceNumber acked[] = { 3, 4 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), NULL, 0);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
-TEST_F(QuicSentPacketManagerTest, TruncatedAck) {
- SerializedPacket serialized_packet(CreatePacket(1));
+ QuicPacketSequenceNumber unacked2[] = { 2 };
+ VerifyUnackedPackets(unacked2, arraysize(unacked2));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
- manager_.OnSerializedPacket(serialized_packet);
- RetransmitPacket(1, 2);
- RetransmitPacket(2, 3);
- RetransmitPacket(3, 4);
+ SendDataPacket(5);
+ received_info.largest_observed = 5;
+ ExpectAckAndLoss(true, 5, 2);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ VerifyUnackedPackets(NULL, 0);
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ EXPECT_EQ(2u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_F(QuicSentPacketManagerTest, LoseButDontRetransmitRevivedPacket) {
+ SendDataPacket(1);
+ SendDataPacket(2);
+ SendFecPacket(3);
+ SendDataPacket(4);
- // Truncated ack with 2 NACKs
+ // Ack 2 and 3, and mark 1 as revived.
ReceivedPacketInfo received_info;
- received_info.largest_observed = 2;
+ received_info.largest_observed = 3;
received_info.missing_packets.insert(1);
- received_info.missing_packets.insert(2);
- received_info.is_truncated = true;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ received_info.revived_packets.insert(1);
+ QuicPacketSequenceNumber acked[] = { 2, 3 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), NULL, 0);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- // High water mark will be raised.
- QuicPacketSequenceNumber unacked[] = { 2, 3, 4 };
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ QuicPacketSequenceNumber unacked[] = { 1, 4 };
VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
QuicPacketSequenceNumber retransmittable[] = { 4 };
VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
-}
-TEST_F(QuicSentPacketManagerTest, SendDropAckRetransmitManyPackets) {
- manager_.OnSerializedPacket(CreatePacket(1));
- manager_.OnSerializedPacket(CreatePacket(2));
- manager_.OnSerializedPacket(CreatePacket(3));
+ // Ack the 4th packet and expect the 1st to be considered lost.
+ received_info.largest_observed = 4;
+ ExpectAckAndLoss(true, 4, 1);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- {
- // Ack packets 1 and 3.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = 3;
- received_info.missing_packets.insert(2);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
-
- QuicPacketSequenceNumber unacked[] = { 2 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 2 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
- }
-
- manager_.OnSerializedPacket(CreatePacket(4));
- manager_.OnSerializedPacket(CreatePacket(5));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ VerifyRetransmittablePackets(NULL, 0);
+}
- {
- // Ack packets 5.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = 5;
- received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(4);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+TEST_F(QuicSentPacketManagerTest, MarkLostThenReviveAndDontRetransmitPacket) {
+ SendDataPacket(1);
+ SendDataPacket(2);
+ SendDataPacket(3);
+ SendDataPacket(4);
+ SendFecPacket(5);
- QuicPacketSequenceNumber unacked[] = { 2, 4 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 2, 4 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
- }
+ // Ack 2, 3, and 4, and expect the 1st to be considered lost.
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 4;
+ received_info.missing_packets.insert(1);
+ QuicPacketSequenceNumber acked[] = { 2, 3, 4 };
+ QuicPacketSequenceNumber lost[] = { 1 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), lost, arraysize(lost));
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- manager_.OnSerializedPacket(CreatePacket(6));
- manager_.OnSerializedPacket(CreatePacket(7));
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ QuicPacketSequenceNumber unacked[] = { 1, 5 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 1 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
- {
- // Ack packets 7.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = 7;
- received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(4);
- received_info.missing_packets.insert(6);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ // Ack 5th packet (FEC) and revive 1st packet. 1st packet should now be
+ // removed from pending retransmissions map.
+ received_info.largest_observed = 5;
+ received_info.revived_packets.insert(1);
+ ExpectAck(5);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- QuicPacketSequenceNumber unacked[] = { 2, 4, 6 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 2, 4, 6 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
- }
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ VerifyRetransmittablePackets(NULL, 0);
+}
- RetransmitPacket(2, 8);
- manager_.OnSerializedPacket(CreatePacket(9));
- manager_.OnSerializedPacket(CreatePacket(10));
+TEST_F(QuicSentPacketManagerTest, TruncatedAck) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ RetransmitAndSendPacket(2, 3);
+ RetransmitAndSendPacket(3, 4);
+ RetransmitAndSendPacket(4, 5);
- {
- // Ack packet 10.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = 10;
- received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(4);
- received_info.missing_packets.insert(6);
- received_info.missing_packets.insert(8);
- received_info.missing_packets.insert(9);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
-
- QuicPacketSequenceNumber unacked[] = { 2, 4, 6, 8, 9 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 4, 6, 8, 9 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
- }
+ // Truncated ack with 4 NACKs, so the first packet is lost.
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 4;
+ received_info.missing_packets.insert(1);
+ received_info.missing_packets.insert(2);
+ received_info.missing_packets.insert(3);
+ received_info.missing_packets.insert(4);
+ received_info.is_truncated = true;
+ QuicPacketSequenceNumber lost[] = { 1 };
+ ExpectAcksAndLosses(true, NULL, 0, lost, arraysize(lost));
+ manager_.OnIncomingAck(received_info, clock_.Now());
- RetransmitPacket(4, 11);
- manager_.OnSerializedPacket(CreatePacket(12));
- manager_.OnSerializedPacket(CreatePacket(13));
+ // High water mark will be raised.
+ QuicPacketSequenceNumber unacked[] = { 2, 3, 4, 5 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 5 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+}
+TEST_F(QuicSentPacketManagerTest, AckPreviousTransmissionThenTruncatedAck) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ RetransmitAndSendPacket(2, 3);
+ RetransmitAndSendPacket(3, 4);
+ manager_.OnSerializedPacket(CreateDataPacket(5));
+ manager_.OnSerializedPacket(CreateDataPacket(6));
+ manager_.OnSerializedPacket(CreateDataPacket(7));
+ manager_.OnSerializedPacket(CreateDataPacket(8));
+ manager_.OnSerializedPacket(CreateDataPacket(9));
+
+ // Ack previous transmission
{
- // Ack packet 13.
ReceivedPacketInfo received_info;
- received_info.largest_observed = 13;
- received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(4);
- received_info.missing_packets.insert(6);
- received_info.missing_packets.insert(8);
- received_info.missing_packets.insert(9);
- received_info.missing_packets.insert(11);
- received_info.missing_packets.insert(12);
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
-
- QuicPacketSequenceNumber unacked[] = { 2, 4, 6, 8, 9, 11, 12 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 6, 8, 9, 11, 12 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+ received_info.largest_observed = 2;
+ received_info.missing_packets.insert(1);
+ ExpectAck(2);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+ EXPECT_TRUE(manager_.IsUnacked(4));
}
- RetransmitPacket(6, 14);
- manager_.OnSerializedPacket(CreatePacket(15));
- manager_.OnSerializedPacket(CreatePacket(16));
-
+ // Truncated ack with 4 NACKs
{
- // Ack packet 16.
ReceivedPacketInfo received_info;
- received_info.largest_observed = 13;
- received_info.missing_packets.insert(2);
+ received_info.largest_observed = 6;
+ received_info.missing_packets.insert(3);
received_info.missing_packets.insert(4);
+ received_info.missing_packets.insert(5);
received_info.missing_packets.insert(6);
- received_info.missing_packets.insert(8);
- received_info.missing_packets.insert(9);
- received_info.missing_packets.insert(11);
- received_info.missing_packets.insert(12);
received_info.is_truncated = true;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
-
- // Truncated ack raises the high water mark by clearing out 2, 4, and 6.
- QuicPacketSequenceNumber unacked[] = { 8, 9, 11, 12, 14, 15, 16 };
- VerifyUnackedPackets(unacked, arraysize(unacked));
- QuicPacketSequenceNumber retransmittable[] = { 8, 9, 11, 12, 14, 15, 16 };
- VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+ ExpectAckAndLoss(false, 1, 3);
+ manager_.OnIncomingAck(received_info, clock_.Now());
}
+
+ // High water mark will be raised.
+ QuicPacketSequenceNumber unacked[] = { 4, 5, 6, 7, 8, 9 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 5, 6, 7, 8, 9 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
}
TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacket) {
- EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(1u));
- EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket());
+ EXPECT_EQ(0u, manager_.GetLeastUnackedSentPacket());
}
TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketUnacked) {
- SerializedPacket serialized_packet(CreatePacket(1));
+ SerializedPacket serialized_packet(CreateDataPacket(1));
manager_.OnSerializedPacket(serialized_packet);
EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket());
@@ -424,15 +631,6 @@ TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketUnackedFec) {
EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket());
}
-TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketDiscardUnacked) {
- SerializedPacket serialized_packet(CreatePacket(1));
-
- manager_.OnSerializedPacket(serialized_packet);
- manager_.DiscardUnackedPacket(1u);
- EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(2u));
- EXPECT_EQ(2u, manager_.GetLeastUnackedSentPacket());
-}
-
TEST_F(QuicSentPacketManagerTest, GetLeastUnackedPacketAndDiscard) {
VerifyUnackedPackets(NULL, 0);
@@ -452,19 +650,12 @@ TEST_F(QuicSentPacketManagerTest, GetLeastUnackedPacketAndDiscard) {
VerifyUnackedPackets(unacked, arraysize(unacked));
VerifyRetransmittablePackets(NULL, 0);
- manager_.DiscardUnackedPacket(1);
- EXPECT_EQ(2u, manager_.GetLeastUnackedSentPacket());
-
- // Ack 2.
+ // Ack 2, which has never been sent, so there's no rtt update.
ReceivedPacketInfo received_info;
received_info.largest_observed = 2;
- manager_.OnIncomingAck(received_info, QuicTime::Zero());
+ manager_.OnIncomingAck(received_info, clock_.Now());
EXPECT_EQ(3u, manager_.GetLeastUnackedSentPacket());
-
- // Discard the 3rd packet and ensure there are no FEC packets.
- manager_.DiscardUnackedPacket(3);
- EXPECT_FALSE(manager_.HasUnackedPackets());
}
TEST_F(QuicSentPacketManagerTest, GetSentTime) {
@@ -472,17 +663,18 @@ TEST_F(QuicSentPacketManagerTest, GetSentTime) {
SerializedPacket serialized_packet(CreateFecPacket(1));
manager_.OnSerializedPacket(serialized_packet);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _))
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _))
.Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(
- 1, QuicTime::Zero(), 0, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ manager_.OnPacketSent(1, QuicTime::Zero(), kDefaultLength, NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA);
+
SerializedPacket serialized_packet2(CreateFecPacket(2));
QuicTime sent_time = QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(1));
manager_.OnSerializedPacket(serialized_packet2);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 2, _, _, _))
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2, _, _))
.Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(
- 2, sent_time, 0, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ manager_.OnPacketSent(2, sent_time, kDefaultLength, NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA);
QuicPacketSequenceNumber unacked[] = { 1, 2 };
VerifyUnackedPackets(unacked, arraysize(unacked));
@@ -494,332 +686,431 @@ TEST_F(QuicSentPacketManagerTest, GetSentTime) {
EXPECT_EQ(sent_time, QuicSentPacketManagerPeer::GetSentTime(&manager_, 2));
}
-TEST_F(QuicSentPacketManagerTest, NackRetransmit1Packet) {
- const size_t kNumSentPackets = 4;
- // Transmit 4 packets.
- for (size_t i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+TEST_F(QuicSentPacketManagerTest, AckAckAndUpdateRtt) {
+ SendDataPacket(1);
+ SendAckPacket(2);
- // Nack the first packet 3 times with increasing largest observed.
+ // Now ack the ack and expect an RTT update.
ReceivedPacketInfo received_info;
+ received_info.largest_observed = 2;
received_info.delta_time_largest_observed =
QuicTime::Delta::FromMilliseconds(5);
- received_info.missing_packets.insert(1);
- for (QuicPacketSequenceNumber i = 1; i <= 3; ++i) {
- received_info.largest_observed = i + 1;
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(i + 1, _, _)).Times(1);
- if (i == 3) {
- EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- }
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(i == 3 ? 1u : 0u, retransmissions.size());
- EXPECT_EQ(i, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
- }
+
+ ExpectAck(1);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+
+ SendAckPacket(3);
+
+ // Now ack the ack and expect only an RTT update.
+ received_info.largest_observed = 3;
+ ExpectUpdatedRtt(3);
+ manager_.OnIncomingAck(received_info, clock_.Now());
}
-// A stretch ack is an ack that covers more than 1 packet of previously
-// unacknowledged data.
-TEST_F(QuicSentPacketManagerTest, NackRetransmit1PacketWith1StretchAck) {
- const size_t kNumSentPackets = 4;
- // Transmit 4 packets.
- for (size_t i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+TEST_F(QuicSentPacketManagerTest, Rtt) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(15);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
- // Nack the first packet 3 times in a single StretchAck.
+ ExpectAck(sequence_number);
ReceivedPacketInfo received_info;
+ received_info.largest_observed = sequence_number;
received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- received_info.missing_packets.insert(1);
- received_info.largest_observed = kNumSentPackets;
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(3);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(1u, retransmissions.size());
- EXPECT_EQ(3u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
-}
-
-// Ack a packet 3 packets ahead, causing a retransmit.
-TEST_F(QuicSentPacketManagerTest, NackRetransmit1PacketSingleAck) {
- const size_t kNumSentPackets = 4;
- // Transmit 4 packets.
- for (size_t i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+ QuicTime::Delta::FromMilliseconds(5);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
- // Nack the first packet 3 times in an AckFrame with three missing packets.
+TEST_F(QuicSentPacketManagerTest, RttWithInvalidDelta) {
+ // Expect that the RTT is equal to the local time elapsed, since the
+ // delta_time_largest_observed is larger than the local time elapsed
+ // and is hence invalid.
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(expected_rtt);
+
+ ExpectAck(sequence_number);
ReceivedPacketInfo received_info;
+ received_info.largest_observed = sequence_number;
received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- received_info.missing_packets.insert(1);
- received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(3);
- received_info.largest_observed = kNumSentPackets;
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(kNumSentPackets, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(1u, retransmissions.size());
- EXPECT_EQ(3u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
-}
-
-TEST_F(QuicSentPacketManagerTest, EarlyRetransmit1Packet) {
- const size_t kNumSentPackets = 2;
- // Transmit 2 packets.
- for (size_t i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+ QuicTime::Delta::FromMilliseconds(11);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, RttWithInfiniteDelta) {
+ // Expect that the RTT is equal to the local time elapsed, since the
+ // delta_time_largest_observed is infinite, and is hence invalid.
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(expected_rtt);
- // Early retransmit when the final packet gets acked and the first is nacked.
+ ExpectAck(sequence_number);
ReceivedPacketInfo received_info;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- received_info.missing_packets.insert(1);
- received_info.largest_observed = kNumSentPackets;
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(kNumSentPackets, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(1u, retransmissions.size());
- EXPECT_EQ(1u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
-}
-
-TEST_F(QuicSentPacketManagerTest, DontEarlyRetransmitPacket) {
- const size_t kNumSentPackets = 4;
- for (size_t i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+ received_info.largest_observed = sequence_number;
+ received_info.delta_time_largest_observed = QuicTime::Delta::Infinite();
+ manager_.OnIncomingAck(received_info, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, RttZeroDelta) {
+ // Expect that the RTT is the time between send and receive since the
+ // delta_time_largest_observed is zero.
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(expected_rtt);
- // Fast retransmit when the final packet gets acked, but don't early
- // retransmit as well, because there are 4 packets outstanding when the ack
- // arrives.
+ ExpectAck(sequence_number);
ReceivedPacketInfo received_info;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
+ received_info.largest_observed = sequence_number;
+ received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ manager_.OnIncomingAck(received_info, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, TailLossProbeTimeout) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+
+ // Send 1 packet.
+ QuicPacketSequenceNumber sequence_number = 1;
+ SendDataPacket(sequence_number);
+
+ // The first tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(2);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // The second tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(3);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // Ack the third and ensure the first two are still pending.
+ ExpectAck(3);
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 3;
received_info.missing_packets.insert(1);
received_info.missing_packets.insert(2);
- received_info.missing_packets.insert(3);
- received_info.largest_observed = kNumSentPackets;
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(kNumSentPackets, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(1u, retransmissions.size());
- EXPECT_EQ(3u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
-}
-
-TEST_F(QuicSentPacketManagerTest, NackRetransmit2Packets) {
- const size_t kNumSentPackets = 20;
- // Transmit 20 packets.
- for (QuicPacketSequenceNumber i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- }
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
- // Nack the first 19 packets 3 times.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = kNumSentPackets;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- for (size_t i = 1; i < kNumSentPackets; ++i) {
- received_info.missing_packets.insert(i);
- }
- EXPECT_CALL(*send_algorithm_,
- OnPacketAcked(kNumSentPackets, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(2u, retransmissions.size());
- for (size_t i = 1; i < kNumSentPackets; ++i) {
- EXPECT_EQ(kNumSentPackets - i,
- QuicSentPacketManagerPeer::GetNackCount(&manager_, i));
- }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+
+ // Acking two more packets will lose both of them due to nacks.
+ received_info.largest_observed = 5;
+ QuicPacketSequenceNumber lost[] = { 1, 2 };
+ ExpectAcksAndLosses(false, NULL, 0, lost, arraysize(lost));
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ EXPECT_EQ(2u, stats_.tlp_count);
+ EXPECT_EQ(0u, stats_.rto_count);
}
-TEST_F(QuicSentPacketManagerTest, NackRetransmit2PacketsAlternateAcks) {
- const size_t kNumSentPackets = 30;
- // Transmit 15 packets of data and 15 ack packets. The send algorithm will
- // inform the congestion manager not to save the acks by returning false.
- for (QuicPacketSequenceNumber i = 1; i <= kNumSentPackets; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(i % 2 == 0 ? false : true));
- manager_.OnPacketSent(
- i, clock_.Now(), 1000, NOT_RETRANSMISSION,
- i % 2 == 0 ? NO_RETRANSMITTABLE_DATA : HAS_RETRANSMITTABLE_DATA);
- }
+TEST_F(QuicSentPacketManagerTest, TailLossProbeThenRTO) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
- // Nack the first 29 packets 3 times.
- ReceivedPacketInfo received_info;
- received_info.largest_observed = kNumSentPackets;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- for (size_t i = 1; i < kNumSentPackets; ++i) {
- received_info.missing_packets.insert(i);
- }
- // We never actually get an ack call, since the kNumSentPackets packet was
- // not saved.
- EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(2u, retransmissions.size());
- // Only non-ack packets have a nack count.
- for (size_t i = 1; i < kNumSentPackets; i += 2) {
- EXPECT_EQ(kNumSentPackets - i,
- QuicSentPacketManagerPeer::GetNackCount(&manager_, i));
+ // Send 100 packets.
+ const size_t kNumSentPackets = 100;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
}
- // Ensure only the odd packets were retransmitted, since the others were not
- // retransmittable(ie: acks).
- for (SequenceNumberSet::const_iterator it = retransmissions.begin();
- it != retransmissions.end(); ++it) {
- EXPECT_EQ(1u, *it % 2);
- }
+ // The first tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(101);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // The second tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe());
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(102);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+
+ // Advance the time enough to ensure all packets are RTO'd.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
+
+ // The final RTO abandons all of them.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ manager_.OnRetransmissionTimeout();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(2u, stats_.tlp_count);
+ EXPECT_EQ(1u, stats_.rto_count);
}
-TEST_F(QuicSentPacketManagerTest, NackTwiceThenAck) {
- // Transmit 4 packets.
- for (QuicPacketSequenceNumber i = 1; i <= 4; ++i) {
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- manager_.OnPacketSent(i, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeout) {
+ // Send 2 crypto packets and 3 data packets.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
+ }
+ const size_t kNumSentDataPackets = 3;
+ for (size_t i = 1; i <= kNumSentDataPackets; ++i) {
+ SendDataPacket(kNumSentCryptoPackets + i);
}
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // The first retransmits 2 packets.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(6);
+ RetransmitNextPacket(7);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
- // Nack the first packet 2 times, then ack it.
+ // The second retransmits 2 packets.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(8);
+ RetransmitNextPacket(9);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Now ack the two crypto packets and the speculatively encrypted request,
+ // and ensure the first four crypto packets get abandoned, but not lost.
+ QuicPacketSequenceNumber acked[] = { 3, 4, 5, 8, 9 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), NULL, 0);
ReceivedPacketInfo received_info;
+ received_info.largest_observed = 9;
received_info.missing_packets.insert(1);
- for (size_t i = 1; i <= 3; ++i) {
- if (i == 3) {
- received_info.missing_packets.clear();
- }
- received_info.largest_observed = i + 1;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- EXPECT_CALL(*send_algorithm_,
- OnPacketAcked(_, _, _)).Times(i == 3 ? 2 : 1);
- SequenceNumberSet retransmissions =
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(0u, retransmissions.size());
- // The nack count remains at 2 when the packet is acked.
- EXPECT_EQ(i == 3 ? 2u : i,
- QuicSentPacketManagerPeer::GetNackCount(&manager_, 1));
+ received_info.missing_packets.insert(2);
+ received_info.missing_packets.insert(6);
+ received_info.missing_packets.insert(7);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+}
+
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeoutVersionNegotiation) {
+ // Send 2 crypto packets and 3 data packets.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
}
+ const size_t kNumSentDataPackets = 3;
+ for (size_t i = 1; i <= kNumSentDataPackets; ++i) {
+ SendDataPacket(kNumSentCryptoPackets + i);
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // The first retransmission timeout retransmits 2 crypto packets.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(6);
+ RetransmitNextPacket(7);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Now act like a version negotiation packet arrived, which would cause all
+ // unacked packets to be retransmitted.
+ manager_.RetransmitUnackedPackets(ALL_PACKETS);
+
+ // Ensure the first two pending packets are the crypto retransmits.
+ ASSERT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(6u, manager_.NextPendingRetransmission().sequence_number);
+ RetransmitNextPacket(8);
+ EXPECT_EQ(7u, manager_.NextPendingRetransmission().sequence_number);
+ RetransmitNextPacket(9);
+
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
}
-TEST_F(QuicSentPacketManagerTest, Rtt) {
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(15);
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeSpuriousRetransmission) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- EXPECT_CALL(*send_algorithm_,
- OnPacketAcked(sequence_number, _, expected_rtt)).Times(1);
+ // Retransmit the crypto packet as 2.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
- manager_.OnPacketSent(sequence_number, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+ // Retransmit the crypto packet as 3.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(3);
+ // Now ack the second crypto packet, and ensure the first gets removed, but
+ // the third does not.
+ ExpectUpdatedRtt(2);
ReceivedPacketInfo received_info;
- received_info.largest_observed = sequence_number;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(5);
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_));
+ received_info.largest_observed = 2;
+ received_info.missing_packets.insert(1);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ QuicPacketSequenceNumber unacked[] = { 3 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
}
-TEST_F(QuicSentPacketManagerTest, RttWithInvalidDelta) {
- // Expect that the RTT is equal to the local time elapsed, since the
- // delta_time_largest_observed is larger than the local time elapsed
- // and is hence invalid.
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeoutUnsentDataPacket) {
+ // Send 2 crypto packets and serialize 1 data packet.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
+ }
+ SerializedPacket packet(CreateDataPacket(3));
+ manager_.OnSerializedPacket(packet);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit 2 crypto packets, but not the serialized packet.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(6);
+ RetransmitNextPacket(7);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+}
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- EXPECT_CALL(*send_algorithm_,
- OnPacketAcked(sequence_number, _, expected_rtt)).Times(1);
+TEST_F(QuicSentPacketManagerTest,
+ CryptoHandshakeRetransmissionThenRetransmitAll) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
- manager_.OnPacketSent(sequence_number, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ // Retransmit the crypto packet as 2.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
- ReceivedPacketInfo received_info;
- received_info.largest_observed = sequence_number;
- received_info.delta_time_largest_observed =
- QuicTime::Delta::FromMilliseconds(11);
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_));
+ // Now retransmit all the unacked packets, which occurs when there is a
+ // version negotiation.
+ manager_.RetransmitUnackedPackets(ALL_PACKETS);
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
}
-TEST_F(QuicSentPacketManagerTest, RttWithInfiniteDelta) {
- // Expect that the RTT is equal to the local time elapsed, since the
- // delta_time_largest_observed is infinite, and is hence invalid.
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+TEST_F(QuicSentPacketManagerTest,
+ CryptoHandshakeRetransmissionThenNeuterAndAck) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- EXPECT_CALL(*send_algorithm_,
- OnPacketAcked(sequence_number, _, expected_rtt)).Times(1);
+ // Retransmit the crypto packet as 2.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
- manager_.OnPacketSent(sequence_number, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ // Retransmit the crypto packet as 3.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(3);
+ // Now neuter all unacked unencrypted packets, which occurs when the
+ // connection goes forward secure.
+ manager_.NeuterUnencryptedPackets();
+ QuicPacketSequenceNumber unacked[] = { 1, 2, 3};
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(NULL, 0);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+
+ // Ensure both packets get discarded when packet 2 is acked.
ReceivedPacketInfo received_info;
- received_info.largest_observed = sequence_number;
- received_info.delta_time_largest_observed = QuicTime::Delta::Infinite();
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_));
+ received_info.largest_observed = 3;
+ received_info.missing_packets.insert(1);
+ received_info.missing_packets.insert(2);
+ ExpectUpdatedRtt(3);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+ VerifyUnackedPackets(NULL, 0);
+ VerifyRetransmittablePackets(NULL, 0);
}
-TEST_F(QuicSentPacketManagerTest, RttZeroDelta) {
- // Expect that the RTT is the time between send and receive since the
- // delta_time_largest_observed is zero.
- QuicPacketSequenceNumber sequence_number = 1;
- QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+TEST_F(QuicSentPacketManagerTest, TailLossProbeTimeoutUnsentDataPacket) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+ // Serialize two data packets and send the latter.
+ SerializedPacket packet(CreateDataPacket(1));
+ manager_.OnSerializedPacket(packet);
+ SendDataPacket(2);
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(1).WillOnce(Return(true));
- EXPECT_CALL(*send_algorithm_, OnPacketAcked(sequence_number, _, expected_rtt))
- .Times(1);
+ // Retransmit 1 unacked packets, but not the first serialized packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(3);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+}
- manager_.OnPacketSent(sequence_number, clock_.Now(), 1000,
- NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
- clock_.AdvanceTime(expected_rtt);
+TEST_F(QuicSentPacketManagerTest, ResetRecentMinRTTWithEmptyWindow) {
+ QuicTime::Delta min_rtt = QuicTime::Delta::FromMilliseconds(50);
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->UpdateRtt(
+ min_rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->min_rtt());
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(
+ &manager_)->recent_min_rtt());
+
+ // Send two packets with no prior bytes in flight.
+ SendDataPacket(1);
+ SendDataPacket(2);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ // Ack two packets with 100ms RTT observations.
ReceivedPacketInfo received_info;
- received_info.largest_observed = sequence_number;
received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
- manager_.OnIncomingAckFrame(received_info, clock_.Now());
- EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_));
+ received_info.largest_observed = 1;
+ ExpectAck(1);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+
+ // First ack does not change recent min rtt.
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(
+ &manager_)->recent_min_rtt());
+
+ received_info.largest_observed = 2;
+ ExpectAck(2);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100),
+ QuicSentPacketManagerPeer::GetRttStats(
+ &manager_)->recent_min_rtt());
}
TEST_F(QuicSentPacketManagerTest, RetransmissionTimeout) {
@@ -829,20 +1120,134 @@ TEST_F(QuicSentPacketManagerTest, RetransmissionTimeout) {
SendDataPacket(i);
}
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(kNumSentPackets);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_FALSE(manager_.MaybeRetransmitTailLossProbe());
+ manager_.OnRetransmissionTimeout();
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTime) {
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeCryptoHandshake) {
+ SendCryptoPacket(1);
+
+ // Check the min.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 1 * base::Time::kMicrosecondsPerMillisecond);
+ EXPECT_EQ(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)),
+ manager_.GetRetransmissionTime());
+
+ // Test with a standard smoothed RTT.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 100 * base::Time::kMicrosecondsPerMillisecond);
+
+ QuicTime::Delta srtt = manager_.GetRttStats()->SmoothedRtt();
+ QuicTime expected_time = clock_.Now().Add(srtt.Multiply(1.5));
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ clock_.AdvanceTime(srtt.Multiply(1.5));
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
+
+ // The retransmission time should now be twice as far in the future.
+ expected_time = clock_.Now().Add(srtt.Multiply(2).Multiply(1.5));
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeTailLossProbe) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+ SendDataPacket(1);
+ SendDataPacket(2);
+
+ // Check the min.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 1 * base::Time::kMicrosecondsPerMillisecond);
+ EXPECT_EQ(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)),
+ manager_.GetRetransmissionTime());
+
+ // Test with a standard smoothed RTT.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 100 * base::Time::kMicrosecondsPerMillisecond);
+ QuicTime::Delta srtt = manager_.GetRttStats()->SmoothedRtt();
+ QuicTime::Delta expected_tlp_delay = srtt.Multiply(2);
+ QuicTime expected_time = clock_.Now().Add(expected_tlp_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ clock_.AdvanceTime(expected_tlp_delay);
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe());
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(3);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ expected_time = clock_.Now().Add(expected_tlp_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeRTO) {
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+
+ SendDataPacket(1);
+ SendDataPacket(2);
+
+ QuicTime::Delta expected_rto_delay = QuicTime::Delta::FromMilliseconds(500);
EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
- .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1)));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
+ .WillRepeatedly(Return(expected_rto_delay));
+ QuicTime expected_time = clock_.Now().Add(expected_rto_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ clock_.AdvanceTime(expected_rto_delay);
manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(3);
+ RetransmitNextPacket(4);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // The delay should double the second time.
+ expected_time = clock_.Now().Add(expected_rto_delay).Add(expected_rto_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Ack a packet and ensure the RTO goes back to the original value.
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 2;
+ received_info.missing_packets.insert(1);
+ ExpectUpdatedRtt(2);
+ manager_.OnIncomingAck(received_info, clock_.ApproximateNow());
+
+ expected_time = clock_.Now().Add(expected_rto_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
}
TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMin) {
+ SendDataPacket(1);
EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
- .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1)));
+ .WillRepeatedly(Return(QuicTime::Delta::FromMilliseconds(1)));
+ QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(200);
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200),
- manager_.GetRetransmissionDelay());
+ // If the delay is smaller than the min, ensure it exponentially backs off
+ // from the min.
+ for (int i = 0; i < 5; ++i) {
+ EXPECT_EQ(delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+ delay = delay.Add(delay);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(i + 2);
+ }
}
TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMax) {
@@ -850,7 +1255,7 @@ TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMax) {
.WillOnce(Return(QuicTime::Delta::FromSeconds(500)));
EXPECT_EQ(QuicTime::Delta::FromSeconds(60),
- manager_.GetRetransmissionDelay());
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
}
TEST_F(QuicSentPacketManagerTest, GetTransmissionDelay) {
@@ -861,36 +1266,62 @@ TEST_F(QuicSentPacketManagerTest, GetTransmissionDelay) {
// Delay should back off exponentially.
for (int i = 0; i < 5; ++i) {
- EXPECT_EQ(delay, manager_.GetRetransmissionDelay());
+ EXPECT_EQ(delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
delay = delay.Add(delay);
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(i + 1, _));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
manager_.OnRetransmissionTimeout();
RetransmitNextPacket(i + 2);
}
}
-TEST_F(QuicSentPacketManagerTest, GetTestTransmissionDelayTailDrop) {
- FLAGS_limit_rto_increase_for_tests = true;
+TEST_F(QuicSentPacketManagerTest, GetLossDelay) {
+ MockLossAlgorithm* loss_algorithm = new MockLossAlgorithm();
+ QuicSentPacketManagerPeer::SetLossAlgorithm(&manager_, loss_algorithm);
+ EXPECT_CALL(*loss_algorithm, GetLossTimeout())
+ .WillRepeatedly(Return(QuicTime::Zero()));
SendDataPacket(1);
- QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(500);
- EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
- .WillRepeatedly(Return(delay));
+ SendDataPacket(2);
- // No backoff for the first 5 retransmissions.
- for (int i = 0; i < 5; ++i) {
- EXPECT_EQ(delay, manager_.GetRetransmissionDelay());
- EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(i + 1, _));
- EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout());
- manager_.OnRetransmissionTimeout();
- RetransmitNextPacket(i + 2);
- }
+ // Handle an ack which causes the loss algorithm to be evaluated and
+ // set the loss timeout.
+ ExpectAck(2);
+ EXPECT_CALL(*loss_algorithm, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ ReceivedPacketInfo received_info;
+ received_info.largest_observed = 2;
+ received_info.missing_packets.insert(1);
+ manager_.OnIncomingAck(received_info, clock_.Now());
+
+ QuicTime timeout(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)));
+ EXPECT_CALL(*loss_algorithm, GetLossTimeout())
+ .WillRepeatedly(Return(timeout));
+ EXPECT_EQ(timeout, manager_.GetRetransmissionTime());
- // Then backoff starts
- EXPECT_EQ(delay.Add(delay), manager_.GetRetransmissionDelay());
+ // Fire the retransmission timeout and ensure the loss detection algorithm
+ // is invoked.
+ EXPECT_CALL(*loss_algorithm, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ manager_.OnRetransmissionTimeout();
+}
+
+TEST_F(QuicSentPacketManagerTest, NegotiateTimeLossDetection) {
+ EXPECT_EQ(kNack,
+ QuicSentPacketManagerPeer::GetLossAlgorithm(
+ &manager_)->GetLossDetectionType());
+
+ QuicConfig config;
+ QuicConfigPeer::SetReceivedLossDetection(&config, kTIME);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+
+ EXPECT_EQ(kTime,
+ QuicSentPacketManagerPeer::GetLossAlgorithm(
+ &manager_)->GetLossDetectionType());
}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/quic_server_id.cc b/chromium/net/quic/quic_server_id.cc
new file mode 100644
index 00000000000..ff870997df7
--- /dev/null
+++ b/chromium/net/quic/quic_server_id.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_server_id.h"
+
+using std::string;
+
+namespace net {
+
+QuicServerId::QuicServerId() {}
+
+QuicServerId::QuicServerId(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode)
+ : host_port_pair_(host_port_pair),
+ is_https_(is_https),
+ privacy_mode_(privacy_mode) {}
+
+QuicServerId::QuicServerId(const string& host,
+ uint16 port,
+ bool is_https)
+ : host_port_pair_(host, port),
+ is_https_(is_https),
+ privacy_mode_(PRIVACY_MODE_DISABLED) {}
+
+QuicServerId::QuicServerId(const string& host,
+ uint16 port,
+ bool is_https,
+ PrivacyMode privacy_mode)
+ : host_port_pair_(host, port),
+ is_https_(is_https),
+ privacy_mode_(privacy_mode) {}
+
+QuicServerId::~QuicServerId() {}
+
+bool QuicServerId::operator<(const QuicServerId& other) const {
+ if (!host_port_pair_.Equals(other.host_port_pair_)) {
+ return host_port_pair_ < other.host_port_pair_;
+ }
+ if (is_https_ != other.is_https_) {
+ return is_https_ < other.is_https_;
+ }
+ return privacy_mode_ < other.privacy_mode_;
+}
+
+bool QuicServerId::operator==(const QuicServerId& other) const {
+ return is_https_ == other.is_https_ &&
+ privacy_mode_ == other.privacy_mode_ &&
+ host_port_pair_.Equals(other.host_port_pair_);
+}
+
+string QuicServerId::ToString() const {
+ return (is_https_ ? "https://" : "http://") + host_port_pair_.ToString() +
+ (privacy_mode_ == PRIVACY_MODE_ENABLED ? "/private" : "");
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_server_id.h b/chromium/net/quic/quic_server_id.h
new file mode 100644
index 00000000000..6d016705cd8
--- /dev/null
+++ b/chromium/net/quic/quic_server_id.h
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SERVER_ID_H_
+#define NET_QUIC_QUIC_SERVER_ID_H_
+
+#include <string>
+
+#include "net/base/host_port_pair.h"
+#include "net/base/net_export.h"
+#include "net/base/privacy_mode.h"
+
+namespace net {
+
+// The id used to identify sessions. Includes the hostname, port, scheme and
+// privacy_mode.
+class NET_EXPORT_PRIVATE QuicServerId {
+ public:
+ QuicServerId();
+ QuicServerId(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode);
+ QuicServerId(const std::string& host,
+ uint16 port,
+ bool is_https);
+ QuicServerId(const std::string& host,
+ uint16 port,
+ bool is_https,
+ PrivacyMode privacy_mode);
+ ~QuicServerId();
+
+ // Needed to be an element of std::set.
+ bool operator<(const QuicServerId& other) const;
+ bool operator==(const QuicServerId& other) const;
+
+ // ToString() will convert the QuicServerId to "scheme:hostname:port" or
+ // "scheme:hostname:port/private". "scheme" would either be "http" or "https"
+ // based on |is_https|.
+ std::string ToString() const;
+
+ const HostPortPair& host_port_pair() const { return host_port_pair_; }
+
+ const std::string& host() const { return host_port_pair_.host(); }
+
+ uint16 port() const { return host_port_pair_.port(); }
+
+ bool is_https() const { return is_https_; }
+
+ PrivacyMode privacy_mode() const { return privacy_mode_; }
+
+ private:
+ HostPortPair host_port_pair_;
+ bool is_https_;
+ PrivacyMode privacy_mode_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SERVER_ID_H_
diff --git a/chromium/net/quic/quic_server_id_test.cc b/chromium/net/quic/quic_server_id_test.cc
new file mode 100644
index 00000000000..4a9c0f3cd80
--- /dev/null
+++ b/chromium/net/quic/quic_server_id_test.cc
@@ -0,0 +1,319 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_server_id.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+
+namespace {
+
+TEST(QuicServerIdTest, ToString) {
+ HostPortPair google_host_port_pair("google.com", 10);
+
+ QuicServerId google_http_server_id(google_host_port_pair, false,
+ PRIVACY_MODE_DISABLED);
+ string google_http_server_id_str = google_http_server_id.ToString();
+ EXPECT_EQ("http://google.com:10", google_http_server_id_str);
+
+ QuicServerId google_https_server_id(google_host_port_pair, true,
+ PRIVACY_MODE_DISABLED);
+ string google_https_server_id_str = google_https_server_id.ToString();
+ EXPECT_EQ("https://google.com:10", google_https_server_id_str);
+
+ QuicServerId private_http_server_id(google_host_port_pair, false,
+ PRIVACY_MODE_ENABLED);
+ string private_http_server_id_str = private_http_server_id.ToString();
+ EXPECT_EQ("http://google.com:10/private", private_http_server_id_str);
+
+ QuicServerId private_https_server_id(google_host_port_pair, true,
+ PRIVACY_MODE_ENABLED);
+ string private_https_server_id_str = private_https_server_id.ToString();
+ EXPECT_EQ("https://google.com:10/private", private_https_server_id_str);
+}
+
+TEST(QuicServerIdTest, LessThan) {
+ QuicServerId a_10_http(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId a_10_https(HostPortPair("a.com", 10), true,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId a_11_http(HostPortPair("a.com", 11), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId a_11_https(HostPortPair("a.com", 11), true,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_10_http(HostPortPair("b.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_10_https(HostPortPair("b.com", 10), true,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_11_http(HostPortPair("b.com", 11), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_11_https(HostPortPair("b.com", 11), true,
+ PRIVACY_MODE_DISABLED);
+
+ QuicServerId a_10_http_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId a_10_https_private(HostPortPair("a.com", 10), true,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId a_11_http_private(HostPortPair("a.com", 11), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId a_11_https_private(HostPortPair("a.com", 11), true,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_10_http_private(HostPortPair("b.com", 10), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_10_https_private(HostPortPair("b.com", 10), true,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_11_http_private(HostPortPair("b.com", 11), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_11_https_private(HostPortPair("b.com", 11), true,
+ PRIVACY_MODE_ENABLED);
+
+ // Test combinations of host, port, https and privacy being same on left and
+ // right side of less than.
+ EXPECT_FALSE(a_10_http < a_10_http);
+ EXPECT_TRUE(a_10_http < a_10_https);
+ EXPECT_FALSE(a_10_https < a_10_http);
+ EXPECT_FALSE(a_10_https < a_10_https);
+
+ EXPECT_TRUE(a_10_http < a_10_http_private);
+ EXPECT_TRUE(a_10_http < a_10_https_private);
+ EXPECT_FALSE(a_10_https < a_10_http_private);
+ EXPECT_TRUE(a_10_https < a_10_https_private);
+
+ EXPECT_FALSE(a_10_http_private < a_10_http);
+ EXPECT_TRUE(a_10_http_private < a_10_https);
+ EXPECT_FALSE(a_10_https_private < a_10_http);
+ EXPECT_FALSE(a_10_https_private < a_10_https);
+
+ EXPECT_FALSE(a_10_http_private < a_10_http_private);
+ EXPECT_TRUE(a_10_http_private < a_10_https_private);
+ EXPECT_FALSE(a_10_https_private < a_10_http_private);
+ EXPECT_FALSE(a_10_https_private < a_10_https_private);
+
+ // Test with either host, port or https being different on left and right side
+ // of less than.
+ PrivacyMode left_privacy;
+ PrivacyMode right_privacy;
+ for (int i = 0; i < 4; i++) {
+ switch (i) {
+ case 0:
+ left_privacy = PRIVACY_MODE_DISABLED;
+ right_privacy = PRIVACY_MODE_DISABLED;
+ break;
+ case 1:
+ left_privacy = PRIVACY_MODE_DISABLED;
+ right_privacy = PRIVACY_MODE_ENABLED;
+ break;
+ case 2:
+ left_privacy = PRIVACY_MODE_ENABLED;
+ right_privacy = PRIVACY_MODE_DISABLED;
+ break;
+ case 3:
+ left_privacy = PRIVACY_MODE_ENABLED;
+ right_privacy = PRIVACY_MODE_ENABLED;
+ break;
+ }
+ QuicServerId a_10_http_left_private(HostPortPair("a.com", 10), false,
+ left_privacy);
+ QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false,
+ right_privacy);
+ QuicServerId a_10_https_left_private(HostPortPair("a.com", 10), true,
+ left_privacy);
+ QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true,
+ right_privacy);
+ QuicServerId a_11_http_left_private(HostPortPair("a.com", 11), false,
+ left_privacy);
+ QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false,
+ right_privacy);
+ QuicServerId a_11_https_left_private(HostPortPair("a.com", 11), true,
+ left_privacy);
+ QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true,
+ right_privacy);
+
+ QuicServerId b_10_http_left_private(HostPortPair("b.com", 10), false,
+ left_privacy);
+ QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false,
+ right_privacy);
+ QuicServerId b_10_https_left_private(HostPortPair("b.com", 10), true,
+ left_privacy);
+ QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true,
+ right_privacy);
+ QuicServerId b_11_http_left_private(HostPortPair("b.com", 11), false,
+ left_privacy);
+ QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false,
+ right_privacy);
+ QuicServerId b_11_https_left_private(HostPortPair("b.com", 11), true,
+ left_privacy);
+ QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true,
+ right_privacy);
+
+ EXPECT_TRUE(a_10_http_left_private < a_11_http_right_private);
+ EXPECT_TRUE(a_10_http_left_private < a_11_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < a_11_http_right_private);
+ EXPECT_TRUE(a_10_https_left_private < a_11_https_right_private);
+
+ EXPECT_TRUE(a_10_http_left_private < b_10_http_right_private);
+ EXPECT_TRUE(a_10_http_left_private < b_10_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_10_http_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_10_https_right_private);
+
+ EXPECT_TRUE(a_10_http_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_10_http_left_private < b_11_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_11_https_right_private);
+
+ EXPECT_FALSE(a_11_http_left_private < a_10_http_right_private);
+ EXPECT_FALSE(a_11_http_left_private < a_10_https_right_private);
+ EXPECT_FALSE(a_11_https_left_private < a_10_http_right_private);
+ EXPECT_FALSE(a_11_https_left_private < a_10_https_right_private);
+
+ EXPECT_FALSE(a_11_http_left_private < b_10_http_right_private);
+ EXPECT_FALSE(a_11_http_left_private < b_10_https_right_private);
+ EXPECT_FALSE(a_11_https_left_private < b_10_http_right_private);
+ EXPECT_FALSE(a_11_https_left_private < b_10_https_right_private);
+
+ EXPECT_TRUE(a_11_http_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_11_http_left_private < b_11_https_right_private);
+ EXPECT_TRUE(a_11_https_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_11_https_left_private < b_11_https_right_private);
+
+ EXPECT_FALSE(b_10_http_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_10_http_left_private < a_10_https_right_private);
+ EXPECT_FALSE(b_10_https_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_10_https_left_private < a_10_https_right_private);
+
+ EXPECT_TRUE(b_10_http_left_private < a_11_http_right_private);
+ EXPECT_TRUE(b_10_http_left_private < a_11_https_right_private);
+ EXPECT_TRUE(b_10_https_left_private < a_11_http_right_private);
+ EXPECT_TRUE(b_10_https_left_private < a_11_https_right_private);
+
+ EXPECT_TRUE(b_10_http_left_private < b_11_http_right_private);
+ EXPECT_TRUE(b_10_http_left_private < b_11_https_right_private);
+ EXPECT_TRUE(b_10_https_left_private < b_11_http_right_private);
+ EXPECT_TRUE(b_10_https_left_private < b_11_https_right_private);
+
+ EXPECT_FALSE(b_11_http_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_11_http_left_private < a_10_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_10_https_right_private);
+
+ EXPECT_FALSE(b_11_http_left_private < a_11_http_right_private);
+ EXPECT_FALSE(b_11_http_left_private < a_11_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_11_http_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_11_https_right_private);
+
+ EXPECT_FALSE(b_11_http_left_private < b_10_http_right_private);
+ EXPECT_FALSE(b_11_http_left_private < b_10_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < b_10_http_right_private);
+ EXPECT_FALSE(b_11_https_left_private < b_10_https_right_private);
+ }
+}
+
+TEST(QuicServerIdTest, Equals) {
+ PrivacyMode left_privacy;
+ PrivacyMode right_privacy;
+ for (int i = 0; i < 2; i++) {
+ switch (i) {
+ case 0:
+ left_privacy = PRIVACY_MODE_DISABLED;
+ right_privacy = PRIVACY_MODE_DISABLED;
+ break;
+ case 1:
+ left_privacy = PRIVACY_MODE_ENABLED;
+ right_privacy = PRIVACY_MODE_ENABLED;
+ break;
+ }
+ QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false,
+ right_privacy);
+ QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true,
+ right_privacy);
+ QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false,
+ right_privacy);
+ QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true,
+ right_privacy);
+ QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false,
+ right_privacy);
+ QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true,
+ right_privacy);
+ QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false,
+ right_privacy);
+ QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true,
+ right_privacy);
+
+ QuicServerId new_a_10_http_left_private(HostPortPair("a.com", 10), false,
+ left_privacy);
+ QuicServerId new_a_10_https_left_private(HostPortPair("a.com", 10), true,
+ left_privacy);
+ QuicServerId new_a_11_http_left_private(HostPortPair("a.com", 11), false,
+ left_privacy);
+ QuicServerId new_a_11_https_left_private(HostPortPair("a.com", 11), true,
+ left_privacy);
+ QuicServerId new_b_10_http_left_private(HostPortPair("b.com", 10), false,
+ left_privacy);
+ QuicServerId new_b_10_https_left_private(HostPortPair("b.com", 10), true,
+ left_privacy);
+ QuicServerId new_b_11_http_left_private(HostPortPair("b.com", 11), false,
+ left_privacy);
+ QuicServerId new_b_11_https_left_private(HostPortPair("b.com", 11), true,
+ left_privacy);
+
+ EXPECT_EQ(new_a_10_http_left_private, a_10_http_right_private);
+ EXPECT_EQ(new_a_10_https_left_private, a_10_https_right_private);
+ EXPECT_EQ(new_a_11_http_left_private, a_11_http_right_private);
+ EXPECT_EQ(new_a_11_https_left_private, a_11_https_right_private);
+ EXPECT_EQ(new_b_10_http_left_private, b_10_http_right_private);
+ EXPECT_EQ(new_b_10_https_left_private, b_10_https_right_private);
+ EXPECT_EQ(new_b_11_http_left_private, b_11_http_right_private);
+ EXPECT_EQ(new_b_11_https_left_private, b_11_https_right_private);
+ }
+
+ for (int i = 0; i < 2; i++) {
+ switch (i) {
+ case 0:
+ right_privacy = PRIVACY_MODE_DISABLED;
+ break;
+ case 1:
+ right_privacy = PRIVACY_MODE_ENABLED;
+ break;
+ }
+ QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false,
+ right_privacy);
+ QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true,
+ right_privacy);
+ QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false,
+ right_privacy);
+ QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true,
+ right_privacy);
+ QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false,
+ right_privacy);
+ QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true,
+ right_privacy);
+ QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false,
+ right_privacy);
+ QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true,
+ right_privacy);
+
+ QuicServerId new_a_10_http_left_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+
+ EXPECT_FALSE(new_a_10_http_left_private == a_10_https_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == a_11_http_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_10_http_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == a_11_https_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_10_https_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_11_http_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_11_https_right_private);
+ }
+ QuicServerId a_10_http_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId new_a_10_http_no_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+ EXPECT_FALSE(new_a_10_http_no_private == a_10_http_private);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/quic/quic_server_packet_writer.cc b/chromium/net/quic/quic_server_packet_writer.cc
new file mode 100644
index 00000000000..c2635b699eb
--- /dev/null
+++ b/chromium/net/quic/quic_server_packet_writer.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_server_packet_writer.h"
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+QuicServerPacketWriter::QuicServerPacketWriter() : weak_factory_(this) {
+}
+
+QuicServerPacketWriter::QuicServerPacketWriter(UDPServerSocket* socket)
+ : weak_factory_(this),
+ socket_(socket),
+ write_blocked_(false) {
+}
+
+QuicServerPacketWriter::~QuicServerPacketWriter() {
+}
+
+WriteResult QuicServerPacketWriter::WritePacket(
+ const char* buffer, size_t buf_len,
+ const net::IPAddressNumber& self_address,
+ const net::IPEndPoint& peer_address) {
+ scoped_refptr<StringIOBuffer> buf(
+ new StringIOBuffer(std::string(buffer, buf_len)));
+ DCHECK(!IsWriteBlocked());
+ int rv = socket_->SendTo(buf.get(),
+ buf_len,
+ peer_address,
+ base::Bind(&QuicServerPacketWriter::OnWriteComplete,
+ weak_factory_.GetWeakPtr()));
+ WriteStatus status = WRITE_STATUS_OK;
+ if (rv < 0) {
+ if (rv != ERR_IO_PENDING) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.WriteError", -rv);
+ status = WRITE_STATUS_ERROR;
+ } else {
+ status = WRITE_STATUS_BLOCKED;
+ write_blocked_ = true;
+ }
+ }
+
+ return WriteResult(status, rv);
+}
+
+bool QuicServerPacketWriter::IsWriteBlockedDataBuffered() const {
+ // UDPServerSocket::SendTo buffers the data until the Write is permitted.
+ return true;
+}
+
+bool QuicServerPacketWriter::IsWriteBlocked() const {
+ return write_blocked_;
+}
+
+void QuicServerPacketWriter::SetWritable() {
+ write_blocked_ = false;
+}
+
+void QuicServerPacketWriter::OnWriteComplete(int rv) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ write_blocked_ = false;
+ WriteResult result(rv < 0 ? WRITE_STATUS_ERROR : WRITE_STATUS_OK, rv);
+ connection_->OnPacketSent(result);
+ connection_->OnCanWrite();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_server_packet_writer.h b/chromium/net/quic/quic_server_packet_writer.h
new file mode 100644
index 00000000000..63829935521
--- /dev/null
+++ b/chromium/net/quic/quic_server_packet_writer.h
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
+#define NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_packet_writer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/udp/udp_server_socket.h"
+
+namespace net {
+
+struct WriteResult;
+
+// Chrome specific packet writer which uses a UDPServerSocket for writing
+// data.
+class QuicServerPacketWriter : public QuicPacketWriter {
+ public:
+ QuicServerPacketWriter();
+ explicit QuicServerPacketWriter(UDPServerSocket* socket);
+ virtual ~QuicServerPacketWriter();
+
+ // QuicPacketWriter
+ virtual WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const net::IPAddressNumber& self_address,
+ const net::IPEndPoint& peer_address) OVERRIDE;
+ virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual bool IsWriteBlocked() const OVERRIDE;
+ virtual void SetWritable() OVERRIDE;
+
+ void OnWriteComplete(int rv);
+ void SetConnection(QuicConnection* connection) {
+ connection_ = connection;
+ }
+
+ private:
+ base::WeakPtrFactory<QuicServerPacketWriter> weak_factory_;
+ UDPServerSocket* socket_;
+ QuicConnection* connection_;
+ bool write_blocked_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerPacketWriter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
diff --git a/chromium/net/quic/quic_server_session.cc b/chromium/net/quic/quic_server_session.cc
new file mode 100644
index 00000000000..1de91a44aa1
--- /dev/null
+++ b/chromium/net/quic/quic_server_session.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_server_session.h"
+
+#include "base/logging.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_spdy_server_stream.h"
+#include "net/quic/reliable_quic_stream.h"
+
+namespace net {
+
+QuicServerSession::QuicServerSession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor)
+ : QuicSession(connection, config),
+ visitor_(visitor) {}
+
+QuicServerSession::~QuicServerSession() {}
+
+void QuicServerSession::InitializeSession(
+ const QuicCryptoServerConfig& crypto_config) {
+ crypto_stream_.reset(CreateQuicCryptoServerStream(crypto_config));
+}
+
+QuicCryptoServerStream* QuicServerSession::CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig& crypto_config) {
+ return new QuicCryptoServerStream(crypto_config, this);
+}
+
+void QuicServerSession::OnConnectionClosed(QuicErrorCode error,
+ bool from_peer) {
+ QuicSession::OnConnectionClosed(error, from_peer);
+ // In the unlikely event we get a connection close while doing an asynchronous
+ // crypto event, make sure we cancel the callback.
+ if (crypto_stream_.get() != NULL) {
+ crypto_stream_->CancelOutstandingCallbacks();
+ }
+ visitor_->OnConnectionClosed(connection()->connection_id(), error);
+}
+
+void QuicServerSession::OnWriteBlocked() {
+ QuicSession::OnWriteBlocked();
+ visitor_->OnWriteBlocked(connection());
+}
+
+bool QuicServerSession::ShouldCreateIncomingDataStream(QuicStreamId id) {
+ if (id % 2 == 0) {
+ DVLOG(1) << "Invalid incoming even stream_id:" << id;
+ connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
+ return false;
+ }
+ if (GetNumOpenStreams() >= get_max_open_streams()) {
+ DVLOG(1) << "Failed to create a new incoming stream with id:" << id
+ << " Already " << GetNumOpenStreams() << " open.";
+ connection()->SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS);
+ return false;
+ }
+ return true;
+}
+
+QuicDataStream* QuicServerSession::CreateIncomingDataStream(
+ QuicStreamId id) {
+ if (!ShouldCreateIncomingDataStream(id)) {
+ return NULL;
+ }
+
+ return new QuicSpdyServerStream(id, this);
+}
+
+QuicDataStream* QuicServerSession::CreateOutgoingDataStream() {
+ DLOG(ERROR) << "Server push not yet supported";
+ return NULL;
+}
+
+QuicCryptoServerStream* QuicServerSession::GetCryptoStream() {
+ return crypto_stream_.get();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_server_session.h b/chromium/net/quic/quic_server_session.h
new file mode 100644
index 00000000000..30ac08c80df
--- /dev/null
+++ b/chromium/net/quic/quic_server_session.h
@@ -0,0 +1,87 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A server specific QuicSession subclass.
+
+#ifndef NET_QUIC_QUIC_SERVER_SESSION_H_
+#define NET_QUIC_QUIC_SERVER_SESSION_H_
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/quic_crypto_server_stream.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_session.h"
+
+namespace net {
+
+class QuicBlockedWriterInterface;
+class QuicConfig;
+class QuicConnection;
+class QuicCryptoServerConfig;
+class ReliableQuicStream;
+
+namespace test {
+class QuicServerSessionPeer;
+} // namespace test
+
+// An interface from the session to the entity owning the session.
+// This lets the session notify its owner (the Dispatcher) when the connection
+// is closed or blocked.
+class QuicServerSessionVisitor {
+ public:
+ virtual ~QuicServerSessionVisitor() {}
+
+ virtual void OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) = 0;
+ virtual void OnWriteBlocked(QuicBlockedWriterInterface* writer) = 0;
+};
+
+class QuicServerSession : public QuicSession {
+ public:
+ QuicServerSession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor);
+
+ // Override the base class to notify the owner of the connection close.
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnWriteBlocked() OVERRIDE;
+
+ virtual ~QuicServerSession();
+
+ virtual void InitializeSession(const QuicCryptoServerConfig& crypto_config);
+
+ const QuicCryptoServerStream* crypto_stream() const {
+ return crypto_stream_.get();
+ }
+
+ protected:
+ // QuicSession methods:
+ virtual QuicDataStream* CreateIncomingDataStream(QuicStreamId id) OVERRIDE;
+ virtual QuicDataStream* CreateOutgoingDataStream() OVERRIDE;
+ virtual QuicCryptoServerStream* GetCryptoStream() OVERRIDE;
+
+ // If we should create an incoming stream, returns true. Otherwise
+ // does error handling, including communicating the error to the client and
+ // possibly closing the connection, and returns false.
+ virtual bool ShouldCreateIncomingDataStream(QuicStreamId id);
+
+ virtual QuicCryptoServerStream* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig& crypto_config);
+
+ private:
+ friend class test::QuicServerSessionPeer;
+
+ scoped_ptr<QuicCryptoServerStream> crypto_stream_;
+ QuicServerSessionVisitor* visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerSession);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SERVER_SESSION_H_
diff --git a/chromium/net/quic/quic_session.cc b/chromium/net/quic/quic_session.cc
index 67cdf54d1d1..4cd9a335718 100644
--- a/chromium/net/quic/quic_session.cc
+++ b/chromium/net/quic/quic_session.cc
@@ -7,6 +7,9 @@
#include "base/stl_util.h"
#include "net/quic/crypto/proof_verifier.h"
#include "net/quic/quic_connection.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_flow_controller.h"
+#include "net/quic/quic_headers_stream.h"
#include "net/ssl/ssl_info.h"
using base::StringPiece;
@@ -17,9 +20,6 @@ using std::vector;
namespace net {
-const size_t kMaxPrematurelyClosedStreamsTracked = 20;
-const size_t kMaxZombieStreams = 20;
-
#define ENDPOINT (is_server() ? "Server: " : " Client: ")
// We want to make sure we delete any closed streams in a safe manner.
@@ -33,10 +33,9 @@ class VisitorShim : public QuicConnectionVisitorInterface {
public:
explicit VisitorShim(QuicSession* session) : session_(session) {}
- virtual bool OnStreamFrames(const vector<QuicStreamFrame>& frames) OVERRIDE {
- bool accepted = session_->OnStreamFrames(frames);
+ virtual void OnStreamFrames(const vector<QuicStreamFrame>& frames) OVERRIDE {
+ session_->OnStreamFrames(frames);
session_->PostProcessAfterData();
- return accepted;
}
virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE {
session_->OnRstStream(frame);
@@ -48,10 +47,21 @@ class VisitorShim : public QuicConnectionVisitorInterface {
session_->PostProcessAfterData();
}
- virtual bool OnCanWrite() OVERRIDE {
- bool rc = session_->OnCanWrite();
+ virtual void OnWindowUpdateFrames(const vector<QuicWindowUpdateFrame>& frames)
+ OVERRIDE {
+ session_->OnWindowUpdateFrames(frames);
+ session_->PostProcessAfterData();
+ }
+
+ virtual void OnBlockedFrames(const vector<QuicBlockedFrame>& frames)
+ OVERRIDE {
+ session_->OnBlockedFrames(frames);
+ session_->PostProcessAfterData();
+ }
+
+ virtual void OnCanWrite() OVERRIDE {
+ session_->OnCanWrite();
session_->PostProcessAfterData();
- return rc;
}
virtual void OnSuccessfulVersionNegotiation(
@@ -59,26 +69,33 @@ class VisitorShim : public QuicConnectionVisitorInterface {
session_->OnSuccessfulVersionNegotiation(version);
}
- virtual void OnConfigNegotiated() OVERRIDE {
- session_->OnConfigNegotiated();
- }
-
- virtual void OnConnectionClosed(QuicErrorCode error,
- bool from_peer) OVERRIDE {
+ virtual void OnConnectionClosed(
+ QuicErrorCode error, bool from_peer) OVERRIDE {
session_->OnConnectionClosed(error, from_peer);
// The session will go away, so don't bother with cleanup.
}
+ virtual void OnWriteBlocked() OVERRIDE {
+ session_->OnWriteBlocked();
+ }
+
+ virtual bool WillingAndAbleToWrite() const OVERRIDE {
+ return session_->WillingAndAbleToWrite();
+ }
+
virtual bool HasPendingHandshake() const OVERRIDE {
return session_->HasPendingHandshake();
}
+ virtual bool HasOpenDataStreams() const OVERRIDE {
+ return session_->HasOpenDataStreams();
+ }
+
private:
QuicSession* session_;
};
-QuicSession::QuicSession(QuicConnection* connection,
- const QuicConfig& config)
+QuicSession::QuicSession(QuicConnection* connection, const QuicConfig& config)
: connection_(connection),
visitor_shim_(new VisitorShim(this)),
config_(config),
@@ -89,6 +106,17 @@ QuicSession::QuicSession(QuicConnection* connection,
goaway_received_(false),
goaway_sent_(false),
has_pending_handshake_(false) {
+ if (connection_->version() <= QUIC_VERSION_19) {
+ flow_controller_.reset(new QuicFlowController(
+ connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow,
+ config_.GetInitialFlowControlWindowToSend(),
+ config_.GetInitialFlowControlWindowToSend()));
+ } else {
+ flow_controller_.reset(new QuicFlowController(
+ connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow,
+ config_.GetInitialSessionFlowControlWindowToSend(),
+ config_.GetInitialSessionFlowControlWindowToSend()));
+ }
connection_->set_visitor(visitor_shim_.get());
connection_->SetFromConfig(config_);
@@ -96,70 +124,77 @@ QuicSession::QuicSession(QuicConnection* connection,
connection_->SetOverallConnectionTimeout(
config_.max_time_before_crypto_handshake());
}
+ headers_stream_.reset(new QuicHeadersStream(this));
+ if (!is_server()) {
+ // For version above QUIC v12, the headers stream is stream 3, so the
+ // next available local stream ID should be 5.
+ DCHECK_EQ(kHeadersStreamId, next_stream_id_);
+ next_stream_id_ += 2;
+ }
}
QuicSession::~QuicSession() {
STLDeleteElements(&closed_streams_);
STLDeleteValues(&stream_map_);
-}
-
-bool QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) {
- for (size_t i = 0; i < frames.size(); ++i) {
- // TODO(rch) deal with the error case of stream id 0
- if (IsClosedStream(frames[i].stream_id)) {
- // If we get additional frames for a stream where we didn't process
- // headers, it's highly likely our compression context will end up
- // permanently out of sync with the peer's, so we give up and close the
- // connection.
- if (ContainsKey(prematurely_closed_streams_, frames[i].stream_id)) {
- connection()->SendConnectionClose(
- QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
- return false;
- }
- continue;
- }
-
- ReliableQuicStream* stream = GetStream(frames[i].stream_id);
- if (stream == NULL) return false;
- if (!stream->WillAcceptStreamFrame(frames[i])) return false;
- // TODO(alyssar) check against existing connection address: if changed, make
- // sure we update the connection.
- }
+ DLOG_IF(WARNING,
+ locally_closed_streams_highest_offset_.size() > max_open_streams_)
+ << "Surprisingly high number of locally closed streams still waiting for "
+ "final byte offset: " << locally_closed_streams_highest_offset_.size();
+}
+void QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) {
for (size_t i = 0; i < frames.size(); ++i) {
- QuicStreamId stream_id = frames[i].stream_id;
+ // TODO(rch) deal with the error case of stream id 0.
+ const QuicStreamFrame& frame = frames[i];
+ QuicStreamId stream_id = frame.stream_id;
ReliableQuicStream* stream = GetStream(stream_id);
if (!stream) {
+ // The stream no longer exists, but we may still be interested in the
+ // final stream byte offset sent by the peer. A frame with a FIN can give
+ // us this offset.
+ if (frame.fin) {
+ QuicStreamOffset final_byte_offset =
+ frame.offset + frame.data.TotalBufferSize();
+ UpdateFlowControlOnFinalReceivedByteOffset(stream_id,
+ final_byte_offset);
+ }
+
continue;
}
stream->OnStreamFrame(frames[i]);
+ }
+}
- // If the stream is a data stream had been prematurely closed, and the
- // headers are now decompressed, then we are finally finished
- // with this stream.
- if (ContainsKey(zombie_streams_, stream_id) &&
- static_cast<QuicDataStream*>(stream)->headers_decompressed()) {
- CloseZombieStream(stream_id);
- }
+void QuicSession::OnStreamHeaders(QuicStreamId stream_id,
+ StringPiece headers_data) {
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
}
+ stream->OnStreamHeaders(headers_data);
+}
- while (!decompression_blocked_streams_.empty()) {
- QuicHeaderId header_id = decompression_blocked_streams_.begin()->first;
- if (header_id != decompressor_.current_header_id()) {
- break;
- }
- QuicStreamId stream_id = decompression_blocked_streams_.begin()->second;
- decompression_blocked_streams_.erase(header_id);
- QuicDataStream* stream = GetDataStream(stream_id);
- if (!stream) {
- connection()->SendConnectionClose(
- QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
- return false;
- }
- stream->OnDecompressorAvailable();
+void QuicSession::OnStreamHeadersPriority(QuicStreamId stream_id,
+ QuicPriority priority) {
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
}
- return true;
+ stream->OnStreamHeadersPriority(priority);
+}
+
+void QuicSession::OnStreamHeadersComplete(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len) {
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
+ }
+ stream->OnStreamHeadersComplete(fin, frame_len);
}
void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) {
@@ -169,25 +204,23 @@ void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) {
"Attempt to reset the crypto stream");
return;
}
+ if (frame.stream_id == kHeadersStreamId) {
+ connection()->SendConnectionCloseWithDetails(
+ QUIC_INVALID_STREAM_ID,
+ "Attempt to reset the headers stream");
+ return;
+ }
+
QuicDataStream* stream = GetDataStream(frame.stream_id);
if (!stream) {
+ // The RST frame contains the final byte offset for the stream: we can now
+ // update the connection level flow controller if needed.
+ UpdateFlowControlOnFinalReceivedByteOffset(frame.stream_id,
+ frame.byte_offset);
return; // Errors are handled by GetStream.
}
- if (ContainsKey(zombie_streams_, stream->id())) {
- // If this was a zombie stream then we close it out now.
- CloseZombieStream(stream->id());
- // However, since the headers still have not been decompressed, we want to
- // mark it a prematurely closed so that if we ever receive frames
- // for this stream we can close the connection.
- DCHECK(!stream->headers_decompressed());
- AddPrematurelyClosedStream(frame.stream_id);
- return;
- }
- if (stream->stream_bytes_read() > 0 && !stream->headers_decompressed()) {
- connection()->SendConnectionClose(
- QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
- }
- stream->OnStreamReset(frame.error_code);
+
+ stream->OnStreamReset(frame);
}
void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) {
@@ -214,60 +247,148 @@ void QuicSession::OnConnectionClosed(QuicErrorCode error, bool from_peer) {
}
}
-bool QuicSession::OnCanWrite() {
- // We latch this here rather than doing a traditional loop, because streams
- // may be modifying the list as we loop.
- int remaining_writes = write_blocked_streams_.NumBlockedStreams();
+void QuicSession::OnWindowUpdateFrames(
+ const vector<QuicWindowUpdateFrame>& frames) {
+ bool connection_window_updated = false;
+ for (size_t i = 0; i < frames.size(); ++i) {
+ // Stream may be closed by the time we receive a WINDOW_UPDATE, so we can't
+ // assume that it still exists.
+ QuicStreamId stream_id = frames[i].stream_id;
+ if (stream_id == 0) {
+ // This is a window update that applies to the connection, rather than an
+ // individual stream.
+ DVLOG(1) << ENDPOINT
+ << "Received connection level flow control window update with "
+ "byte offset: " << frames[i].byte_offset;
+ if (FLAGS_enable_quic_connection_flow_control_2 &&
+ flow_controller_->UpdateSendWindowOffset(frames[i].byte_offset)) {
+ connection_window_updated = true;
+ }
+ continue;
+ }
+
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (stream) {
+ stream->OnWindowUpdateFrame(frames[i]);
+ }
+ }
+
+ // Connection level flow control window has increased, so blocked streams can
+ // write again.
+ if (connection_window_updated) {
+ OnCanWrite();
+ }
+}
+
+void QuicSession::OnBlockedFrames(const vector<QuicBlockedFrame>& frames) {
+ for (size_t i = 0; i < frames.size(); ++i) {
+ // TODO(rjshade): Compare our flow control receive windows for specified
+ // streams: if we have a large window then maybe something
+ // had gone wrong with the flow control accounting.
+ DVLOG(1) << ENDPOINT << "Received BLOCKED frame with stream id: "
+ << frames[i].stream_id;
+ }
+}
+
+void QuicSession::OnCanWrite() {
+ // We limit the number of writes to the number of pending streams. If more
+ // streams become pending, WillingAndAbleToWrite will be true, which will
+ // cause the connection to request resumption before yielding to other
+ // connections.
+ size_t num_writes = write_blocked_streams_.NumBlockedStreams();
+ if (flow_controller_->IsBlocked()) {
+ // If we are connection level flow control blocked, then only allow the
+ // crypto and headers streams to try writing as all other streams will be
+ // blocked.
+ num_writes = 0;
+ if (write_blocked_streams_.crypto_stream_blocked()) {
+ num_writes += 1;
+ }
+ if (write_blocked_streams_.headers_stream_blocked()) {
+ num_writes += 1;
+ }
+ }
+ if (num_writes == 0) {
+ return;
+ }
- while (!connection_->HasQueuedData() &&
- remaining_writes > 0) {
- DCHECK(write_blocked_streams_.HasWriteBlockedStreams());
- if (!write_blocked_streams_.HasWriteBlockedStreams()) {
+ QuicConnection::ScopedPacketBundler ack_bundler(
+ connection_.get(), QuicConnection::NO_ACK);
+ for (size_t i = 0; i < num_writes; ++i) {
+ if (!(write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() ||
+ write_blocked_streams_.HasWriteBlockedDataStreams())) {
+ // Writing one stream removed another!? Something's broken.
LOG(DFATAL) << "WriteBlockedStream is missing";
connection_->CloseConnection(QUIC_INTERNAL_ERROR, false);
- return true; // We have no write blocked streams.
+ return;
+ }
+ if (!connection_->CanWriteStreamData()) {
+ return;
}
- int index = write_blocked_streams_.GetHighestPriorityWriteBlockedList();
- QuicStreamId stream_id = write_blocked_streams_.PopFront(index);
+ QuicStreamId stream_id = write_blocked_streams_.PopFront();
if (stream_id == kCryptoStreamId) {
has_pending_handshake_ = false; // We just popped it.
}
ReliableQuicStream* stream = GetStream(stream_id);
- if (stream != NULL) {
+ if (stream != NULL && !stream->flow_controller()->IsBlocked()) {
// If the stream can't write all bytes, it'll re-add itself to the blocked
// list.
stream->OnCanWrite();
}
- --remaining_writes;
}
+}
- return !write_blocked_streams_.HasWriteBlockedStreams();
+bool QuicSession::WillingAndAbleToWrite() const {
+ // If the crypto or headers streams are blocked, we want to schedule a write -
+ // they don't get blocked by connection level flow control. Otherwise only
+ // schedule a write if we are not flow control blocked at the connection
+ // level.
+ return write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() ||
+ (!flow_controller_->IsBlocked() &&
+ write_blocked_streams_.HasWriteBlockedDataStreams());
}
bool QuicSession::HasPendingHandshake() const {
return has_pending_handshake_;
}
+bool QuicSession::HasOpenDataStreams() const {
+ return GetNumOpenStreams() > 0;
+}
+
QuicConsumedData QuicSession::WritevData(
QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
+ const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
- IOVector data;
- data.AppendIovec(iov, iov_count);
- return connection_->SendStreamData(id, data, offset, fin,
+ return connection_->SendStreamData(id, data, offset, fin, fec_protection,
ack_notifier_delegate);
}
+size_t QuicSession::WriteHeaders(
+ QuicStreamId id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ return headers_stream_->WriteHeaders(id, headers, fin, ack_notifier_delegate);
+}
+
void QuicSession::SendRstStream(QuicStreamId id,
- QuicRstStreamErrorCode error) {
- connection_->SendRstStream(id, error);
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ if (connection()->connected()) {
+ // Only send a RST_STREAM frame if still connected.
+ connection_->SendRstStream(id, error, bytes_written);
+ }
CloseStreamInner(id, true);
}
void QuicSession::SendGoAway(QuicErrorCode error_code, const string& reason) {
+ if (goaway_sent_) {
+ return;
+ }
goaway_sent_ = true;
connection_->SendGoAway(error_code, largest_peer_created_stream_id_, reason);
}
@@ -286,63 +407,55 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id,
return;
}
QuicDataStream* stream = it->second;
- if (connection_->connected() && !stream->headers_decompressed()) {
- // If the stream is being closed locally (for example a client cancelling
- // a request before receiving the response) then we need to make sure that
- // we keep the stream alive long enough to process any response or
- // RST_STREAM frames.
- if (locally_reset && !is_server()) {
- AddZombieStream(stream_id);
- return;
- }
- // This stream has been closed before the headers were decompressed.
- // This might cause problems with head of line blocking of headers.
- // If the peer sent headers which were lost but we now close the stream
- // we will never be able to decompress headers for other streams.
- // To deal with this, we keep track of streams which have been closed
- // prematurely. If we ever receive data frames for this steam, then we
- // know there actually has been a problem and we close the connection.
- AddPrematurelyClosedStream(stream->id());
+ // Tell the stream that a RST has been sent.
+ if (locally_reset) {
+ stream->set_rst_sent(true);
}
+
closed_streams_.push_back(it->second);
- if (ContainsKey(zombie_streams_, stream->id())) {
- zombie_streams_.erase(stream->id());
+
+ // If we haven't received a FIN or RST for this stream, we need to keep track
+ // of the how many bytes the stream's flow controller believes it has
+ // received, for accurate connection level flow control accounting.
+ if (!stream->HasFinalReceivedByteOffset() &&
+ stream->flow_controller()->IsEnabled() &&
+ FLAGS_enable_quic_connection_flow_control_2) {
+ locally_closed_streams_highest_offset_[stream_id] =
+ stream->flow_controller()->highest_received_byte_offset();
}
+
stream_map_.erase(it);
stream->OnClose();
}
-void QuicSession::AddZombieStream(QuicStreamId stream_id) {
- if (zombie_streams_.size() == kMaxZombieStreams) {
- QuicStreamId oldest_zombie_stream_id = zombie_streams_.begin()->first;
- CloseZombieStream(oldest_zombie_stream_id);
- // However, since the headers still have not been decompressed, we want to
- // mark it a prematurely closed so that if we ever receive frames
- // for this stream we can close the connection.
- AddPrematurelyClosedStream(oldest_zombie_stream_id);
+void QuicSession::UpdateFlowControlOnFinalReceivedByteOffset(
+ QuicStreamId stream_id, QuicStreamOffset final_byte_offset) {
+ if (!FLAGS_enable_quic_connection_flow_control_2) {
+ return;
}
- zombie_streams_.insert(make_pair(stream_id, true));
-}
-void QuicSession::CloseZombieStream(QuicStreamId stream_id) {
- DCHECK(ContainsKey(zombie_streams_, stream_id));
- zombie_streams_.erase(stream_id);
- QuicDataStream* stream = GetDataStream(stream_id);
- if (!stream) {
+ map<QuicStreamId, QuicStreamOffset>::iterator it =
+ locally_closed_streams_highest_offset_.find(stream_id);
+ if (it == locally_closed_streams_highest_offset_.end()) {
return;
}
- stream_map_.erase(stream_id);
- stream->OnClose();
- closed_streams_.push_back(stream);
-}
-void QuicSession::AddPrematurelyClosedStream(QuicStreamId stream_id) {
- if (prematurely_closed_streams_.size() ==
- kMaxPrematurelyClosedStreamsTracked) {
- prematurely_closed_streams_.erase(prematurely_closed_streams_.begin());
+ DVLOG(1) << ENDPOINT << "Received final byte offset " << final_byte_offset
+ << " for stream " << stream_id;
+ uint64 offset_diff = final_byte_offset - it->second;
+ if (flow_controller_->UpdateHighestReceivedOffset(
+ flow_controller_->highest_received_byte_offset() + offset_diff)) {
+ // If the final offset violates flow control, close the connection now.
+ if (flow_controller_->FlowControlViolation()) {
+ connection_->SendConnectionClose(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA);
+ return;
+ }
}
- prematurely_closed_streams_.insert(make_pair(stream_id, true));
+
+ flow_controller_->AddBytesConsumed(offset_diff);
+ locally_closed_streams_highest_offset_.erase(it);
}
bool QuicSession::IsEncryptionEstablished() {
@@ -355,6 +468,68 @@ bool QuicSession::IsCryptoHandshakeConfirmed() {
void QuicSession::OnConfigNegotiated() {
connection_->SetFromConfig(config_);
+ QuicVersion version = connection()->version();
+ if (version < QUIC_VERSION_17) {
+ return;
+ }
+
+ if (version <= QUIC_VERSION_19) {
+ // QUIC_VERSION_17,18,19 don't support independent stream/session flow
+ // control windows.
+ if (config_.HasReceivedInitialFlowControlWindowBytes()) {
+ // Streams which were created before the SHLO was received (0-RTT
+ // requests) are now informed of the peer's initial flow control window.
+ uint32 new_window = config_.ReceivedInitialFlowControlWindowBytes();
+ OnNewStreamFlowControlWindow(new_window);
+ OnNewSessionFlowControlWindow(new_window);
+ }
+
+ return;
+ }
+
+ // QUIC_VERSION_20 and higher can have independent stream and session flow
+ // control windows.
+ if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) {
+ // Streams which were created before the SHLO was received (0-RTT
+ // requests) are now informed of the peer's initial flow control window.
+ OnNewStreamFlowControlWindow(
+ config_.ReceivedInitialStreamFlowControlWindowBytes());
+ }
+ if (config_.HasReceivedInitialSessionFlowControlWindowBytes()) {
+ OnNewSessionFlowControlWindow(
+ config_.ReceivedInitialSessionFlowControlWindowBytes());
+ }
+}
+
+void QuicSession::OnNewStreamFlowControlWindow(uint32 new_window) {
+ if (new_window < kDefaultFlowControlSendWindow) {
+ LOG(ERROR)
+ << "Peer sent us an invalid stream flow control send window: "
+ << new_window << ", below default: " << kDefaultFlowControlSendWindow;
+ if (connection_->connected()) {
+ connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+ }
+ return;
+ }
+
+ for (DataStreamMap::iterator it = stream_map_.begin();
+ it != stream_map_.end(); ++it) {
+ it->second->flow_controller()->UpdateSendWindowOffset(new_window);
+ }
+}
+
+void QuicSession::OnNewSessionFlowControlWindow(uint32 new_window) {
+ if (new_window < kDefaultFlowControlSendWindow) {
+ LOG(ERROR)
+ << "Peer sent us an invalid session flow control send window: "
+ << new_window << ", below default: " << kDefaultFlowControlSendWindow;
+ if (connection_->connected()) {
+ connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+ }
+ return;
+ }
+
+ flow_controller_->UpdateSendWindowOffset(new_window);
}
void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
@@ -373,6 +548,9 @@ void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
case HANDSHAKE_CONFIRMED:
LOG_IF(DFATAL, !config_.negotiated()) << ENDPOINT
<< "Handshake confirmed without parameter negotiation.";
+ // Discard originally encrypted packets, since they can't be decrypted by
+ // the peer.
+ connection_->NeuterUnencryptedPackets();
connection_->SetOverallConnectionTimeout(QuicTime::Delta::Infinite());
max_open_streams_ = config_.max_streams_per_connection();
break;
@@ -396,7 +574,7 @@ QuicConfig* QuicSession::config() {
void QuicSession::ActivateStream(QuicDataStream* stream) {
DVLOG(1) << ENDPOINT << "num_streams: " << stream_map_.size()
- << ". activating " << stream->id();
+ << ". activating " << stream->id();
DCHECK_EQ(stream_map_.count(stream->id()), 0u);
stream_map_[stream->id()] = stream;
}
@@ -411,6 +589,9 @@ ReliableQuicStream* QuicSession::GetStream(const QuicStreamId stream_id) {
if (stream_id == kCryptoStreamId) {
return GetCryptoStream();
}
+ if (stream_id == kHeadersStreamId) {
+ return headers_stream_.get();
+ }
return GetDataStream(stream_id);
}
@@ -419,6 +600,10 @@ QuicDataStream* QuicSession::GetDataStream(const QuicStreamId stream_id) {
DLOG(FATAL) << "Attempt to call GetDataStream with the crypto stream id";
return NULL;
}
+ if (stream_id == kHeadersStreamId) {
+ DLOG(FATAL) << "Attempt to call GetDataStream with the headers stream id";
+ return NULL;
+ }
DataStreamMap::iterator it = stream_map_.find(stream_id);
if (it != stream_map_.end()) {
@@ -432,34 +617,40 @@ QuicDataStream* QuicSession::GetDataStream(const QuicStreamId stream_id) {
if (stream_id % 2 == next_stream_id_ % 2) {
// We've received a frame for a locally-created stream that is not
// currently active. This is an error.
- connection()->SendConnectionClose(QUIC_PACKET_FOR_NONEXISTENT_STREAM);
+ if (connection()->connected()) {
+ connection()->SendConnectionClose(QUIC_PACKET_FOR_NONEXISTENT_STREAM);
+ }
return NULL;
}
- return GetIncomingReliableStream(stream_id);
+ return GetIncomingDataStream(stream_id);
}
-QuicDataStream* QuicSession::GetIncomingReliableStream(
- QuicStreamId stream_id) {
+QuicDataStream* QuicSession::GetIncomingDataStream(QuicStreamId stream_id) {
if (IsClosedStream(stream_id)) {
return NULL;
}
- if (goaway_sent_) {
- // We've already sent a GoAway
- SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY);
- return NULL;
- }
-
implicitly_created_streams_.erase(stream_id);
if (stream_id > largest_peer_created_stream_id_) {
- // TODO(rch) add unit test for this
if (stream_id - largest_peer_created_stream_id_ > kMaxStreamIdDelta) {
- connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
+ // We may already have sent a connection close due to multiple reset
+ // streams in the same packet.
+ if (connection()->connected()) {
+ LOG(ERROR) << "Trying to get stream: " << stream_id
+ << ", largest peer created stream: "
+ << largest_peer_created_stream_id_
+ << ", max delta: " << kMaxStreamIdDelta;
+ connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
+ }
return NULL;
}
if (largest_peer_created_stream_id_ == 0) {
- largest_peer_created_stream_id_= 1;
+ if (is_server()) {
+ largest_peer_created_stream_id_= 3;
+ } else {
+ largest_peer_created_stream_id_= 1;
+ }
}
for (QuicStreamId id = largest_peer_created_stream_id_ + 2;
id < stream_id;
@@ -481,8 +672,8 @@ bool QuicSession::IsClosedStream(QuicStreamId id) {
if (id == kCryptoStreamId) {
return false;
}
- if (ContainsKey(zombie_streams_, id)) {
- return true;
+ if (id == kHeadersStreamId) {
+ return false;
}
if (ContainsKey(stream_map_, id)) {
// Stream is active
@@ -500,11 +691,22 @@ bool QuicSession::IsClosedStream(QuicStreamId id) {
}
size_t QuicSession::GetNumOpenStreams() const {
- return stream_map_.size() + implicitly_created_streams_.size() -
- zombie_streams_.size();
+ return stream_map_.size() + implicitly_created_streams_.size();
}
void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) {
+#ifndef NDEBUG
+ ReliableQuicStream* stream = GetStream(id);
+ if (stream != NULL) {
+ LOG_IF(DFATAL, priority != stream->EffectivePriority())
+ << ENDPOINT << "Stream " << id
+ << "Priorities do not match. Got: " << priority
+ << " Expected: " << stream->EffectivePriority();
+ } else {
+ LOG(DFATAL) << "Marking unknown stream " << id << " blocked.";
+ }
+#endif
+
if (id == kCryptoStreamId) {
DCHECK(!has_pending_handshake_);
has_pending_handshake_ = true;
@@ -516,17 +718,13 @@ void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) {
write_blocked_streams_.PushBack(id, priority);
}
-bool QuicSession::HasQueuedData() const {
- return write_blocked_streams_.NumBlockedStreams() ||
- connection_->HasQueuedData();
-}
-
-void QuicSession::MarkDecompressionBlocked(QuicHeaderId header_id,
- QuicStreamId stream_id) {
- decompression_blocked_streams_[header_id] = stream_id;
+bool QuicSession::HasDataToWrite() const {
+ return write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() ||
+ write_blocked_streams_.HasWriteBlockedDataStreams() ||
+ connection_->HasQueuedData();
}
-bool QuicSession::GetSSLInfo(SSLInfo* ssl_info) {
+bool QuicSession::GetSSLInfo(SSLInfo* ssl_info) const {
NOTIMPLEMENTED();
return false;
}
@@ -536,4 +734,19 @@ void QuicSession::PostProcessAfterData() {
closed_streams_.clear();
}
+void QuicSession::OnSuccessfulVersionNegotiation(const QuicVersion& version) {
+ if (version < QUIC_VERSION_19) {
+ flow_controller_->Disable();
+ }
+
+ // Inform all streams about the negotiated version. They may have been created
+ // with a different version.
+ for (DataStreamMap::iterator it = stream_map_.begin();
+ it != stream_map_.end(); ++it) {
+ if (version < QUIC_VERSION_17) {
+ it->second->flow_controller()->Disable();
+ }
+ }
+}
+
} // namespace net
diff --git a/chromium/net/quic/quic_session.h b/chromium/net/quic/quic_session.h
index 083990b55f6..1b31280020c 100644
--- a/chromium/net/quic/quic_session.h
+++ b/chromium/net/quic/quic_session.h
@@ -16,16 +16,16 @@
#include "net/quic/quic_connection.h"
#include "net/quic/quic_crypto_stream.h"
#include "net/quic/quic_data_stream.h"
+#include "net/quic/quic_headers_stream.h"
#include "net/quic/quic_packet_creator.h"
#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
+#include "net/quic/quic_write_blocked_list.h"
#include "net/quic/reliable_quic_stream.h"
-#include "net/spdy/write_blocked_list.h"
namespace net {
class QuicCryptoStream;
+class QuicFlowController;
class ReliableQuicStream;
class SSLInfo;
class VisitorShim;
@@ -53,42 +53,75 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
HANDSHAKE_CONFIRMED,
};
- QuicSession(QuicConnection* connection,
- const QuicConfig& config);
+ QuicSession(QuicConnection* connection, const QuicConfig& config);
virtual ~QuicSession();
// QuicConnectionVisitorInterface methods:
- virtual bool OnStreamFrames(
+ virtual void OnStreamFrames(
const std::vector<QuicStreamFrame>& frames) OVERRIDE;
virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE;
virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual void OnWindowUpdateFrames(
+ const std::vector<QuicWindowUpdateFrame>& frames) OVERRIDE;
+ virtual void OnBlockedFrames(
+ const std::vector<QuicBlockedFrame>& frames) OVERRIDE;
virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnWriteBlocked() OVERRIDE {}
virtual void OnSuccessfulVersionNegotiation(
- const QuicVersion& version) OVERRIDE {}
- virtual void OnConfigNegotiated() OVERRIDE;
- // Not needed for HTTP.
- virtual bool OnCanWrite() OVERRIDE;
+ const QuicVersion& version) OVERRIDE;
+ virtual void OnCanWrite() OVERRIDE;
+ virtual bool WillingAndAbleToWrite() const OVERRIDE;
virtual bool HasPendingHandshake() const OVERRIDE;
+ virtual bool HasOpenDataStreams() const OVERRIDE;
+
+ // Called by the headers stream when headers have been received for a stream.
+ virtual void OnStreamHeaders(QuicStreamId stream_id,
+ base::StringPiece headers_data);
+ // Called by the headers stream when headers with a priority have been
+ // received for this stream. This method will only be called for server
+ // streams.
+ virtual void OnStreamHeadersPriority(QuicStreamId stream_id,
+ QuicPriority priority);
+ // Called by the headers stream when headers have been completely received
+ // for a stream. |fin| will be true if the fin flag was set in the headers
+ // frame.
+ virtual void OnStreamHeadersComplete(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len);
// Called by streams when they want to write data to the peer.
// Returns a pair with the number of bytes consumed from data, and a boolean
// indicating if the fin bit was consumed. This does not indicate the data
// has been sent on the wire: it may have been turned into a packet and queued
- // if the socket was unexpectedly blocked.
+ // if the socket was unexpectedly blocked. |fec_protection| indicates if
+ // data is to be FEC protected. Note that data that is sent immediately
+ // following MUST_FEC_PROTECT data may get protected by falling within the
+ // same FEC group.
// If provided, |ack_notifier_delegate| will be registered to be notified when
- // we have seen ACKs for all packets resulting from this call. Not owned by
- // this class.
+ // we have seen ACKs for all packets resulting from this call.
virtual QuicConsumedData WritevData(
QuicStreamId id,
- const struct iovec* iov,
- int iov_count,
+ const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
+ // Writes |headers| for the stream |id| to the dedicated headers stream.
+ // If |fin| is true, then no more data will be sent for the stream |id|.
+ // If provided, |ack_notifier_delegate| will be registered to be notified when
+ // we have seen ACKs for all packets resulting from this call.
+ size_t WriteHeaders(
+ QuicStreamId id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
// Called by streams when they want to close the stream in both directions.
- virtual void SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error);
+ virtual void SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written);
// Called when the session wants to go away and not accept any new streams.
void SendGoAway(QuicErrorCode error_code, const std::string& reason);
@@ -104,6 +137,9 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// a server, returns true if a full, valid client hello has been received.
virtual bool IsCryptoHandshakeConfirmed();
+ // Called by the QuicCryptoStream when a new QuicConfig has been negotiated.
+ virtual void OnConfigNegotiated();
+
// Called by the QuicCryptoStream when the handshake enters a new state.
//
// Clients will call this function in the order:
@@ -137,24 +173,20 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
const IPEndPoint& peer_address() const {
return connection_->peer_address();
}
- QuicGuid guid() const { return connection_->guid(); }
-
- QuicPacketCreator::Options* options() { return connection()->options(); }
+ QuicConnectionId connection_id() const {
+ return connection_->connection_id();
+ }
// Returns the number of currently open streams, including those which have
- // been implicitly created.
+ // been implicitly created, but excluding the reserved headers and crypto
+ // streams.
virtual size_t GetNumOpenStreams() const;
void MarkWriteBlocked(QuicStreamId id, QuicPriority priority);
// Returns true if the session has data to be sent, either queued in the
// connection, or in a write-blocked stream.
- bool HasQueuedData() const;
-
- // Marks that |stream_id| is blocked waiting to decompress the
- // headers identified by |decompression_id|.
- void MarkDecompressionBlocked(QuicHeaderId decompression_id,
- QuicStreamId stream_id);
+ bool HasDataToWrite() const;
bool goaway_received() const {
return goaway_received_;
@@ -164,16 +196,15 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
return goaway_sent_;
}
- QuicSpdyDecompressor* decompressor() { return &decompressor_; }
- QuicSpdyCompressor* compressor() { return &compressor_; }
-
// Gets the SSL connection information.
- virtual bool GetSSLInfo(SSLInfo* ssl_info);
+ virtual bool GetSSLInfo(SSLInfo* ssl_info) const;
QuicErrorCode error() const { return error_; }
bool is_server() const { return connection_->is_server(); }
+ QuicFlowController* flow_controller() { return flow_controller_.get(); }
+
protected:
typedef base::hash_map<QuicStreamId, QuicDataStream*> DataStreamMap;
@@ -195,7 +226,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// Returns the stream id for a new stream.
QuicStreamId GetNextStreamId();
- QuicDataStream* GetIncomingReliableStream(QuicStreamId stream_id);
+ QuicDataStream* GetIncomingDataStream(QuicStreamId stream_id);
QuicDataStream* GetDataStream(const QuicStreamId stream_id);
@@ -226,35 +257,32 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
friend class VisitorShim;
// Performs the work required to close |stream_id|. If |locally_reset|
- // then the stream has been reset by this endpoint, not by the peer. This
- // means the stream may become a zombie stream which needs to stay
- // around until headers have been decompressed.
+ // then the stream has been reset by this endpoint, not by the peer.
void CloseStreamInner(QuicStreamId stream_id, bool locally_reset);
- // Adds |stream_id| to the zobmie stream map, closing the oldest
- // zombie stream if the set is full.
- void AddZombieStream(QuicStreamId stream_id);
+ // When a stream is closed locally, it may not yet know how many bytes the
+ // peer sent on that stream.
+ // When this data arrives (via stream frame w. FIN, or RST) this method
+ // is called, and correctly updates the connection level flow controller.
+ void UpdateFlowControlOnFinalReceivedByteOffset(
+ QuicStreamId id, QuicStreamOffset final_byte_offset);
- // Closes the zombie stream |stream_id| and removes it from the zombie
- // stream map.
- void CloseZombieStream(QuicStreamId stream_id);
+ // Called in OnConfigNegotiated when we receive a new stream level flow
+ // control window in a negotiated config. Closes the connection if invalid.
+ void OnNewStreamFlowControlWindow(uint32 new_window);
- // Adds |stream_id| to the prematurely closed stream map, removing the
- // oldest prematurely closed stream if the set is full.
- void AddPrematurelyClosedStream(QuicStreamId stream_id);
+ // Called in OnConfigNegotiated when we receive a new session level flow
+ // control window in a negotiated config. Closes the connection if invalid.
+ void OnNewSessionFlowControlWindow(uint32 new_window);
- scoped_ptr<QuicConnection> connection_;
+ // Keep track of highest received byte offset of locally closed streams, while
+ // waiting for a definitive final highest offset from the peer.
+ std::map<QuicStreamId, QuicStreamOffset>
+ locally_closed_streams_highest_offset_;
- // Tracks the last 20 streams which closed without decompressing headers.
- // This is for best-effort detection of an unrecoverable compression context.
- // Ideally this would be a linked_hash_set as the boolean is unused.
- linked_hash_map<QuicStreamId, bool> prematurely_closed_streams_;
+ scoped_ptr<QuicConnection> connection_;
- // Streams which have been locally reset before decompressing headers
- // from the peer. These streams need to stay open long enough to
- // process any headers from the peer.
- // Ideally this would be a linked_hash_set as the boolean is unused.
- linked_hash_map<QuicStreamId, bool> zombie_streams_;
+ scoped_ptr<QuicHeadersStream> headers_stream_;
// A shim to stand between the connection and the session, to handle stream
// deletions.
@@ -262,9 +290,6 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
std::vector<QuicDataStream*> closed_streams_;
- QuicSpdyDecompressor decompressor_;
- QuicSpdyCompressor compressor_;
-
QuicConfig config_;
// Returns the maximum number of streams this connection can open.
@@ -279,11 +304,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
base::hash_set<QuicStreamId> implicitly_created_streams_;
// A list of streams which need to write more data.
- WriteBlockedList<QuicStreamId> write_blocked_streams_;
-
- // A map of headers waiting to be compressed, and the streams
- // they are associated with.
- map<uint32, QuicStreamId> decompression_blocked_streams_;
+ QuicWriteBlockedList write_blocked_streams_;
QuicStreamId largest_peer_created_stream_id_;
@@ -298,6 +319,9 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// Indicate if there is pending data for the crypto stream.
bool has_pending_handshake_;
+ // Used for session level flow control.
+ scoped_ptr<QuicFlowController> flow_controller_;
+
DISALLOW_COPY_AND_ASSIGN(QuicSession);
};
diff --git a/chromium/net/quic/quic_session_test.cc b/chromium/net/quic/quic_session_test.cc
index ecbafb5c6a2..aeb41037bee 100644
--- a/chromium/net/quic/quic_session_test.cc
+++ b/chromium/net/quic/quic_session_test.cc
@@ -7,31 +7,43 @@
#include <set>
#include <vector>
+#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
-#include "net/quic/crypto/crypto_handshake.h"
-#include "net/quic/quic_connection.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/quic/test_tools/quic_config_peer.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_data_stream_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/reliable_quic_stream_peer.h"
#include "net/spdy/spdy_framer.h"
+#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock_mutant.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::hash_map;
using std::set;
using std::vector;
-using testing::_;
+using testing::CreateFunctor;
using testing::InSequence;
-using testing::InvokeWithoutArgs;
+using testing::Invoke;
+using testing::Return;
using testing::StrictMock;
+using testing::_;
namespace net {
namespace test {
namespace {
-const QuicPriority kSomeMiddlePriority = 2;
+const QuicPriority kHighestPriority = 0;
+const QuicPriority kSomeMiddlePriority = 3;
class TestCryptoStream : public QuicCryptoStream {
public:
@@ -45,9 +57,15 @@ class TestCryptoStream : public QuicCryptoStream {
handshake_confirmed_ = true;
CryptoHandshakeMessage msg;
string error_details;
+ session()->config()->SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session()->config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session()->config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
session()->config()->ToHandshakeMessage(&msg);
- const QuicErrorCode error = session()->config()->ProcessClientHello(
- msg, &error_details);
+ const QuicErrorCode error = session()->config()->ProcessPeerHello(
+ msg, CLIENT, &error_details);
EXPECT_EQ(QUIC_NO_ERROR, error);
session()->OnConfigNegotiated();
session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
@@ -56,6 +74,15 @@ class TestCryptoStream : public QuicCryptoStream {
MOCK_METHOD0(OnCanWrite, void());
};
+class TestHeadersStream : public QuicHeadersStream {
+ public:
+ explicit TestHeadersStream(QuicSession* session)
+ : QuicHeadersStream(session) {
+ }
+
+ MOCK_METHOD0(OnCanWrite, void());
+};
+
class TestStream : public QuicDataStream {
public:
TestStream(QuicStreamId id, QuicSession* session)
@@ -64,10 +91,14 @@ class TestStream : public QuicDataStream {
using ReliableQuicStream::CloseWriteSide;
- virtual uint32 ProcessData(const char* data, uint32 data_len) {
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE {
return data_len;
}
+ void SendBody(const string& data, bool fin) {
+ WriteOrBufferData(data, fin, NULL);
+ }
+
MOCK_METHOD0(OnCanWrite, void());
};
@@ -91,9 +122,10 @@ class StreamBlocker {
class TestSession : public QuicSession {
public:
explicit TestSession(QuicConnection* connection)
- : QuicSession(connection, DefaultQuicConfig()),
- crypto_stream_(this) {
- }
+ : QuicSession(connection,
+ DefaultQuicConfig()),
+ crypto_stream_(this),
+ writev_consumes_all_data_(false) {}
virtual TestCryptoStream* GetCryptoStream() OVERRIDE {
return &crypto_stream_;
@@ -113,18 +145,53 @@ class TestSession : public QuicSession {
return QuicSession::IsClosedStream(id);
}
- QuicDataStream* GetIncomingReliableStream(QuicStreamId stream_id) {
- return QuicSession::GetIncomingReliableStream(stream_id);
+ QuicDataStream* GetIncomingDataStream(QuicStreamId stream_id) {
+ return QuicSession::GetIncomingDataStream(stream_id);
}
- TestCryptoStream crypto_stream_;
+ virtual QuicConsumedData WritevData(
+ QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) OVERRIDE {
+ // Always consumes everything.
+ if (writev_consumes_all_data_) {
+ return QuicConsumedData(data.TotalBufferSize(), fin);
+ } else {
+ return QuicSession::WritevData(id, data, offset, fin, fec_protection,
+ ack_notifier_delegate);
+ }
+ }
+
+ void set_writev_consumes_all_data(bool val) {
+ writev_consumes_all_data_ = val;
+ }
+
+ QuicConsumedData SendStreamData(QuicStreamId id) {
+ return WritevData(id, IOVector(), 0, true, MAY_FEC_PROTECT, NULL);
+ }
+
+ using QuicSession::PostProcessAfterData;
+
+ private:
+ StrictMock<TestCryptoStream> crypto_stream_;
+
+ bool writev_consumes_all_data_;
};
-class QuicSessionTest : public ::testing::Test {
+class QuicSessionTest : public ::testing::TestWithParam<QuicVersion> {
protected:
QuicSessionTest()
- : connection_(new MockConnection(true)),
+ : connection_(new MockConnection(true, SupportedVersions(GetParam()))),
session_(connection_) {
+ session_.config()->SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session_.config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session_.config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
headers_[":host"] = "www.google.com";
headers_[":path"] = "/index.hml";
headers_[":scheme"] = "http";
@@ -169,46 +236,49 @@ class QuicSessionTest : public ::testing::Test {
closed_streams_.insert(id);
}
+ QuicVersion version() const { return connection_->version(); }
+
MockConnection* connection_;
TestSession session_;
set<QuicStreamId> closed_streams_;
SpdyHeaderBlock headers_;
};
-TEST_F(QuicSessionTest, PeerAddress) {
+INSTANTIATE_TEST_CASE_P(Tests, QuicSessionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicSessionTest, PeerAddress) {
EXPECT_EQ(IPEndPoint(Loopback4(), kTestPort), session_.peer_address());
}
-TEST_F(QuicSessionTest, IsCryptoHandshakeConfirmed) {
+TEST_P(QuicSessionTest, IsCryptoHandshakeConfirmed) {
EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed());
CryptoHandshakeMessage message;
- session_.crypto_stream_.OnHandshakeMessage(message);
+ session_.GetCryptoStream()->OnHandshakeMessage(message);
EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed());
}
-TEST_F(QuicSessionTest, IsClosedStreamDefault) {
+TEST_P(QuicSessionTest, IsClosedStreamDefault) {
// Ensure that no streams are initially closed.
for (int i = kCryptoStreamId; i < 100; i++) {
- EXPECT_FALSE(session_.IsClosedStream(i));
+ EXPECT_FALSE(session_.IsClosedStream(i)) << "stream id: " << i;
}
}
-TEST_F(QuicSessionTest, ImplicitlyCreatedStreams) {
- ASSERT_TRUE(session_.GetIncomingReliableStream(7) != NULL);
+TEST_P(QuicSessionTest, ImplicitlyCreatedStreams) {
+ ASSERT_TRUE(session_.GetIncomingDataStream(7) != NULL);
// Both 3 and 5 should be implicitly created.
EXPECT_FALSE(session_.IsClosedStream(3));
EXPECT_FALSE(session_.IsClosedStream(5));
- ASSERT_TRUE(session_.GetIncomingReliableStream(5) != NULL);
- ASSERT_TRUE(session_.GetIncomingReliableStream(3) != NULL);
+ ASSERT_TRUE(session_.GetIncomingDataStream(5) != NULL);
+ ASSERT_TRUE(session_.GetIncomingDataStream(3) != NULL);
}
-TEST_F(QuicSessionTest, IsClosedStreamLocallyCreated) {
+TEST_P(QuicSessionTest, IsClosedStreamLocallyCreated) {
TestStream* stream2 = session_.CreateOutgoingDataStream();
EXPECT_EQ(2u, stream2->id());
- QuicDataStreamPeer::SetHeadersDecompressed(stream2, true);
TestStream* stream4 = session_.CreateOutgoingDataStream();
EXPECT_EQ(4u, stream4->id());
- QuicDataStreamPeer::SetHeadersDecompressed(stream4, true);
CheckClosedStreams();
CloseStream(4);
@@ -217,43 +287,78 @@ TEST_F(QuicSessionTest, IsClosedStreamLocallyCreated) {
CheckClosedStreams();
}
-TEST_F(QuicSessionTest, IsClosedStreamPeerCreated) {
- QuicDataStream* stream3 = session_.GetIncomingReliableStream(3);
- QuicDataStreamPeer::SetHeadersDecompressed(stream3, true);
- QuicDataStream* stream5 = session_.GetIncomingReliableStream(5);
- QuicDataStreamPeer::SetHeadersDecompressed(stream5, true);
+TEST_P(QuicSessionTest, IsClosedStreamPeerCreated) {
+ QuicStreamId stream_id1 = kClientDataStreamId1;
+ QuicStreamId stream_id2 = kClientDataStreamId2;
+ QuicDataStream* stream1 = session_.GetIncomingDataStream(stream_id1);
+ QuicDataStreamPeer::SetHeadersDecompressed(stream1, true);
+ QuicDataStream* stream2 = session_.GetIncomingDataStream(stream_id2);
+ QuicDataStreamPeer::SetHeadersDecompressed(stream2, true);
CheckClosedStreams();
- CloseStream(3);
+ CloseStream(stream_id1);
CheckClosedStreams();
- CloseStream(5);
- // Create stream id 9, and implicitly 7
- QuicDataStream* stream9 = session_.GetIncomingReliableStream(9);
- QuicDataStreamPeer::SetHeadersDecompressed(stream9, true);
+ CloseStream(stream_id2);
+ // Create a stream explicitly, and another implicitly.
+ QuicDataStream* stream3 = session_.GetIncomingDataStream(stream_id2 + 4);
+ QuicDataStreamPeer::SetHeadersDecompressed(stream3, true);
CheckClosedStreams();
- // Close 9, but make sure 7 is still not closed
- CloseStream(9);
+ // Close one, but make sure the other is still not closed
+ CloseStream(stream3->id());
CheckClosedStreams();
}
-TEST_F(QuicSessionTest, StreamIdTooLarge) {
- session_.GetIncomingReliableStream(3);
+TEST_P(QuicSessionTest, StreamIdTooLarge) {
+ QuicStreamId stream_id = kClientDataStreamId1;
+ session_.GetIncomingDataStream(stream_id);
EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID));
- session_.GetIncomingReliableStream(105);
+ session_.GetIncomingDataStream(stream_id + kMaxStreamIdDelta + 2);
+}
+
+TEST_P(QuicSessionTest, DecompressionError) {
+ QuicHeadersStream* stream = QuicSessionPeer::GetHeadersStream(&session_);
+ const unsigned char data[] = {
+ 0x80, 0x03, 0x00, 0x01, // SPDY/3 SYN_STREAM frame
+ 0x00, 0x00, 0x00, 0x25, // flags/length
+ 0x00, 0x00, 0x00, 0x05, // stream id
+ 0x00, 0x00, 0x00, 0x00, // associated stream id
+ 0x00, 0x00,
+ 'a', 'b', 'c', 'd' // invalid compressed data
+ };
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY framing error."));
+ stream->ProcessRawData(reinterpret_cast<const char*>(data),
+ arraysize(data));
}
-TEST_F(QuicSessionTest, DecompressionError) {
- ReliableQuicStream* stream = session_.GetIncomingReliableStream(3);
- EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE));
- const char data[] =
- "\0\0\0\0" // priority
- "\1\0\0\0" // headers id
- "\0\0\0\4" // length
- "abcd"; // invalid compressed data
- stream->ProcessRawData(data, arraysize(data));
+TEST_P(QuicSessionTest, DebugDFatalIfMarkingClosedStreamWriteBlocked) {
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ // Close the stream.
+ stream2->Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ // TODO(rtenneti): enable when chromium supports EXPECT_DEBUG_DFATAL.
+ /*
+ QuicStreamId kClosedStreamId = stream2->id();
+ EXPECT_DEBUG_DFATAL(
+ session_.MarkWriteBlocked(kClosedStreamId, kSomeMiddlePriority),
+ "Marking unknown stream 2 blocked.");
+ */
}
-TEST_F(QuicSessionTest, OnCanWrite) {
+TEST_P(QuicSessionTest, DebugDFatalIfMarkWriteBlockedCalledWithWrongPriority) {
+ const QuicPriority kDifferentPriority = 0;
+
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ EXPECT_NE(kDifferentPriority, stream2->EffectivePriority());
+ // TODO(rtenneti): enable when chromium supports EXPECT_DEBUG_DFATAL.
+ /*
+ EXPECT_DEBUG_DFATAL(
+ session_.MarkWriteBlocked(stream2->id(), kDifferentPriority),
+ "Priorities do not match. Got: 0 Expected: 3");
+ */
+}
+
+TEST_P(QuicSessionTest, OnCanWrite) {
TestStream* stream2 = session_.CreateOutgoingDataStream();
TestStream* stream4 = session_.CreateOutgoingDataStream();
TestStream* stream6 = session_.CreateOutgoingDataStream();
@@ -264,16 +369,99 @@ TEST_F(QuicSessionTest, OnCanWrite) {
InSequence s;
StreamBlocker stream2_blocker(&session_, stream2->id());
- EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(
- // Reregister, to test the loop limit.
- InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
+ // Reregister, to test the loop limit.
+ EXPECT_CALL(*stream2, OnCanWrite())
+ .WillOnce(Invoke(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
EXPECT_CALL(*stream6, OnCanWrite());
EXPECT_CALL(*stream4, OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTest, OnCanWriteBundlesStreams) {
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
- EXPECT_FALSE(session_.OnCanWrite());
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ TestStream* stream4 = session_.CreateOutgoingDataStream();
+ TestStream* stream6 = session_.CreateOutgoingDataStream();
+
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
+
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillRepeatedly(
+ Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm, GetCongestionWindow())
+ .WillOnce(Return(kMaxPacketSize * 10));
+ EXPECT_CALL(*stream2, OnCanWrite())
+ .WillOnce(IgnoreResult(Invoke(CreateFunctor(
+ &session_, &TestSession::SendStreamData, stream2->id()))));
+ EXPECT_CALL(*stream4, OnCanWrite())
+ .WillOnce(IgnoreResult(Invoke(CreateFunctor(
+ &session_, &TestSession::SendStreamData, stream4->id()))));
+ EXPECT_CALL(*stream6, OnCanWrite())
+ .WillOnce(IgnoreResult(Invoke(CreateFunctor(
+ &session_, &TestSession::SendStreamData, stream6->id()))));
+
+ // Expect that we only send one packet, the writes from different streams
+ // should be bundled together.
+ MockPacketWriter* writer =
+ static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _)).WillOnce(
+ Return(WriteResult(WRITE_STATUS_OK, 0)));
+ EXPECT_CALL(*send_algorithm, OnPacketSent(_, _, _, _, _)).Times(1);
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
}
-TEST_F(QuicSessionTest, BufferedHandshake) {
+TEST_P(QuicSessionTest, OnCanWriteCongestionControlBlocks) {
+ InSequence s;
+
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ TestStream* stream4 = session_.CreateOutgoingDataStream();
+ TestStream* stream6 = session_.CreateOutgoingDataStream();
+
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
+
+ StreamBlocker stream2_blocker(&session_, stream2->id());
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Zero()));
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Zero()));
+ EXPECT_CALL(*stream6, OnCanWrite());
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ // stream4->OnCanWrite is not called.
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // Still congestion-control blocked.
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // stream4->OnCanWrite is called once the connection stops being
+ // congestion-control blocked.
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Zero()));
+ EXPECT_CALL(*stream4, OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTest, BufferedHandshake) {
EXPECT_FALSE(session_.HasPendingHandshake()); // Default value.
// Test that blocking other streams does not change our status.
@@ -288,7 +476,7 @@ TEST_F(QuicSessionTest, BufferedHandshake) {
EXPECT_FALSE(session_.HasPendingHandshake());
// Blocking (due to buffering of) the Crypto stream is detected.
- session_.MarkWriteBlocked(kCryptoStreamId, kSomeMiddlePriority);
+ session_.MarkWriteBlocked(kCryptoStreamId, kHighestPriority);
EXPECT_TRUE(session_.HasPendingHandshake());
TestStream* stream4 = session_.CreateOutgoingDataStream();
@@ -307,20 +495,19 @@ TEST_F(QuicSessionTest, BufferedHandshake) {
EXPECT_CALL(*crypto_stream, OnCanWrite());
// Re-register all other streams, to show they weren't able to proceed.
- EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(
- InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
-
- EXPECT_CALL(*stream3, OnCanWrite()).WillOnce(
- InvokeWithoutArgs(&stream3_blocker, &StreamBlocker::MarkWriteBlocked));
-
- EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(
- InvokeWithoutArgs(&stream4_blocker, &StreamBlocker::MarkWriteBlocked));
-
- EXPECT_FALSE(session_.OnCanWrite());
+ EXPECT_CALL(*stream2, OnCanWrite())
+ .WillOnce(Invoke(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
+ EXPECT_CALL(*stream3, OnCanWrite())
+ .WillOnce(Invoke(&stream3_blocker, &StreamBlocker::MarkWriteBlocked));
+ EXPECT_CALL(*stream4, OnCanWrite())
+ .WillOnce(Invoke(&stream4_blocker, &StreamBlocker::MarkWriteBlocked));
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote.
}
-TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) {
+TEST_P(QuicSessionTest, OnCanWriteWithClosedStream) {
TestStream* stream2 = session_.CreateOutgoingDataStream();
TestStream* stream4 = session_.CreateOutgoingDataStream();
TestStream* stream6 = session_.CreateOutgoingDataStream();
@@ -333,146 +520,410 @@ TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) {
InSequence s;
EXPECT_CALL(*stream2, OnCanWrite());
EXPECT_CALL(*stream4, OnCanWrite());
- EXPECT_TRUE(session_.OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
}
-// Regression test for http://crbug.com/248737
-TEST_F(QuicSessionTest, OutOfOrderHeaders) {
- QuicSpdyCompressor compressor;
- vector<QuicStreamFrame> frames;
- QuicPacketHeader header;
- header.public_header.guid = session_.guid();
-
- TestStream* stream2 = session_.CreateOutgoingDataStream();
- TestStream* stream4 = session_.CreateOutgoingDataStream();
- stream2->CloseWriteSide();
- stream4->CloseWriteSide();
-
- // Create frame with headers for stream2.
- string compressed_headers1 = compressor.CompressHeaders(headers_);
- QuicStreamFrame frame1(
- stream2->id(), false, 0, MakeIOVector(compressed_headers1));
-
- // Create frame with headers for stream4.
- string compressed_headers2 = compressor.CompressHeaders(headers_);
- QuicStreamFrame frame2(
- stream4->id(), true, 0, MakeIOVector(compressed_headers2));
-
- // Process the second frame first. This will cause the headers to
- // be queued up and processed after the first frame is processed.
- frames.push_back(frame2);
- session_.OnStreamFrames(frames);
+TEST_P(QuicSessionTest, OnCanWriteLimitsNumWritesIfFlowControlBlocked) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
- // Process the first frame, and un-cork the buffered headers.
- frames[0] = frame1;
- session_.OnStreamFrames(frames);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Ensure connection level flow control blockage.
+ QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0);
+ EXPECT_TRUE(session_.flow_controller()->IsBlocked());
+
+ // Mark the crypto and headers streams as write blocked, we expect them to be
+ // allowed to write later.
+ session_.MarkWriteBlocked(kCryptoStreamId, kHighestPriority);
+ session_.MarkWriteBlocked(kHeadersStreamId, kHighestPriority);
+
+ // Create a data stream, and although it is write blocked we never expect it
+ // to be allowed to write as we are connection level flow control blocked.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ session_.MarkWriteBlocked(stream->id(), kSomeMiddlePriority);
+ EXPECT_CALL(*stream, OnCanWrite()).Times(0);
+
+ // The crypto and headers streams should be called even though we are
+ // connection flow control blocked.
+ TestCryptoStream* crypto_stream = session_.GetCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite()).Times(1);
+ TestHeadersStream* headers_stream = new TestHeadersStream(&session_);
+ QuicSessionPeer::SetHeadersStream(&session_, headers_stream);
+ EXPECT_CALL(*headers_stream, OnCanWrite()).Times(1);
- // Ensure that the streams actually close and we don't DCHECK.
- connection_->CloseConnection(QUIC_CONNECTION_TIMED_OUT, true);
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
}
-TEST_F(QuicSessionTest, SendGoAway) {
- // After sending a GoAway, ensure new incoming streams cannot be created and
- // result in a RST being sent.
+TEST_P(QuicSessionTest, SendGoAway) {
EXPECT_CALL(*connection_,
SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away."));
session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
EXPECT_TRUE(session_.goaway_sent());
- EXPECT_CALL(*connection_, SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY));
- EXPECT_FALSE(session_.GetIncomingReliableStream(3u));
+ EXPECT_CALL(*connection_,
+ SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY, 0)).Times(0);
+ EXPECT_TRUE(session_.GetIncomingDataStream(3u));
+}
+
+TEST_P(QuicSessionTest, DoNotSendGoAwayTwice) {
+ EXPECT_CALL(*connection_,
+ SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away.")).Times(1);
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+ EXPECT_TRUE(session_.goaway_sent());
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
}
-TEST_F(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) {
+TEST_P(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) {
EXPECT_EQ(kDefaultInitialTimeoutSecs,
QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
CryptoHandshakeMessage msg;
- session_.crypto_stream_.OnHandshakeMessage(msg);
+ session_.GetCryptoStream()->OnHandshakeMessage(msg);
EXPECT_EQ(kDefaultTimeoutSecs,
QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
}
-TEST_F(QuicSessionTest, ZombieStream) {
- StrictMock<MockConnection>* connection =
- new StrictMock<MockConnection>(false);
- TestSession session(connection);
-
- TestStream* stream3 = session.CreateOutgoingDataStream();
- EXPECT_EQ(3u, stream3->id());
- TestStream* stream5 = session.CreateOutgoingDataStream();
- EXPECT_EQ(5u, stream5->id());
- EXPECT_EQ(2u, session.GetNumOpenStreams());
+TEST_P(QuicSessionTest, RstStreamBeforeHeadersDecompressed) {
+ // Send two bytes of payload.
+ QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT"));
+ vector<QuicStreamFrame> frames;
+ frames.push_back(data1);
+ session_.OnStreamFrames(frames);
+ EXPECT_EQ(1u, session_.GetNumOpenStreams());
- // Reset the stream, but since the headers have not been decompressed
- // it will become a zombie and will continue to process data
- // until the headers are decompressed.
- EXPECT_CALL(*connection, SendRstStream(3, QUIC_STREAM_CANCELLED));
- session.SendRstStream(3, QUIC_STREAM_CANCELLED);
+ QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0);
+ session_.OnRstStream(rst1);
+ EXPECT_EQ(0u, session_.GetNumOpenStreams());
+ // Connection should remain alive.
+ EXPECT_TRUE(connection_->connected());
+}
- EXPECT_EQ(1u, session.GetNumOpenStreams());
+TEST_P(QuicSessionTest, MultipleRstStreamsCauseSingleConnectionClose) {
+ // If multiple invalid reset stream frames arrive in a single packet, this
+ // should trigger a connection close. However there is no need to send
+ // multiple connection close frames.
+ // Create valid stream.
+ QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT"));
vector<QuicStreamFrame> frames;
- QuicPacketHeader header;
- header.public_header.guid = session_.guid();
+ frames.push_back(data1);
+ session_.OnStreamFrames(frames);
+ EXPECT_EQ(1u, session_.GetNumOpenStreams());
- // Create frame with headers for stream2.
- QuicSpdyCompressor compressor;
- string compressed_headers1 = compressor.CompressHeaders(headers_);
- QuicStreamFrame frame1(
- stream3->id(), false, 0, MakeIOVector(compressed_headers1));
+ // Process first invalid stream reset, resulting in the connection being
+ // closed.
+ EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID))
+ .Times(1);
+ QuicStreamId kLargeInvalidStreamId = 99999999;
+ QuicRstStreamFrame rst1(kLargeInvalidStreamId, QUIC_STREAM_NO_ERROR, 0);
+ session_.OnRstStream(rst1);
+ QuicConnectionPeer::CloseConnection(connection_);
- // Process the second frame first. This will cause the headers to
- // be queued up and processed after the first frame is processed.
- frames.push_back(frame1);
- EXPECT_FALSE(stream3->headers_decompressed());
+ // Processing of second invalid stream reset should not result in the
+ // connection being closed for a second time.
+ QuicRstStreamFrame rst2(kLargeInvalidStreamId, QUIC_STREAM_NO_ERROR, 0);
+ session_.OnRstStream(rst2);
+}
+
+TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedStream) {
+ // Test that if a stream is flow control blocked, then on receipt of the SHLO
+ // containing a suitable send window offset, the stream becomes unblocked.
+ if (version() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
- session.OnStreamFrames(frames);
- EXPECT_EQ(1u, session.GetNumOpenStreams());
+ // Ensure that Writev consumes all the data it is given (simulate no socket
+ // blocking).
+ session_.set_writev_consumes_all_data(true);
+
+ // Create a stream, and send enough data to make it flow control blocked.
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ string body(kDefaultFlowControlSendWindow, '.');
+ EXPECT_FALSE(stream2->flow_controller()->IsBlocked());
+ stream2->SendBody(body, false);
+ EXPECT_TRUE(stream2->flow_controller()->IsBlocked());
- EXPECT_TRUE(connection->connected());
+ // Now complete the crypto handshake, resulting in an increased flow control
+ // send window.
+ CryptoHandshakeMessage msg;
+ session_.GetCryptoStream()->OnHandshakeMessage(msg);
+
+ // Stream is now unblocked.
+ EXPECT_FALSE(stream2->flow_controller()->IsBlocked());
}
-TEST_F(QuicSessionTest, ZombieStreamConnectionClose) {
- StrictMock<MockConnection>* connection =
- new StrictMock<MockConnection>(false);
- TestSession session(connection);
+TEST_P(QuicSessionTest, InvalidFlowControlWindowInHandshake) {
+ // TODO(rjshade): Remove this test when removing QUIC_VERSION_19.
+ // Test that receipt of an invalid (< default) flow control window from
+ // the peer results in the connection being torn down.
+ if (version() <= QUIC_VERSION_16 || version() > QUIC_VERSION_19) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
- TestStream* stream3 = session.CreateOutgoingDataStream();
- EXPECT_EQ(3u, stream3->id());
- TestStream* stream5 = session.CreateOutgoingDataStream();
- EXPECT_EQ(5u, stream5->id());
- EXPECT_EQ(2u, session.GetNumOpenStreams());
+ uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialFlowControlWindow(session_.config(),
+ kInvalidWindow);
- stream3->CloseWriteSide();
- // Reset the stream, but since the headers have not been decompressed
- // it will become a zombie and will continue to process data
- // until the headers are decompressed.
- EXPECT_CALL(*connection, SendRstStream(3, QUIC_STREAM_CANCELLED));
- session.SendRstStream(3, QUIC_STREAM_CANCELLED);
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW)).Times(2);
+ session_.OnConfigNegotiated();
+}
- EXPECT_EQ(1u, session.GetNumOpenStreams());
+TEST_P(QuicSessionTest, InvalidStreamFlowControlWindowInHandshake) {
+ // Test that receipt of an invalid (< default) stream flow control window from
+ // the peer results in the connection being torn down.
+ if (version() <= QUIC_VERSION_19) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
- connection->CloseConnection(QUIC_CONNECTION_TIMED_OUT, false);
+ uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(),
+ kInvalidWindow);
- EXPECT_EQ(0u, session.GetNumOpenStreams());
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW));
+ session_.OnConfigNegotiated();
}
-TEST_F(QuicSessionTest, RstStreamBeforeHeadersDecompressed) {
- // Send two bytes of payload.
- QuicStreamFrame data1(3, false, 0, MakeIOVector("HT"));
+TEST_P(QuicSessionTest, InvalidSessionFlowControlWindowInHandshake) {
+ // Test that receipt of an invalid (< default) session flow control window
+ // from the peer results in the connection being torn down.
+ if (version() <= QUIC_VERSION_19) {
+ return;
+ }
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+ uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(),
+ kInvalidWindow);
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW));
+ session_.OnConfigNegotiated();
+}
+
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Test that when we receive an out of order stream RST we correctly adjust
+ // our connection level flow control receive window.
+ // On close, the stream should mark as consumed all bytes between the highest
+ // byte consumed so far and the final byte offset from the RST frame.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+
+ const QuicStreamOffset kByteOffset =
+ 1 + kInitialSessionFlowControlWindowForTest / 2;
+
+ // Expect no stream WINDOW_UPDATE frames, as stream read side closed.
+ EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0);
+ // We do expect a connection level WINDOW_UPDATE when the stream is reset.
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest +
+ kByteOffset)).Times(1);
+
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED,
+ kByteOffset);
+ session_.OnRstStream(rst_frame);
+ session_.PostProcessAfterData();
+ EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
+}
+
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAndLocalReset) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Test the situation where we receive a FIN on a stream, and before we fully
+ // consume all the data from the sequencer buffer we locally RST the stream.
+ // The bytes between highest consumed byte, and the final byte offset that we
+ // determined when the FIN arrived, should be marked as consumed at the
+ // connection level flow controller when the stream is reset.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+
+ const QuicStreamOffset kByteOffset =
+ 1 + kInitialSessionFlowControlWindowForTest / 2;
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, IOVector());
vector<QuicStreamFrame> frames;
- frames.push_back(data1);
- EXPECT_TRUE(session_.OnStreamFrames(frames));
- EXPECT_EQ(1u, session_.GetNumOpenStreams());
+ frames.push_back(frame);
+ session_.OnStreamFrames(frames);
+ session_.PostProcessAfterData();
+
+ EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed());
+ EXPECT_EQ(kByteOffset,
+ stream->flow_controller()->highest_received_byte_offset());
+
+ // We only expect to see a connection WINDOW_UPDATE when talking
+ // QUIC_VERSION_19, as in this case both stream and session flow control
+ // windows are the same size. In later versions we will not see a connection
+ // level WINDOW_UPDATE when exhausting a stream, as the stream flow control
+ // limit is much lower than the connection flow control limit.
+ if (version() == QUIC_VERSION_19) {
+ // Expect no stream WINDOW_UPDATE frames, as stream read side closed.
+ EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0);
+ // We do expect a connection level WINDOW_UPDATE when the stream is reset.
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest +
+ kByteOffset)).Times(1);
+ }
- // Send a reset before the headers have been decompressed. This causes
- // an unrecoverable compression context state.
- EXPECT_CALL(*connection_, SendConnectionClose(
- QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED));
+ // Reset stream locally.
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
+}
- QuicRstStreamFrame rst1(3, QUIC_STREAM_NO_ERROR);
- session_.OnRstStream(rst1);
- EXPECT_EQ(0u, session_.GetNumOpenStreams());
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) {
+ // Test that when we RST the stream (and tear down stream state), and then
+ // receive a FIN from the peer, we correctly adjust our connection level flow
+ // control receive window.
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Connection starts with some non-zero highest received byte offset,
+ // due to other active streams.
+ const uint64 kInitialConnectionBytesConsumed = 567;
+ const uint64 kInitialConnectionHighestReceivedOffset = 1234;
+ EXPECT_LT(kInitialConnectionBytesConsumed,
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->UpdateHighestReceivedOffset(
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed);
+
+ // Reset our stream: this results in the stream being closed locally.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ stream->Reset(QUIC_STREAM_CANCELLED);
+
+ // Now receive a response from the peer with a FIN. We should handle this by
+ // adjusting the connection level flow control receive window to take into
+ // account the total number of bytes sent by the peer.
+ const QuicStreamOffset kByteOffset = 5678;
+ string body = "hello";
+ IOVector data = MakeIOVector(body);
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, data);
+ vector<QuicStreamFrame> frames;
+ frames.push_back(frame);
+ session_.OnStreamFrames(frames);
+
+ QuicStreamOffset total_stream_bytes_sent_by_peer =
+ kByteOffset + body.length();
+ EXPECT_EQ(kInitialConnectionBytesConsumed + total_stream_bytes_sent_by_peer,
+ session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(
+ kInitialConnectionHighestReceivedOffset + total_stream_bytes_sent_by_peer,
+ session_.flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstAfterRst) {
+ // Test that when we RST the stream (and tear down stream state), and then
+ // receive a RST from the peer, we correctly adjust our connection level flow
+ // control receive window.
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Connection starts with some non-zero highest received byte offset,
+ // due to other active streams.
+ const uint64 kInitialConnectionBytesConsumed = 567;
+ const uint64 kInitialConnectionHighestReceivedOffset = 1234;
+ EXPECT_LT(kInitialConnectionBytesConsumed,
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->UpdateHighestReceivedOffset(
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed);
+
+ // Reset our stream: this results in the stream being closed locally.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ stream->Reset(QUIC_STREAM_CANCELLED);
+
+ // Now receive a RST from the peer. We should handle this by adjusting the
+ // connection level flow control receive window to take into account the total
+ // number of bytes sent by the peer.
+ const QuicStreamOffset kByteOffset = 5678;
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED,
+ kByteOffset);
+ session_.OnRstStream(rst_frame);
+
+ EXPECT_EQ(kInitialConnectionBytesConsumed + kByteOffset,
+ session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(kInitialConnectionHighestReceivedOffset + kByteOffset,
+ session_.flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicSessionTest, FlowControlWithInvalidFinalOffset) {
+ // Test that if we receive a stream RST with a highest byte offset that
+ // violates flow control, that we close the connection.
+ if (version() < QUIC_VERSION_17) {
+ return;
+ }
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+
+ const uint64 kLargeOffset = kInitialSessionFlowControlWindowForTest + 1;
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA))
+ .Times(2);
+
+ // Check that stream frame + FIN results in connection close.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ QuicStreamFrame frame(stream->id(), true, kLargeOffset, IOVector());
+ vector<QuicStreamFrame> frames;
+ frames.push_back(frame);
+ session_.OnStreamFrames(frames);
+
+ // Check that RST results in connection close.
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED,
+ kLargeOffset);
+ session_.OnRstStream(rst_frame);
+}
+
+TEST_P(QuicSessionTest, VersionNegotiationDisablesFlowControl) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
+ true);
+ // Test that after successful version negotiation, flow control is disabled
+ // appropriately at both the connection and stream level.
+
+ // Initially both stream and connection flow control are enabled.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ EXPECT_TRUE(stream->flow_controller()->IsEnabled());
+ EXPECT_TRUE(session_.flow_controller()->IsEnabled());
+
+ // Version 17 implies that stream flow control is enabled, but connection
+ // level is disabled.
+ session_.OnSuccessfulVersionNegotiation(QUIC_VERSION_17);
+ EXPECT_FALSE(session_.flow_controller()->IsEnabled());
+ EXPECT_TRUE(stream->flow_controller()->IsEnabled());
+
+ // Version 16 means all flow control is disabled.
+ session_.OnSuccessfulVersionNegotiation(QUIC_VERSION_16);
+ EXPECT_FALSE(session_.flow_controller()->IsEnabled());
+ EXPECT_FALSE(stream->flow_controller()->IsEnabled());
}
} // namespace
diff --git a/chromium/net/quic/quic_socket_address_coder.cc b/chromium/net/quic/quic_socket_address_coder.cc
new file mode 100644
index 00000000000..77e595f2d65
--- /dev/null
+++ b/chromium/net/quic/quic_socket_address_coder.cc
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_socket_address_coder.h"
+
+using std::string;
+
+namespace net {
+
+namespace {
+
+// For convenience, the values of these constants match the values of AF_INET
+// and AF_INET6 on Linux.
+const uint16 kIPv4 = 2;
+const uint16 kIPv6 = 10;
+
+} // namespace
+
+QuicSocketAddressCoder::QuicSocketAddressCoder() {
+}
+
+QuicSocketAddressCoder::QuicSocketAddressCoder(const IPEndPoint& address)
+ : address_(address) {
+}
+
+QuicSocketAddressCoder::~QuicSocketAddressCoder() {
+}
+
+string QuicSocketAddressCoder::Encode() const {
+ string serialized;
+ uint16 address_family;
+ switch (address_.GetSockAddrFamily()) {
+ case AF_INET:
+ address_family = kIPv4;
+ break;
+ case AF_INET6:
+ address_family = kIPv6;
+ break;
+ default:
+ return serialized;
+ }
+ serialized.append(reinterpret_cast<const char*>(&address_family),
+ sizeof(address_family));
+ serialized.append(IPAddressToPackedString(address_.address()));
+ uint16 port = address_.port();
+ serialized.append(reinterpret_cast<const char*>(&port), sizeof(port));
+ return serialized;
+}
+
+bool QuicSocketAddressCoder::Decode(const char* data, size_t length) {
+ uint16 address_family;
+ if (length < sizeof(address_family)) {
+ return false;
+ }
+ memcpy(&address_family, data, sizeof(address_family));
+ data += sizeof(address_family);
+ length -= sizeof(address_family);
+
+ size_t ip_length;
+ switch (address_family) {
+ case kIPv4:
+ ip_length = kIPv4AddressSize;
+ break;
+ case kIPv6:
+ ip_length = kIPv6AddressSize;
+ break;
+ default:
+ return false;
+ }
+ if (length < ip_length) {
+ return false;
+ }
+ IPAddressNumber ip(ip_length);
+ memcpy(&ip[0], data, ip_length);
+ data += ip_length;
+ length -= ip_length;
+
+ uint16 port;
+ if (length != sizeof(port)) {
+ return false;
+ }
+ memcpy(&port, data, length);
+
+ address_ = IPEndPoint(ip, port);
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_socket_address_coder.h b/chromium/net/quic/quic_socket_address_coder.h
new file mode 100644
index 00000000000..36ad1d03620
--- /dev/null
+++ b/chromium/net/quic/quic_socket_address_coder.h
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SOCKET_ADDRESS_CODER_H_
+#define NET_QUIC_QUIC_SOCKET_ADDRESS_CODER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Serializes and parses a socket address (IP address and port), to be used in
+// the kCADR tag in the ServerHello handshake message and the Public Reset
+// packet.
+class NET_EXPORT_PRIVATE QuicSocketAddressCoder {
+ public:
+ QuicSocketAddressCoder();
+ explicit QuicSocketAddressCoder(const IPEndPoint& address);
+ ~QuicSocketAddressCoder();
+
+ std::string Encode() const;
+
+ bool Decode(const char* data, size_t length);
+
+ IPAddressNumber ip() const {
+ return address_.address();
+ }
+
+ uint16 port() const {
+ return address_.port();
+ }
+
+ private:
+ IPEndPoint address_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSocketAddressCoder);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SOCKET_ADDRESS_CODER_H_
diff --git a/chromium/net/quic/quic_socket_address_coder_test.cc b/chromium/net/quic/quic_socket_address_coder_test.cc
new file mode 100644
index 00000000000..56bed58b488
--- /dev/null
+++ b/chromium/net/quic/quic_socket_address_coder_test.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_socket_address_coder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+TEST(QuicSocketAddressCoderTest, EncodeIPv4) {
+ IPAddressNumber ip;
+ ASSERT_TRUE(ParseIPLiteralToNumber("4.31.198.44", &ip));
+ QuicSocketAddressCoder coder(IPEndPoint(ip, 0x1234));
+ string serialized = coder.Encode();
+ string expected("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8);
+ EXPECT_EQ(expected, serialized);
+}
+
+TEST(QuicSocketAddressCoderTest, EncodeIPv6) {
+ IPAddressNumber ip;
+ ASSERT_TRUE(ParseIPLiteralToNumber("2001:700:300:1800::f", &ip));
+ QuicSocketAddressCoder coder(IPEndPoint(ip, 0x5678));
+ string serialized = coder.Encode();
+ string expected("\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56", 20);
+ EXPECT_EQ(expected, serialized);
+}
+
+TEST(QuicSocketAddressCoderTest, DecodeIPv4) {
+ string serialized("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8);
+ QuicSocketAddressCoder coder;
+ ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(AF_INET, ConvertAddressFamily(GetAddressFamily(coder.ip())));
+ string expected_addr("\x04\x1f\xc6\x2c", 4);
+ EXPECT_EQ(expected_addr, IPAddressToPackedString(coder.ip()));
+ EXPECT_EQ(0x1234, coder.port());
+}
+
+TEST(QuicSocketAddressCoderTest, DecodeIPv6) {
+ string serialized("\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56", 20);
+ QuicSocketAddressCoder coder;
+ ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(AF_INET6, ConvertAddressFamily(GetAddressFamily(coder.ip())));
+ string expected_addr("\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f", 16);
+ EXPECT_EQ(expected_addr, IPAddressToPackedString(coder.ip()));
+ EXPECT_EQ(0x5678, coder.port());
+}
+
+TEST(QuicSocketAddressCoderTest, DecodeBad) {
+ string serialized("\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56", 20);
+ QuicSocketAddressCoder coder;
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ // Append junk.
+ serialized.push_back('\0');
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ // Undo.
+ serialized.resize(20);
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+
+ // Set an unknown address family.
+ serialized[0] = '\x03';
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ // Undo.
+ serialized[0] = '\x0a';
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+
+ // Truncate.
+ size_t len = serialized.length();
+ for (size_t i = 0; i < len; i++) {
+ ASSERT_FALSE(serialized.empty());
+ serialized.erase(serialized.length() - 1);
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ }
+ EXPECT_TRUE(serialized.empty());
+}
+
+TEST(QuicSocketAddressCoderTest, EncodeAndDecode) {
+ struct {
+ const char* ip_literal;
+ uint16 port;
+ } test_case[] = {
+ { "93.184.216.119", 0x1234 },
+ { "199.204.44.194", 80 },
+ { "149.20.4.69", 443 },
+ { "127.0.0.1", 8080 },
+ { "2001:700:300:1800::", 0x5678 },
+ { "::1", 65534 },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_case); i++) {
+ IPAddressNumber ip;
+ ASSERT_TRUE(ParseIPLiteralToNumber(test_case[i].ip_literal, &ip));
+ QuicSocketAddressCoder encoder(IPEndPoint(ip, test_case[i].port));
+ string serialized = encoder.Encode();
+
+ QuicSocketAddressCoder decoder;
+ ASSERT_TRUE(decoder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(encoder.ip(), decoder.ip());
+ EXPECT_EQ(encoder.port(), decoder.port());
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_spdy_compressor.cc b/chromium/net/quic/quic_spdy_compressor.cc
deleted file mode 100644
index 55015b0b42d..00000000000
--- a/chromium/net/quic/quic_spdy_compressor.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/quic_spdy_compressor.h"
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-
-using std::string;
-
-namespace net {
-
-QuicSpdyCompressor::QuicSpdyCompressor()
- : spdy_framer_(SPDY3),
- header_sequence_id_(1) {
- spdy_framer_.set_enable_compression(true);
-}
-
-QuicSpdyCompressor::~QuicSpdyCompressor() {
-}
-
-string QuicSpdyCompressor::CompressHeadersWithPriority(
- QuicPriority priority,
- const SpdyHeaderBlock& headers) {
- return CompressHeadersInternal(priority, headers, true);
-}
-
-string QuicSpdyCompressor::CompressHeaders(
- const SpdyHeaderBlock& headers) {
- // CompressHeadersInternal ignores priority when write_priority is false.
- return CompressHeadersInternal(0 /* ignored */, headers, false);
-}
-
-string QuicSpdyCompressor::CompressHeadersInternal(
- QuicPriority priority,
- const SpdyHeaderBlock& headers,
- bool write_priority) {
- // TODO(rch): Modify the SpdyFramer to expose a
- // CreateCompressedHeaderBlock method, or some such.
- SpdyStreamId stream_id = 3; // unused.
- scoped_ptr<SpdyFrame> frame(spdy_framer_.CreateHeaders(
- stream_id, CONTROL_FLAG_NONE, &headers));
-
- // The size of the spdy HEADER frame's fixed prefix which
- // needs to be stripped off from the resulting frame.
- const size_t header_frame_prefix_len = 12;
- string serialized = string(frame->data() + header_frame_prefix_len,
- frame->size() - header_frame_prefix_len);
- uint32 serialized_len = serialized.length();
- char priority_str[sizeof(priority)];
- memcpy(&priority_str, &priority, sizeof(priority));
- char id_str[sizeof(header_sequence_id_)];
- memcpy(&id_str, &header_sequence_id_, sizeof(header_sequence_id_));
- char len_str[sizeof(serialized_len)];
- memcpy(&len_str, &serialized_len, sizeof(serialized_len));
- string compressed;
- int priority_len = write_priority ? arraysize(priority_str) : 0;
- compressed.reserve(
- priority_len + arraysize(id_str) + arraysize(len_str) + serialized_len);
- if (write_priority) {
- compressed.append(priority_str, arraysize(priority_str));
- }
- compressed.append(id_str, arraysize(id_str));
- compressed.append(len_str, arraysize(len_str));
- compressed.append(serialized);
- ++header_sequence_id_;
- return compressed;
-}
-
-} // namespace net
diff --git a/chromium/net/quic/quic_spdy_compressor.h b/chromium/net/quic/quic_spdy_compressor.h
deleted file mode 100644
index 6efb73c74bd..00000000000
--- a/chromium/net/quic/quic_spdy_compressor.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_QUIC_SPDY_COMPRESSOR_H_
-#define NET_QUIC_QUIC_SPDY_COMPRESSOR_H_
-
-#include <string>
-
-#include "net/base/net_export.h"
-#include "net/quic/quic_protocol.h"
-#include "net/spdy/spdy_framer.h"
-
-namespace net {
-
-// Handles the compression of request/response headers blocks. The
-// serialized format is:
-// uint32 - Priority
-// uint32 - Header ID
-// uint32 - Compressed header length
-// ... - Compressed data
-//
-class NET_EXPORT_PRIVATE QuicSpdyCompressor {
- public:
- QuicSpdyCompressor();
- ~QuicSpdyCompressor();
-
- // Returns a string comprised of [header_sequence_id, compressed_headers].
- std::string CompressHeaders(const SpdyHeaderBlock& headers);
-
- // Returns a string comprised of
- // [priority, header_sequence_id, compressed_headers]
- std::string CompressHeadersWithPriority(QuicPriority priority,
- const SpdyHeaderBlock& headers);
-
- private:
- std::string CompressHeadersInternal(QuicPriority priority,
- const SpdyHeaderBlock& headers,
- bool write_priority);
-
- SpdyFramer spdy_framer_;
- QuicHeaderId header_sequence_id_;
-
- DISALLOW_COPY_AND_ASSIGN(QuicSpdyCompressor);
-};
-
-} // namespace net
-
-#endif // NET_QUIC_QUIC_SPDY_COMPRESSOR_H_
diff --git a/chromium/net/quic/quic_spdy_compressor_test.cc b/chromium/net/quic/quic_spdy_compressor_test.cc
deleted file mode 100644
index 93066f29091..00000000000
--- a/chromium/net/quic/quic_spdy_compressor_test.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
-#include "net/quic/spdy_utils.h"
-#include "net/quic/test_tools/quic_test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using std::string;
-
-namespace net {
-namespace test {
-namespace {
-
-class QuicSpdyCompressorTest : public ::testing::Test {
- protected:
- TestDecompressorVisitor visitor_;
-};
-
-TEST_F(QuicSpdyCompressorTest, Compress) {
- QuicSpdyCompressor compressor;
- QuicSpdyDecompressor decompressor;
-
- SpdyHeaderBlock headers;
- headers[":host"] = "www.google.com";
- headers[":path"] = "/index.hml";
- headers[":scheme"] = "https";
-
- string compressed_headers = compressor.CompressHeaders(headers);
- EXPECT_EQ('\1', compressed_headers[0]);
- EXPECT_EQ('\0', compressed_headers[1]);
- EXPECT_EQ('\0', compressed_headers[2]);
- EXPECT_EQ('\0', compressed_headers[3]);
- string compressed_data = compressed_headers.substr(4);
- decompressor.DecompressData(compressed_data, &visitor_);
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers),
- visitor_.data());
-}
-
-} // namespace
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/quic_spdy_decompressor.cc b/chromium/net/quic/quic_spdy_decompressor.cc
deleted file mode 100644
index f8bd4c142a8..00000000000
--- a/chromium/net/quic/quic_spdy_decompressor.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/quic_spdy_decompressor.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-
-using base::StringPiece;
-using std::min;
-
-namespace net {
-
-class SpdyFramerVisitor : public SpdyFramerVisitorInterface {
- public:
- explicit SpdyFramerVisitor(QuicSpdyDecompressor::Visitor* visitor)
- : visitor_(visitor),
- error_(false) {
- }
-
- virtual void OnError(SpdyFramer* framer) OVERRIDE {
- error_ = true;
- }
- virtual void OnDataFrameHeader(SpdyStreamId stream_id,
- size_t length,
- bool fin) OVERRIDE {}
- virtual void OnStreamFrameData(SpdyStreamId stream_id,
- const char* data,
- size_t len,
- bool fin) OVERRIDE {}
- virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id,
- const char* header_data,
- size_t len) OVERRIDE;
- virtual void OnSynStream(SpdyStreamId stream_id,
- SpdyStreamId associated_stream_id,
- SpdyPriority priority,
- uint8 credential_slot,
- bool fin,
- bool unidirectional) OVERRIDE {}
- virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {}
- virtual void OnRstStream(SpdyStreamId stream_id,
- SpdyRstStreamStatus status) OVERRIDE {}
- virtual void OnSetting(SpdySettingsIds id,
- uint8 flags,
- uint32 value) OVERRIDE {}
- virtual void OnPing(uint32 unique_id) OVERRIDE {}
- virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
- SpdyGoAwayStatus status) OVERRIDE {}
- virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {}
- virtual void OnWindowUpdate(SpdyStreamId stream_id,
- uint32 delta_window_size) OVERRIDE {}
- virtual bool OnCredentialFrameData(const char* credential_data,
- size_t len) OVERRIDE {
- return false;
- }
- virtual void OnPushPromise(SpdyStreamId stream_id,
- SpdyStreamId promised_stream_id) OVERRIDE {}
- void set_visitor(QuicSpdyDecompressor::Visitor* visitor) {
- DCHECK(visitor);
- visitor_ = visitor;
- }
-
- private:
- QuicSpdyDecompressor::Visitor* visitor_;
- bool error_;
-};
-
-bool SpdyFramerVisitor::OnControlFrameHeaderData(SpdyStreamId stream_id,
- const char* header_data,
- size_t len) {
- DCHECK(visitor_);
- return visitor_->OnDecompressedData(StringPiece(header_data, len));
-}
-
-QuicSpdyDecompressor::QuicSpdyDecompressor()
- : spdy_framer_(SPDY3),
- spdy_visitor_(new SpdyFramerVisitor(NULL)),
- current_header_id_(1),
- has_current_compressed_size_(false),
- current_compressed_size_(0),
- compressed_bytes_consumed_(0) {
- spdy_framer_.set_visitor(spdy_visitor_.get());
-}
-
-QuicSpdyDecompressor::~QuicSpdyDecompressor() {
-}
-
-size_t QuicSpdyDecompressor::DecompressData(StringPiece data,
- Visitor* visitor) {
- spdy_visitor_->set_visitor(visitor);
- size_t bytes_consumed = 0;
-
- if (!has_current_compressed_size_) {
- const size_t kCompressedBufferSizeSize = sizeof(uint32);
- DCHECK_GT(kCompressedBufferSizeSize, compressed_size_buffer_.length());
- size_t missing_size =
- kCompressedBufferSizeSize - compressed_size_buffer_.length();
- if (data.length() < missing_size) {
- data.AppendToString(&compressed_size_buffer_);
- return data.length();
- }
- bytes_consumed += missing_size;
- data.substr(0, missing_size).AppendToString(&compressed_size_buffer_);
- DCHECK_EQ(kCompressedBufferSizeSize, compressed_size_buffer_.length());
- memcpy(&current_compressed_size_, compressed_size_buffer_.data(),
- kCompressedBufferSizeSize);
- compressed_size_buffer_.clear();
- has_current_compressed_size_ = true;
- data = data.substr(missing_size);
- compressed_bytes_consumed_ = 0;
- }
-
- size_t bytes_to_consume =
- min(current_compressed_size_ - compressed_bytes_consumed_,
- static_cast<uint32>(data.length()));
- if (bytes_to_consume > 0) {
- if (!spdy_framer_.IncrementallyDecompressControlFrameHeaderData(
- current_header_id_, data.data(), bytes_to_consume)) {
- visitor->OnDecompressionError();
- return bytes_consumed;
- }
- compressed_bytes_consumed_ += bytes_to_consume;
- bytes_consumed += bytes_to_consume;
- }
- if (current_compressed_size_ - compressed_bytes_consumed_ == 0) {
- ResetForNextHeaders();
- }
- return bytes_consumed;
-}
-
-void QuicSpdyDecompressor::ResetForNextHeaders() {
- has_current_compressed_size_ = false;
- current_compressed_size_ = 0;
- compressed_bytes_consumed_ = 0;
- ++current_header_id_;
-}
-
-} // namespace net
diff --git a/chromium/net/quic/quic_spdy_decompressor.h b/chromium/net/quic/quic_spdy_decompressor.h
deleted file mode 100644
index a56c4799015..00000000000
--- a/chromium/net/quic/quic_spdy_decompressor.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_
-#define NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/string_piece.h"
-#include "net/base/net_export.h"
-#include "net/quic/quic_protocol.h"
-#include "net/spdy/spdy_framer.h"
-
-namespace net {
-
-class SpdyFramerVisitor;
-
-// Handles the compression of request/response headers blocks.
-class NET_EXPORT_PRIVATE QuicSpdyDecompressor {
- public:
- // Interface that receives callbacks with decompressed data as it
- // becomes available.
- class NET_EXPORT_PRIVATE Visitor {
- public:
- virtual ~Visitor() {}
- virtual bool OnDecompressedData(base::StringPiece data) = 0;
- virtual void OnDecompressionError() = 0;
- };
-
- QuicSpdyDecompressor();
- ~QuicSpdyDecompressor();
-
- // Decompresses the data in |data| and invokes |OnDecompressedData|,
- // possibly multiple times, on |visitor|. Returns number of bytes
- // consumed from |data|.
- size_t DecompressData(base::StringPiece data, Visitor* visitor);
-
- QuicHeaderId current_header_id() { return current_header_id_; }
-
- private:
- void ResetForNextHeaders();
-
- SpdyFramer spdy_framer_;
- scoped_ptr<SpdyFramerVisitor> spdy_visitor_;
- // ID of the header currently being parsed.
- QuicHeaderId current_header_id_;
- // True when the size of the headers has been parsed.
- bool has_current_compressed_size_;
- // Size of the headers being parsed.
- uint32 current_compressed_size_;
- // Buffer into which the partial compressed size is written until
- // it is fully parsed.
- std::string compressed_size_buffer_;
- // Number of compressed bytes consumed, out of the total in
- // |current_compressed_size_|.
- uint32 compressed_bytes_consumed_;
- DISALLOW_COPY_AND_ASSIGN(QuicSpdyDecompressor);
-};
-
-} // namespace net
-
-#endif // NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_
diff --git a/chromium/net/quic/quic_spdy_decompressor_test.cc b/chromium/net/quic/quic_spdy_decompressor_test.cc
deleted file mode 100644
index de9156bdfa4..00000000000
--- a/chromium/net/quic/quic_spdy_decompressor_test.cc
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
-#include "net/quic/spdy_utils.h"
-#include "net/quic/test_tools/quic_test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using std::string;
-
-namespace net {
-namespace test {
-namespace {
-
-class QuicSpdyDecompressorTest : public ::testing::Test {
- protected:
- QuicSpdyDecompressor decompressor_;
- QuicSpdyCompressor compressor_;
- TestDecompressorVisitor visitor_;
-};
-
-TEST_F(QuicSpdyDecompressorTest, Decompress) {
- SpdyHeaderBlock headers;
- headers[":host"] = "www.google.com";
- headers[":path"] = "/index.hml";
- headers[":scheme"] = "https";
-
- EXPECT_EQ(1u, decompressor_.current_header_id());
- string compressed_headers = compressor_.CompressHeaders(headers).substr(4);
- EXPECT_EQ(compressed_headers.length(),
- decompressor_.DecompressData(compressed_headers, &visitor_));
-
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor_.data());
- EXPECT_EQ(2u, decompressor_.current_header_id());
-}
-
-TEST_F(QuicSpdyDecompressorTest, DecompressPartial) {
- SpdyHeaderBlock headers;
- headers[":host"] = "www.google.com";
- headers[":path"] = "/index.hml";
- headers[":scheme"] = "https";
- string compressed_headers = compressor_.CompressHeaders(headers).substr(4);
-
- for (size_t i = 0; i < compressed_headers.length(); ++i) {
- QuicSpdyDecompressor decompressor;
- TestDecompressorVisitor visitor;
-
- EXPECT_EQ(1u, decompressor.current_header_id());
-
- string partial_compressed_headers = compressed_headers.substr(0, i);
- EXPECT_EQ(partial_compressed_headers.length(),
- decompressor.DecompressData(partial_compressed_headers,
- &visitor));
- EXPECT_EQ(1u, decompressor.current_header_id()) << "i: " << i;
-
- string remaining_compressed_headers =
- compressed_headers.substr(partial_compressed_headers.length());
- EXPECT_EQ(remaining_compressed_headers.length(),
- decompressor.DecompressData(remaining_compressed_headers,
- &visitor));
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor.data());
-
- EXPECT_EQ(2u, decompressor.current_header_id());
- }
-}
-
-TEST_F(QuicSpdyDecompressorTest, DecompressAndIgnoreTrailingData) {
- SpdyHeaderBlock headers;
- headers[":host"] = "www.google.com";
- headers[":path"] = "/index.hml";
- headers[":scheme"] = "https";
-
- string compressed_headers = compressor_.CompressHeaders(headers).substr(4);
- EXPECT_EQ(compressed_headers.length(),
- decompressor_.DecompressData(compressed_headers + "abc123",
- &visitor_));
-
- EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor_.data());
-}
-
-TEST_F(QuicSpdyDecompressorTest, DecompressError) {
- SpdyHeaderBlock headers;
- headers[":host"] = "www.google.com";
- headers[":path"] = "/index.hml";
- headers[":scheme"] = "https";
-
- EXPECT_EQ(1u, decompressor_.current_header_id());
- string compressed_headers = compressor_.CompressHeaders(headers).substr(4);
- compressed_headers[compressed_headers.length() - 1] ^= 0x01;
- EXPECT_NE(compressed_headers.length(),
- decompressor_.DecompressData(compressed_headers, &visitor_));
-
- EXPECT_TRUE(visitor_.error());
- EXPECT_EQ("", visitor_.data());
-}
-
-} // namespace
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/quic_spdy_server_stream.cc b/chromium/net/quic/quic_spdy_server_stream.cc
new file mode 100644
index 00000000000..81fc2e064e7
--- /dev/null
+++ b/chromium/net/quic/quic_spdy_server_stream.cc
@@ -0,0 +1,143 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_spdy_server_stream.h"
+
+#include "base/memory/singleton.h"
+#include "net/quic/quic_in_memory_cache.h"
+#include "net/quic/quic_session.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/tools/quic/spdy_utils.h"
+
+using base::StringPiece;
+using std::string;
+using net::tools::SpdyUtils;
+
+namespace net {
+
+static const size_t kHeaderBufInitialSize = 4096;
+
+QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id,
+ QuicSession* session)
+ : QuicDataStream(id, session),
+ read_buf_(new GrowableIOBuffer()),
+ request_headers_received_(false) {
+}
+
+QuicSpdyServerStream::~QuicSpdyServerStream() {
+}
+
+uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) {
+ uint32 total_bytes_processed = 0;
+
+ // Are we still reading the request headers.
+ if (!request_headers_received_) {
+ // Grow the read buffer if necessary.
+ if (read_buf_->RemainingCapacity() < (int)data_len) {
+ read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
+ }
+ memcpy(read_buf_->data(), data, data_len);
+ read_buf_->set_offset(read_buf_->offset() + data_len);
+ ParseRequestHeaders();
+ } else {
+ body_.append(data + total_bytes_processed,
+ data_len - total_bytes_processed);
+ }
+ return data_len;
+}
+
+void QuicSpdyServerStream::OnFinRead() {
+ ReliableQuicStream::OnFinRead();
+ if (write_side_closed() || fin_buffered()) {
+ return;
+ }
+
+ if (!request_headers_received_) {
+ SendErrorResponse(); // We're not done reading headers.
+ } else if ((headers_.content_length_status() ==
+ BalsaHeadersEnums::VALID_CONTENT_LENGTH) &&
+ body_.size() != headers_.content_length()) {
+ SendErrorResponse(); // Invalid content length
+ } else {
+ SendResponse();
+ }
+}
+
+int QuicSpdyServerStream::ParseRequestHeaders() {
+ size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
+ SpdyFramer framer(SPDY3);
+ SpdyHeaderBlock headers;
+ char* data = read_buf_->StartOfBuffer();
+ size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(),
+ &headers);
+ if (len == 0) {
+ return -1;
+ }
+
+ if (!SpdyUtils::FillBalsaRequestHeaders(headers, &headers_)) {
+ SendErrorResponse();
+ return -1;
+ }
+
+ size_t delta = read_buf_len - len;
+ if (delta > 0) {
+ body_.append(data + len, delta);
+ }
+
+ request_headers_received_ = true;
+ return len;
+}
+
+void QuicSpdyServerStream::SendResponse() {
+ // Find response in cache. If not found, send error response.
+ const QuicInMemoryCache::Response* response =
+ QuicInMemoryCache::GetInstance()->GetResponse(headers_);
+ if (response == NULL) {
+ SendErrorResponse();
+ return;
+ }
+
+ if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) {
+ DVLOG(1) << "Special response: closing connection.";
+ CloseConnection(QUIC_NO_ERROR);
+ return;
+ }
+
+ if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) {
+ DVLOG(1) << "Special response: ignoring request.";
+ return;
+ }
+
+ DVLOG(1) << "Sending response for stream " << id();
+ SendHeadersAndBody(response->headers(), response->body());
+}
+
+void QuicSpdyServerStream::SendErrorResponse() {
+ DVLOG(1) << "Sending error response for stream " << id();
+ BalsaHeaders headers;
+ headers.SetResponseFirstlineFromStringPieces(
+ "HTTP/1.1", "500", "Server Error");
+ headers.ReplaceOrAppendHeader("content-length", "3");
+ SendHeadersAndBody(headers, "bad");
+}
+
+void QuicSpdyServerStream::SendHeadersAndBody(
+ const BalsaHeaders& response_headers,
+ StringPiece body) {
+ // We only support SPDY and HTTP, and neither handles bidirectional streaming.
+ if (!read_side_closed()) {
+ CloseReadSide();
+ }
+
+ SpdyHeaderBlock header_block =
+ SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers);
+
+ WriteHeaders(header_block, body.empty(), NULL);
+
+ if (!body.empty()) {
+ WriteOrBufferData(body, true, NULL);
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_spdy_server_stream.h b/chromium/net/quic/quic_spdy_server_stream.h
new file mode 100644
index 00000000000..8a6808bd3bc
--- /dev/null
+++ b/chromium/net/quic/quic_spdy_server_stream.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
+#define NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "net/base/io_buffer.h"
+#include "net/quic/quic_data_stream.h"
+#include "net/quic/quic_protocol.h"
+#include "net/tools/balsa/balsa_headers.h"
+
+namespace net {
+
+class QuicSession;
+
+namespace test {
+class QuicSpdyServerStreamPeer;
+} // namespace test
+
+// All this does right now is aggregate data, and on fin, send an HTTP
+// response.
+class QuicSpdyServerStream : public QuicDataStream {
+ public:
+ QuicSpdyServerStream(QuicStreamId id, QuicSession* session);
+ virtual ~QuicSpdyServerStream();
+
+ // ReliableQuicStream implementation called by the session when there's
+ // data for us.
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE;
+ virtual void OnFinRead() OVERRIDE;
+
+ int ParseRequestHeaders();
+
+ private:
+ friend class test::QuicSpdyServerStreamPeer;
+
+ // Sends a basic 200 response using SendHeaders for the headers and WriteData
+ // for the body.
+ void SendResponse();
+
+ // Sends a basic 500 response using SendHeaders for the headers and WriteData
+ // for the body
+ void SendErrorResponse();
+
+ void SendHeadersAndBody(const BalsaHeaders& response_headers,
+ base::StringPiece body);
+
+ BalsaHeaders headers_;
+ string body_;
+
+ // Buffer into which response header data is read.
+ scoped_refptr<GrowableIOBuffer> read_buf_;
+ bool request_headers_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSpdyServerStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
diff --git a/chromium/net/quic/quic_stream_factory.cc b/chromium/net/quic/quic_stream_factory.cc
index 036a91d38aa..2fc1b9e334c 100644
--- a/chromium/net/quic/quic_stream_factory.cc
+++ b/chromium/net/quic/quic_stream_factory.cc
@@ -6,7 +6,7 @@
#include <set>
-#include "base/logging.h"
+#include "base/cpu.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/histogram.h"
@@ -22,6 +22,7 @@
#include "net/quic/congestion_control/tcp_receiver.h"
#include "net/quic/crypto/proof_verifier_chromium.h"
#include "net/quic/crypto/quic_random.h"
+#include "net/quic/crypto/quic_server_info.h"
#include "net/quic/port_suggester.h"
#include "net/quic/quic_client_session.h"
#include "net/quic/quic_clock.h"
@@ -31,24 +32,117 @@
#include "net/quic/quic_default_packet_writer.h"
#include "net/quic/quic_http_stream.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
#include "net/socket/client_socket_factory.h"
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
using std::string;
using std::vector;
namespace net {
+namespace {
+
+enum CreateSessionFailure {
+ CREATION_ERROR_CONNECTING_SOCKET,
+ CREATION_ERROR_SETTING_RECEIVE_BUFFER,
+ CREATION_ERROR_SETTING_SEND_BUFFER,
+ CREATION_ERROR_MAX
+};
+
+// When a connection is idle for 30 seconds it will be closed.
+const int kIdleConnectionTimeoutSeconds = 30;
+
+// The initial receive window size for both streams and sessions.
+const int32 kInitialReceiveWindowSize = 10 * 1024 * 1024; // 10MB
+
+// The suggested initial congestion windows for a server to use.
+// TODO: This should be tested and optimized, and even better, suggest a window
+// that corresponds to historical bandwidth and min-RTT.
+// Larger initial congestion windows can, if we don't overshoot, reduce latency
+// by avoiding the RTT needed for slow start to double (and re-double) from a
+// default of 10.
+// We match SPDY's use of 32 when secure (since we'd compete with SPDY).
+const int32 kServerSecureInitialCongestionWindow = 32;
+// Be conservative, and just use double a typical TCP ICWND for HTTP.
+const int32 kServerInecureInitialCongestionWindow = 20;
+
+void HistogramCreateSessionFailure(enum CreateSessionFailure error) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.CreationError", error,
+ CREATION_ERROR_MAX);
+}
+
+bool IsEcdsaSupported() {
+#if defined(OS_WIN)
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return false;
+#endif
+
+ return true;
+}
+
+QuicConfig InitializeQuicConfig(bool enable_pacing,
+ bool enable_time_based_loss_detection) {
+ QuicConfig config;
+ config.SetDefaults();
+ config.EnablePacing(enable_pacing);
+ if (enable_time_based_loss_detection)
+ config.SetLossDetectionToSend(kTIME);
+ config.set_idle_connection_state_lifetime(
+ QuicTime::Delta::FromSeconds(kIdleConnectionTimeoutSeconds),
+ QuicTime::Delta::FromSeconds(kIdleConnectionTimeoutSeconds));
+ return config;
+}
+
+} // namespace
+
+QuicStreamFactory::IpAliasKey::IpAliasKey() {}
+
+QuicStreamFactory::IpAliasKey::IpAliasKey(IPEndPoint ip_endpoint,
+ bool is_https)
+ : ip_endpoint(ip_endpoint),
+ is_https(is_https) {}
+
+QuicStreamFactory::IpAliasKey::~IpAliasKey() {}
+
+bool QuicStreamFactory::IpAliasKey::operator<(
+ const QuicStreamFactory::IpAliasKey& other) const {
+ if (!(ip_endpoint == other.ip_endpoint)) {
+ return ip_endpoint < other.ip_endpoint;
+ }
+ return is_https < other.is_https;
+}
+
+bool QuicStreamFactory::IpAliasKey::operator==(
+ const QuicStreamFactory::IpAliasKey& other) const {
+ return is_https == other.is_https &&
+ ip_endpoint == other.ip_endpoint;
+};
+
// Responsible for creating a new QUIC session to the specified server, and
// for notifying any associated requests when complete.
class QuicStreamFactory::Job {
public:
Job(QuicStreamFactory* factory,
HostResolver* host_resolver,
- const HostPortProxyPair& host_port_proxy_pair,
+ const HostPortPair& host_port_pair,
bool is_https,
- CertVerifier* cert_verifier,
+ bool was_alternate_protocol_recently_broken,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ QuicServerInfo* server_info,
const BoundNetLog& net_log);
+ // Creates a new job to handle the resumption of for connecting an
+ // existing session.
+ Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ QuicClientSession* session,
+ QuicServerId server_id);
+
~Job();
int Run(const CompletionCallback& callback);
@@ -56,7 +150,10 @@ class QuicStreamFactory::Job {
int DoLoop(int rv);
int DoResolveHost();
int DoResolveHostComplete(int rv);
+ int DoLoadServerInfo();
+ int DoLoadServerInfoComplete(int rv);
int DoConnect();
+ int DoResumeConnect();
int DoConnectComplete(int rv);
void OnIOComplete(int rv);
@@ -65,8 +162,8 @@ class QuicStreamFactory::Job {
return callback_;
}
- const HostPortProxyPair& host_port_proxy_pair() const {
- return host_port_proxy_pair_;
+ const QuicServerId server_id() const {
+ return server_id_;
}
private:
@@ -74,44 +171,68 @@ class QuicStreamFactory::Job {
STATE_NONE,
STATE_RESOLVE_HOST,
STATE_RESOLVE_HOST_COMPLETE,
+ STATE_LOAD_SERVER_INFO,
+ STATE_LOAD_SERVER_INFO_COMPLETE,
STATE_CONNECT,
+ STATE_RESUME_CONNECT,
STATE_CONNECT_COMPLETE,
};
IoState io_state_;
QuicStreamFactory* factory_;
SingleRequestHostResolver host_resolver_;
- const HostPortProxyPair host_port_proxy_pair_;
- bool is_https_;
- CertVerifier* cert_verifier_;
+ QuicServerId server_id_;
+ bool is_post_;
+ bool was_alternate_protocol_recently_broken_;
+ scoped_ptr<QuicServerInfo> server_info_;
const BoundNetLog net_log_;
QuicClientSession* session_;
CompletionCallback callback_;
AddressList address_list_;
+ base::TimeTicks disk_cache_load_start_time_;
+ base::WeakPtrFactory<Job> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Job);
};
-QuicStreamFactory::Job::Job(
- QuicStreamFactory* factory,
- HostResolver* host_resolver,
- const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
- const BoundNetLog& net_log)
- : factory_(factory),
+QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ const HostPortPair& host_port_pair,
+ bool is_https,
+ bool was_alternate_protocol_recently_broken,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ QuicServerInfo* server_info,
+ const BoundNetLog& net_log)
+ : io_state_(STATE_RESOLVE_HOST),
+ factory_(factory),
host_resolver_(host_resolver),
- host_port_proxy_pair_(host_port_proxy_pair),
- is_https_(is_https),
- cert_verifier_(cert_verifier),
+ server_id_(host_port_pair, is_https, privacy_mode),
+ is_post_(method == "POST"),
+ was_alternate_protocol_recently_broken_(
+ was_alternate_protocol_recently_broken),
+ server_info_(server_info),
net_log_(net_log),
- session_(NULL) {
-}
+ session_(NULL),
+ weak_factory_(this) {}
+
+QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ QuicClientSession* session,
+ QuicServerId server_id)
+ : io_state_(STATE_RESUME_CONNECT),
+ factory_(factory),
+ host_resolver_(host_resolver), // unused
+ server_id_(server_id),
+ is_post_(false), // unused
+ was_alternate_protocol_recently_broken_(false), // unused
+ net_log_(session->net_log()), // unused
+ session_(session),
+ weak_factory_(this) {}
QuicStreamFactory::Job::~Job() {
}
int QuicStreamFactory::Job::Run(const CompletionCallback& callback) {
- io_state_ = STATE_RESOLVE_HOST;
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
callback_ = callback;
@@ -131,10 +252,21 @@ int QuicStreamFactory::Job::DoLoop(int rv) {
case STATE_RESOLVE_HOST_COMPLETE:
rv = DoResolveHostComplete(rv);
break;
+ case STATE_LOAD_SERVER_INFO:
+ CHECK_EQ(OK, rv);
+ rv = DoLoadServerInfo();
+ break;
+ case STATE_LOAD_SERVER_INFO_COMPLETE:
+ rv = DoLoadServerInfoComplete(rv);
+ break;
case STATE_CONNECT:
CHECK_EQ(OK, rv);
rv = DoConnect();
break;
+ case STATE_RESUME_CONNECT:
+ CHECK_EQ(OK, rv);
+ rv = DoResumeConnect();
+ break;
case STATE_CONNECT_COMPLETE:
rv = DoConnectComplete(rv);
break;
@@ -155,12 +287,19 @@ void QuicStreamFactory::Job::OnIOComplete(int rv) {
}
int QuicStreamFactory::Job::DoResolveHost() {
+ // Start loading the data now, and wait for it after we resolve the host.
+ if (server_info_) {
+ disk_cache_load_start_time_ = base::TimeTicks::Now();
+ server_info_->Start();
+ }
+
io_state_ = STATE_RESOLVE_HOST_COMPLETE;
return host_resolver_.Resolve(
- HostResolver::RequestInfo(host_port_proxy_pair_.first),
+ HostResolver::RequestInfo(server_id_.host_port_pair()),
DEFAULT_PRIORITY,
&address_list_,
- base::Bind(&QuicStreamFactory::Job::OnIOComplete, base::Unretained(this)),
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ weak_factory_.GetWeakPtr()),
net_log_);
}
@@ -168,11 +307,97 @@ int QuicStreamFactory::Job::DoResolveHostComplete(int rv) {
if (rv != OK)
return rv;
- DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_));
+ DCHECK(!factory_->HasActiveSession(server_id_));
+
+ // Inform the factory of this resolution, which will set up
+ // a session alias, if possible.
+ if (factory_->OnResolution(server_id_, address_list_)) {
+ return OK;
+ }
+
+ io_state_ = STATE_LOAD_SERVER_INFO;
+ return OK;
+}
+
+int QuicStreamFactory::Job::DoLoadServerInfo() {
+ io_state_ = STATE_LOAD_SERVER_INFO_COMPLETE;
+
+ if (!server_info_)
+ return OK;
+
+ return server_info_->WaitForDataReady(
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) {
+ if (server_info_) {
+ UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheReadTime",
+ base::TimeTicks::Now() - disk_cache_load_start_time_);
+ }
+
+ if (rv != OK) {
+ server_info_.reset();
+ }
+
io_state_ = STATE_CONNECT;
return OK;
}
+int QuicStreamFactory::Job::DoConnect() {
+ io_state_ = STATE_CONNECT_COMPLETE;
+
+ int rv = factory_->CreateSession(server_id_, server_info_.Pass(),
+ address_list_, net_log_, &session_);
+ if (rv != OK) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(!session_);
+ return rv;
+ }
+
+ session_->StartReading();
+ if (!session_->connection()->connected()) {
+ return ERR_QUIC_PROTOCOL_ERROR;
+ }
+ bool require_confirmation =
+ factory_->require_confirmation() || is_post_ ||
+ was_alternate_protocol_recently_broken_;
+ rv = session_->CryptoConnect(
+ require_confirmation,
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ base::Unretained(this)));
+ return rv;
+}
+
+int QuicStreamFactory::Job::DoResumeConnect() {
+ io_state_ = STATE_CONNECT_COMPLETE;
+
+ int rv = session_->ResumeCryptoConnect(
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ base::Unretained(this)));
+
+ return rv;
+}
+
+int QuicStreamFactory::Job::DoConnectComplete(int rv) {
+ if (rv != OK)
+ return rv;
+
+ DCHECK(!factory_->HasActiveSession(server_id_));
+ // There may well now be an active session for this IP. If so, use the
+ // existing session instead.
+ AddressList address(session_->connection()->peer_address());
+ if (factory_->OnResolution(server_id_, address)) {
+ session_->connection()->SendConnectionClose(QUIC_CONNECTION_IP_POOLED);
+ session_ = NULL;
+ return OK;
+ }
+
+ factory_->ActivateSession(server_id_, session_);
+
+ return OK;
+}
+
QuicStreamRequest::QuicStreamRequest(QuicStreamFactory* factory)
: factory_(factory) {}
@@ -181,20 +406,20 @@ QuicStreamRequest::~QuicStreamRequest() {
factory_->CancelRequest(this);
}
-int QuicStreamRequest::Request(
- const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
- const BoundNetLog& net_log,
- const CompletionCallback& callback) {
+int QuicStreamRequest::Request(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ const BoundNetLog& net_log,
+ const CompletionCallback& callback) {
DCHECK(!stream_);
DCHECK(callback_.is_null());
- int rv = factory_->Create(host_port_proxy_pair, is_https, cert_verifier,
+ DCHECK(factory_);
+ int rv = factory_->Create(host_port_pair, is_https, privacy_mode, method,
net_log, this);
if (rv == ERR_IO_PENDING) {
- host_port_proxy_pair_ = host_port_proxy_pair;
+ host_port_pair_ = host_port_pair;
is_https_ = is_https;
- cert_verifier_ = cert_verifier;
net_log_ = net_log;
callback_ = callback;
} else {
@@ -220,103 +445,131 @@ scoped_ptr<QuicHttpStream> QuicStreamRequest::ReleaseStream() {
return stream_.Pass();
}
-int QuicStreamFactory::Job::DoConnect() {
- io_state_ = STATE_CONNECT_COMPLETE;
-
- int rv = factory_->CreateSession(host_port_proxy_pair_, is_https_,
- cert_verifier_, address_list_, net_log_, &session_);
- if (rv != OK) {
- DCHECK(rv != ERR_IO_PENDING);
- DCHECK(!session_);
- return rv;
- }
-
- session_->StartReading();
- rv = session_->CryptoConnect(
- factory_->require_confirmation() || is_https_,
- base::Bind(&QuicStreamFactory::Job::OnIOComplete,
- base::Unretained(this)));
- return rv;
-}
-
-int QuicStreamFactory::Job::DoConnectComplete(int rv) {
- if (rv != OK)
- return rv;
-
- DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_));
- factory_->ActivateSession(host_port_proxy_pair_, session_);
-
- return OK;
-}
-
QuicStreamFactory::QuicStreamFactory(
HostResolver* host_resolver,
ClientSocketFactory* client_socket_factory,
base::WeakPtr<HttpServerProperties> http_server_properties,
+ CertVerifier* cert_verifier,
QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory,
QuicRandom* random_generator,
QuicClock* clock,
- size_t max_packet_length)
+ size_t max_packet_length,
+ const std::string& user_agent_id,
+ const QuicVersionVector& supported_versions,
+ bool enable_port_selection,
+ bool enable_pacing,
+ bool enable_time_based_loss_detection)
: require_confirmation_(true),
host_resolver_(host_resolver),
client_socket_factory_(client_socket_factory),
http_server_properties_(http_server_properties),
+ cert_verifier_(cert_verifier),
+ quic_server_info_factory_(NULL),
quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory),
random_generator_(random_generator),
clock_(clock),
max_packet_length_(max_packet_length),
- weak_factory_(this),
- port_seed_(random_generator_->RandUint64()) {
- config_.SetDefaults();
- config_.set_idle_connection_state_lifetime(
- QuicTime::Delta::FromSeconds(30),
- QuicTime::Delta::FromSeconds(30));
-
- cannoncial_suffixes_.push_back(string(".c.youtube.com"));
- cannoncial_suffixes_.push_back(string(".googlevideo.com"));
+ config_(InitializeQuicConfig(enable_pacing,
+ enable_time_based_loss_detection)),
+ supported_versions_(supported_versions),
+ enable_port_selection_(enable_port_selection),
+ port_seed_(random_generator_->RandUint64()),
+ weak_factory_(this) {
+ crypto_config_.SetDefaults();
+ crypto_config_.set_user_agent_id(user_agent_id);
+ crypto_config_.AddCanonicalSuffix(".c.youtube.com");
+ crypto_config_.AddCanonicalSuffix(".googlevideo.com");
+ crypto_config_.SetProofVerifier(new ProofVerifierChromium(cert_verifier));
+ base::CPU cpu;
+ if (cpu.has_aesni() && cpu.has_avx())
+ crypto_config_.PreferAesGcm();
+ if (!IsEcdsaSupported())
+ crypto_config_.DisableEcdsa();
}
QuicStreamFactory::~QuicStreamFactory() {
CloseAllSessions(ERR_ABORTED);
- STLDeleteElements(&all_sessions_);
+ while (!all_sessions_.empty()) {
+ delete all_sessions_.begin()->first;
+ all_sessions_.erase(all_sessions_.begin());
+ }
STLDeleteValues(&active_jobs_);
- STLDeleteValues(&all_crypto_configs_);
}
-int QuicStreamFactory::Create(const HostPortProxyPair& host_port_proxy_pair,
+int QuicStreamFactory::Create(const HostPortPair& host_port_pair,
bool is_https,
- CertVerifier* cert_verifier,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
const BoundNetLog& net_log,
QuicStreamRequest* request) {
- if (HasActiveSession(host_port_proxy_pair)) {
- request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log));
+ QuicServerId server_id(host_port_pair, is_https, privacy_mode);
+ if (HasActiveSession(server_id)) {
+ request->set_stream(CreateIfSessionExists(server_id, net_log));
return OK;
}
- if (HasActiveJob(host_port_proxy_pair)) {
- Job* job = active_jobs_[host_port_proxy_pair];
+ if (HasActiveJob(server_id)) {
+ Job* job = active_jobs_[server_id];
active_requests_[request] = job;
job_requests_map_[job].insert(request);
return ERR_IO_PENDING;
}
- scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_proxy_pair,
- is_https, cert_verifier, net_log));
+ QuicServerInfo* quic_server_info = NULL;
+ if (quic_server_info_factory_) {
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_.LookupOrCreate(server_id);
+ DCHECK(cached);
+ if (cached->IsEmpty()) {
+ quic_server_info = quic_server_info_factory_->GetForServer(server_id);
+ }
+ }
+ bool was_alternate_protocol_recently_broken =
+ http_server_properties_ &&
+ http_server_properties_->WasAlternateProtocolRecentlyBroken(
+ server_id.host_port_pair());
+ scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_pair, is_https,
+ was_alternate_protocol_recently_broken,
+ privacy_mode, method, quic_server_info, net_log));
int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
base::Unretained(this), job.get()));
if (rv == ERR_IO_PENDING) {
active_requests_[request] = job.get();
job_requests_map_[job.get()].insert(request);
- active_jobs_[host_port_proxy_pair] = job.release();
+ active_jobs_[server_id] = job.release();
}
if (rv == OK) {
- DCHECK(HasActiveSession(host_port_proxy_pair));
- request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log));
+ DCHECK(HasActiveSession(server_id));
+ request->set_stream(CreateIfSessionExists(server_id, net_log));
}
return rv;
}
+bool QuicStreamFactory::OnResolution(
+ const QuicServerId& server_id,
+ const AddressList& address_list) {
+ DCHECK(!HasActiveSession(server_id));
+ for (size_t i = 0; i < address_list.size(); ++i) {
+ const IPEndPoint& address = address_list[i];
+ const IpAliasKey ip_alias_key(address, server_id.is_https());
+ if (!ContainsKey(ip_aliases_, ip_alias_key))
+ continue;
+
+ const SessionSet& sessions = ip_aliases_[ip_alias_key];
+ for (SessionSet::const_iterator i = sessions.begin();
+ i != sessions.end(); ++i) {
+ QuicClientSession* session = *i;
+ if (!session->CanPool(server_id.host()))
+ continue;
+ active_sessions_[server_id] = session;
+ session_aliases_[session].insert(server_id);
+ return true;
+ }
+ }
+ return false;
+}
+
void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
if (rv == OK) {
require_confirmation_ = false;
@@ -324,8 +577,8 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
// Create all the streams, but do not notify them yet.
for (RequestSet::iterator it = job_requests_map_[job].begin();
it != job_requests_map_[job].end() ; ++it) {
- DCHECK(HasActiveSession(job->host_port_proxy_pair()));
- (*it)->set_stream(CreateIfSessionExists(job->host_port_proxy_pair(),
+ DCHECK(HasActiveSession(job->server_id()));
+ (*it)->set_stream(CreateIfSessionExists(job->server_id(),
(*it)->net_log()));
}
}
@@ -339,7 +592,7 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
// profile which can not be deleted via callbacks.
request->OnRequestComplete(rv);
}
- active_jobs_.erase(job->host_port_proxy_pair());
+ active_jobs_.erase(job->server_id());
job_requests_map_.erase(job);
delete job;
return;
@@ -348,14 +601,14 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
// Returns a newly created QuicHttpStream owned by the caller, if a
// matching session already exists. Returns NULL otherwise.
scoped_ptr<QuicHttpStream> QuicStreamFactory::CreateIfSessionExists(
- const HostPortProxyPair& host_port_proxy_pair,
+ const QuicServerId& server_id,
const BoundNetLog& net_log) {
- if (!HasActiveSession(host_port_proxy_pair)) {
+ if (!HasActiveSession(server_id)) {
DVLOG(1) << "No active session";
return scoped_ptr<QuicHttpStream>();
}
- QuicClientSession* session = active_sessions_[host_port_proxy_pair];
+ QuicClientSession* session = active_sessions_[server_id];
DCHECK(session);
return scoped_ptr<QuicHttpStream>(
new QuicHttpStream(session->GetWeakPtr()));
@@ -370,13 +623,22 @@ void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) {
++it) {
DCHECK(active_sessions_.count(*it));
DCHECK_EQ(session, active_sessions_[*it]);
+ // Track sessions which have recently gone away so that we can disable
+ // port suggestions.
+ if (session->goaway_received()) {
+ gone_away_aliases_.insert(*it);
+ }
+
active_sessions_.erase(*it);
- if (!session->IsCryptoHandshakeConfirmed() && http_server_properties_) {
- // TODO(rch): In the special case where the session has received no
- // packets from the peer, we should consider blacklisting this
- // differently so that we still race TCP but we don't consider the
- // session connected until the handshake has been confirmed.
- http_server_properties_->SetBrokenAlternateProtocol(it->first);
+ ProcessGoingAwaySession(session, *it, true);
+ }
+ ProcessGoingAwaySession(session, all_sessions_[session], false);
+ if (!aliases.empty()) {
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ aliases.begin()->is_https());
+ ip_aliases_[ip_alias_key].erase(session);
+ if (ip_aliases_[ip_alias_key].empty()) {
+ ip_aliases_.erase(ip_alias_key);
}
}
session_aliases_.erase(session);
@@ -385,8 +647,37 @@ void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) {
void QuicStreamFactory::OnSessionClosed(QuicClientSession* session) {
DCHECK_EQ(0u, session->GetNumOpenStreams());
OnSessionGoingAway(session);
- all_sessions_.erase(session);
delete session;
+ all_sessions_.erase(session);
+}
+
+void QuicStreamFactory::OnSessionConnectTimeout(
+ QuicClientSession* session) {
+ const AliasSet& aliases = session_aliases_[session];
+ for (AliasSet::const_iterator it = aliases.begin(); it != aliases.end();
+ ++it) {
+ DCHECK(active_sessions_.count(*it));
+ DCHECK_EQ(session, active_sessions_[*it]);
+ active_sessions_.erase(*it);
+ }
+
+ if (aliases.empty()) {
+ return;
+ }
+
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ aliases.begin()->is_https());
+ ip_aliases_[ip_alias_key].erase(session);
+ if (ip_aliases_[ip_alias_key].empty()) {
+ ip_aliases_.erase(ip_alias_key);
+ }
+ QuicServerId server_id = *aliases.begin();
+ session_aliases_.erase(session);
+ Job* job = new Job(this, host_resolver_, session, server_id);
+ active_jobs_[server_id] = job;
+ int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
+ base::Unretained(this), job));
+ DCHECK_EQ(ERR_IO_PENDING, rv);
}
void QuicStreamFactory::CancelRequest(QuicStreamRequest* request) {
@@ -404,7 +695,7 @@ void QuicStreamFactory::CloseAllSessions(int error) {
}
while (!all_sessions_.empty()) {
size_t initial_size = all_sessions_.size();
- (*all_sessions_.begin())->CloseSessionOnError(error);
+ all_sessions_.begin()->first->CloseSessionOnError(error);
DCHECK_NE(initial_size, all_sessions_.size());
}
DCHECK(all_sessions_.empty());
@@ -415,14 +706,26 @@ base::Value* QuicStreamFactory::QuicStreamFactoryInfoToValue() const {
for (SessionMap::const_iterator it = active_sessions_.begin();
it != active_sessions_.end(); ++it) {
- const HostPortProxyPair& pair = it->first;
- const QuicClientSession* session = it->second;
-
- list->Append(session->GetInfoAsValue(pair.first));
+ const QuicServerId& server_id = it->first;
+ QuicClientSession* session = it->second;
+ const AliasSet& aliases = session_aliases_.find(session)->second;
+ // Only add a session to the list once.
+ if (server_id == *aliases.begin()) {
+ std::set<HostPortPair> hosts;
+ for (AliasSet::const_iterator alias_it = aliases.begin();
+ alias_it != aliases.end(); ++alias_it) {
+ hosts.insert(alias_it->host_port_pair());
+ }
+ list->Append(session->GetInfoAsValue(hosts));
+ }
}
return list;
}
+void QuicStreamFactory::ClearCachedStatesInCryptoConfig() {
+ crypto_config_.ClearCachedStates();
+}
+
void QuicStreamFactory::OnIPAddressChanged() {
CloseAllSessions(ERR_NETWORK_CHANGED);
require_confirmation_ = true;
@@ -446,54 +749,69 @@ void QuicStreamFactory::OnCACertChanged(const X509Certificate* cert) {
}
bool QuicStreamFactory::HasActiveSession(
- const HostPortProxyPair& host_port_proxy_pair) {
- return ContainsKey(active_sessions_, host_port_proxy_pair);
+ const QuicServerId& server_id) const {
+ return ContainsKey(active_sessions_, server_id);
}
int QuicStreamFactory::CreateSession(
- const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
+ const QuicServerId& server_id,
+ scoped_ptr<QuicServerInfo> server_info,
const AddressList& address_list,
const BoundNetLog& net_log,
QuicClientSession** session) {
- QuicGuid guid = random_generator_->RandUint64();
+ bool enable_port_selection = enable_port_selection_;
+ if (enable_port_selection &&
+ ContainsKey(gone_away_aliases_, server_id)) {
+ // Disable port selection when the server is going away.
+ // There is no point in trying to return to the same server, if
+ // that server is no longer handling requests.
+ enable_port_selection = false;
+ gone_away_aliases_.erase(server_id);
+ }
+
+ QuicConnectionId connection_id = random_generator_->RandUint64();
IPEndPoint addr = *address_list.begin();
scoped_refptr<PortSuggester> port_suggester =
- new PortSuggester(host_port_proxy_pair.first, port_seed_);
- DatagramSocket::BindType bind_type = DatagramSocket::RANDOM_BIND;
-#if defined(OS_WIN)
- // TODO(jar)bug=329255 Provide better implementation to avoid pop-up warning.
- bind_type = DatagramSocket::DEFAULT_BIND;
-#endif
+ new PortSuggester(server_id.host_port_pair(), port_seed_);
+ DatagramSocket::BindType bind_type = enable_port_selection ?
+ DatagramSocket::RANDOM_BIND : // Use our callback.
+ DatagramSocket::DEFAULT_BIND; // Use OS to randomize.
scoped_ptr<DatagramClientSocket> socket(
client_socket_factory_->CreateDatagramClientSocket(
bind_type,
base::Bind(&PortSuggester::SuggestPort, port_suggester),
net_log.net_log(), net_log.source()));
int rv = socket->Connect(addr);
- if (rv != OK)
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_CONNECTING_SOCKET);
return rv;
+ }
UMA_HISTOGRAM_COUNTS("Net.QuicEphemeralPortsSuggested",
port_suggester->call_count());
-
-#if defined(OS_WIN)
- // TODO(jar)bug=329255 Provide better implementation to avoid pop-up warning.
- DCHECK_EQ(0u, port_suggester->call_count());
-#else
- DCHECK_LE(1u, port_suggester->call_count());
-#endif
+ if (enable_port_selection) {
+ DCHECK_LE(1u, port_suggester->call_count());
+ } else {
+ DCHECK_EQ(0u, port_suggester->call_count());
+ }
// We should adaptively set this buffer size, but for now, we'll use a size
// that is more than large enough for a full receive window, and yet
// does not consume "too much" memory. If we see bursty packet loss, we may
// revisit this setting and test for its impact.
const int32 kSocketBufferSize(TcpReceiver::kReceiveWindowTCP);
- socket->SetReceiveBufferSize(kSocketBufferSize);
+ rv = socket->SetReceiveBufferSize(kSocketBufferSize);
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_SETTING_RECEIVE_BUFFER);
+ return rv;
+ }
// Set a buffer large enough to contain the initial CWND's worth of packet
// to work around the problem with CHLO packets being sent out with the
// wrong encryption level, when the send buffer is full.
- socket->SetSendBufferSize(kMaxPacketSize * 20); // Support 20 packets.
+ rv = socket->SetSendBufferSize(kMaxPacketSize * 20);
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_SETTING_SEND_BUFFER);
+ return rv;
+ }
scoped_ptr<QuicDefaultPacketWriter> writer(
new QuicDefaultPacketWriter(socket.get()));
@@ -504,96 +822,134 @@ int QuicStreamFactory::CreateSession(
clock_.get(), random_generator_));
}
- QuicConnection* connection = new QuicConnection(guid, addr, helper_.get(),
- writer.get(), false,
- QuicSupportedVersions());
+ QuicConnection* connection =
+ new QuicConnection(connection_id, addr, helper_.get(), writer.get(),
+ false, supported_versions_);
writer->SetConnection(connection);
- connection->options()->max_packet_length = max_packet_length_;
-
- QuicCryptoClientConfig* crypto_config =
- GetOrCreateCryptoConfig(host_port_proxy_pair);
- DCHECK(crypto_config);
+ connection->set_max_packet_length(max_packet_length_);
+
+ InitializeCachedStateInCryptoConfig(server_id, server_info);
+
+ QuicConfig config = config_;
+ config.SetInitialCongestionWindowToSend(
+ server_id.is_https() ? kServerSecureInitialCongestionWindow
+ : kServerInecureInitialCongestionWindow);
+ config.SetInitialFlowControlWindowToSend(kInitialReceiveWindowSize);
+ config.SetInitialStreamFlowControlWindowToSend(kInitialReceiveWindowSize);
+ config.SetInitialSessionFlowControlWindowToSend(kInitialReceiveWindowSize);
+ if (http_server_properties_) {
+ const HttpServerProperties::NetworkStats* stats =
+ http_server_properties_->GetServerNetworkStats(
+ server_id.host_port_pair());
+ if (stats != NULL) {
+ config.SetInitialRoundTripTimeUsToSend(stats->srtt.InMicroseconds());
+ }
+ }
*session = new QuicClientSession(
connection, socket.Pass(), writer.Pass(), this,
- quic_crypto_client_stream_factory_, host_port_proxy_pair.first.host(),
- config_, crypto_config, net_log.net_log());
- all_sessions_.insert(*session); // owning pointer
- if (is_https) {
- crypto_config->SetProofVerifier(
- new ProofVerifierChromium(cert_verifier, net_log));
- }
+ quic_crypto_client_stream_factory_, server_info.Pass(), server_id,
+ config, &crypto_config_,
+ base::MessageLoop::current()->message_loop_proxy().get(),
+ net_log.net_log());
+ all_sessions_[*session] = server_id; // owning pointer
return OK;
}
-bool QuicStreamFactory::HasActiveJob(
- const HostPortProxyPair& host_port_proxy_pair) {
- return ContainsKey(active_jobs_, host_port_proxy_pair);
+bool QuicStreamFactory::HasActiveJob(const QuicServerId& key) const {
+ return ContainsKey(active_jobs_, key);
}
void QuicStreamFactory::ActivateSession(
- const HostPortProxyPair& host_port_proxy_pair,
+ const QuicServerId& server_id,
QuicClientSession* session) {
- DCHECK(!HasActiveSession(host_port_proxy_pair));
- active_sessions_[host_port_proxy_pair] = session;
- session_aliases_[session].insert(host_port_proxy_pair);
-}
+ DCHECK(!HasActiveSession(server_id));
+ UMA_HISTOGRAM_COUNTS("Net.QuicActiveSessions", active_sessions_.size());
+ active_sessions_[server_id] = session;
+ session_aliases_[session].insert(server_id);
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ server_id.is_https());
+ DCHECK(!ContainsKey(ip_aliases_[ip_alias_key], session));
+ ip_aliases_[ip_alias_key].insert(session);
+}
+
+void QuicStreamFactory::InitializeCachedStateInCryptoConfig(
+ const QuicServerId& server_id,
+ const scoped_ptr<QuicServerInfo>& server_info) {
+ if (!server_info)
+ return;
-QuicCryptoClientConfig* QuicStreamFactory::GetOrCreateCryptoConfig(
- const HostPortProxyPair& host_port_proxy_pair) {
- QuicCryptoClientConfig* crypto_config;
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_.LookupOrCreate(server_id);
+ if (!cached->IsEmpty())
+ return;
- if (ContainsKey(all_crypto_configs_, host_port_proxy_pair)) {
- crypto_config = all_crypto_configs_[host_port_proxy_pair];
- DCHECK(crypto_config);
- } else {
- // TODO(rtenneti): if two quic_sessions for the same host_port_proxy_pair
- // share the same crypto_config, will it cause issues?
- crypto_config = new QuicCryptoClientConfig();
- crypto_config->SetDefaults();
- all_crypto_configs_[host_port_proxy_pair] = crypto_config;
- PopulateFromCanonicalConfig(host_port_proxy_pair, crypto_config);
+ if (!cached->Initialize(server_info->state().server_config,
+ server_info->state().source_address_token,
+ server_info->state().certs,
+ server_info->state().server_config_sig,
+ clock_->WallNow()))
+ return;
+
+ if (!server_id.is_https()) {
+ // Don't check the certificates for insecure QUIC.
+ cached->SetProofValid();
}
- return crypto_config;
}
-void QuicStreamFactory::PopulateFromCanonicalConfig(
- const HostPortProxyPair& host_port_proxy_pair,
- QuicCryptoClientConfig* crypto_config) {
- const string server_hostname = host_port_proxy_pair.first.host();
-
- unsigned i = 0;
- for (; i < cannoncial_suffixes_.size(); ++i) {
- if (EndsWith(server_hostname, cannoncial_suffixes_[i], false)) {
- break;
- }
- }
- if (i == cannoncial_suffixes_.size())
+void QuicStreamFactory::ProcessGoingAwaySession(
+ QuicClientSession* session,
+ const QuicServerId& server_id,
+ bool session_was_active) {
+ if (!http_server_properties_)
return;
- HostPortPair canonical_host_port(cannoncial_suffixes_[i],
- host_port_proxy_pair.first.port());
- if (!ContainsKey(canonical_hostname_to_origin_map_, canonical_host_port)) {
- // This is the first host we've seen which matches the suffix, so make it
- // canonical.
- canonical_hostname_to_origin_map_[canonical_host_port] =
- host_port_proxy_pair;
+ const QuicConnectionStats& stats = session->connection()->GetStats();
+ if (session->IsCryptoHandshakeConfirmed()) {
+ HttpServerProperties::NetworkStats network_stats;
+ network_stats.srtt = base::TimeDelta::FromMicroseconds(stats.srtt_us);
+ network_stats.bandwidth_estimate = stats.estimated_bandwidth;
+ http_server_properties_->SetServerNetworkStats(server_id.host_port_pair(),
+ network_stats);
return;
}
- const HostPortProxyPair& canonical_host_port_proxy_pair =
- canonical_hostname_to_origin_map_[canonical_host_port];
- QuicCryptoClientConfig* canonical_crypto_config =
- all_crypto_configs_[canonical_host_port_proxy_pair];
- DCHECK(canonical_crypto_config);
+ UMA_HISTOGRAM_COUNTS("Net.QuicHandshakeNotConfirmedNumPacketsReceived",
+ stats.packets_received);
+
+ if (!session_was_active)
+ return;
+
+ const HostPortPair& server = server_id.host_port_pair();
+ // Don't try to change the alternate-protocol state, if the
+ // alternate-protocol state is unknown.
+ if (!http_server_properties_->HasAlternateProtocol(server))
+ return;
- // Copy the CachedState for the canonical server from canonical_crypto_config
- // as the initial CachedState for the server_hostname in crypto_config.
- crypto_config->InitializeFrom(server_hostname,
- canonical_host_port_proxy_pair.first.host(),
- canonical_crypto_config);
- // Update canonical version to point at the "most recent" crypto_config.
- canonical_hostname_to_origin_map_[canonical_host_port] = host_port_proxy_pair;
+ // TODO(rch): In the special case where the session has received no
+ // packets from the peer, we should consider blacklisting this
+ // differently so that we still race TCP but we don't consider the
+ // session connected until the handshake has been confirmed.
+ HistogramBrokenAlternateProtocolLocation(
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_QUIC_STREAM_FACTORY);
+ PortAlternateProtocolPair alternate =
+ http_server_properties_->GetAlternateProtocol(server);
+ DCHECK_EQ(QUIC, alternate.protocol);
+
+ // Since the session was active, there's no longer an
+ // HttpStreamFactoryImpl::Job running which can mark it broken, unless the
+ // TCP job also fails. So to avoid not using QUIC when we otherwise could,
+ // we mark it as broken, and then immediately re-enable it. This leaves
+ // QUIC as "recently broken" which means that 0-RTT will be disabled but
+ // we'll still race.
+ http_server_properties_->SetBrokenAlternateProtocol(server);
+ http_server_properties_->ClearAlternateProtocol(server);
+ http_server_properties_->SetAlternateProtocol(
+ server, alternate.port, alternate.protocol);
+ DCHECK_EQ(QUIC,
+ http_server_properties_->GetAlternateProtocol(server).protocol);
+ DCHECK(http_server_properties_->WasAlternateProtocolRecentlyBroken(
+ server));
}
} // namespace net
diff --git a/chromium/net/quic/quic_stream_factory.h b/chromium/net/quic/quic_stream_factory.h
index e55c83c3cbe..eb82659bbf8 100644
--- a/chromium/net/quic/quic_stream_factory.h
+++ b/chromium/net/quic/quic_stream_factory.h
@@ -5,10 +5,12 @@
#ifndef NET_QUIC_QUIC_STREAM_FACTORY_H_
#define NET_QUIC_QUIC_STREAM_FACTORY_H_
+#include <list>
#include <map>
#include <string>
#include <vector>
+#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "net/base/address_list.h"
#include "net/base/completion_callback.h"
@@ -33,6 +35,8 @@ class QuicClientSession;
class QuicConnectionHelper;
class QuicCryptoClientStreamFactory;
class QuicRandom;
+class QuicServerInfoFactory;
+class QuicServerId;
class QuicStreamFactory;
namespace test {
@@ -48,9 +52,10 @@ class NET_EXPORT_PRIVATE QuicStreamRequest {
~QuicStreamRequest();
// For http, |is_https| is false and |cert_verifier| can be null.
- int Request(const HostPortProxyPair& host_port_proxy_pair,
+ int Request(const HostPortPair& host_port_pair,
bool is_https,
- CertVerifier* cert_verifier,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
const BoundNetLog& net_log,
const CompletionCallback& callback);
@@ -66,9 +71,8 @@ class NET_EXPORT_PRIVATE QuicStreamRequest {
private:
QuicStreamFactory* factory_;
- HostPortProxyPair host_port_proxy_pair_;
+ HostPortPair host_port_pair_;
bool is_https_;
- CertVerifier* cert_verifier_;
BoundNetLog net_log_;
CompletionCallback callback_;
scoped_ptr<QuicHttpStream> stream_;
@@ -86,31 +90,32 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
HostResolver* host_resolver,
ClientSocketFactory* client_socket_factory,
base::WeakPtr<HttpServerProperties> http_server_properties,
+ CertVerifier* cert_verifier,
QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory,
QuicRandom* random_generator,
QuicClock* clock,
- size_t max_packet_length);
+ size_t max_packet_length,
+ const std::string& user_agent_id,
+ const QuicVersionVector& supported_versions,
+ bool enable_port_selection,
+ bool enable_pacing,
+ bool enable_time_based_loss_detection);
virtual ~QuicStreamFactory();
- // Creates a new QuicHttpStream to |host_port_proxy_pair| which will be
+ // Creates a new QuicHttpStream to |host_port_pair| which will be
// owned by |request|. |is_https| specifies if the protocol is https or not.
// |cert_verifier| is used by ProofVerifier for verifying the certificate
// chain and signature. For http, this can be null. If a matching session
// already exists, this method will return OK. If no matching session exists,
// this will return ERR_IO_PENDING and will invoke OnRequestComplete
// asynchronously.
- int Create(const HostPortProxyPair& host_port_proxy_pair,
+ int Create(const HostPortPair& host_port_pair,
bool is_https,
- CertVerifier* cert_verifier,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
const BoundNetLog& net_log,
QuicStreamRequest* request);
- // Returns a newly created QuicHttpStream owned by the caller, if a
- // matching session already exists. Returns NULL otherwise.
- scoped_ptr<QuicHttpStream> CreateIfSessionExists(
- const HostPortProxyPair& host_port_proxy_pair,
- const BoundNetLog& net_log);
-
// Called by a session when it becomes idle.
void OnIdleSession(QuicClientSession* session);
@@ -121,6 +126,9 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
// Called by a session after it shuts down.
void OnSessionClosed(QuicClientSession* session);
+ // Called by a session whose connection has timed out.
+ void OnSessionConnectTimeout(QuicClientSession* session);
+
// Cancels a pending request.
void CancelRequest(QuicStreamRequest* request);
@@ -129,6 +137,9 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
base::Value* QuicStreamFactoryInfoToValue() const;
+ // Delete all cached state objects in |crypto_config_|.
+ void ClearCachedStatesInCryptoConfig();
+
// NetworkChangeNotifier::IPAddressObserver methods:
// Until the servers support roaming, close all connections when the local
@@ -149,48 +160,83 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
QuicConnectionHelper* helper() { return helper_.get(); }
+ bool enable_port_selection() const { return enable_port_selection_; }
+
+ bool has_quic_server_info_factory() {
+ return quic_server_info_factory_ != NULL;
+ }
+
+ void set_quic_server_info_factory(
+ QuicServerInfoFactory* quic_server_info_factory) {
+ DCHECK(!quic_server_info_factory_);
+ quic_server_info_factory_ = quic_server_info_factory;
+ }
+
private:
class Job;
friend class test::QuicStreamFactoryPeer;
- typedef std::map<HostPortProxyPair, QuicClientSession*> SessionMap;
- typedef std::set<HostPortProxyPair> AliasSet;
+ // The key used to find session by ip. Includes
+ // the ip address, port, and scheme.
+ struct NET_EXPORT_PRIVATE IpAliasKey {
+ IpAliasKey();
+ IpAliasKey(IPEndPoint ip_endpoint, bool is_https);
+ ~IpAliasKey();
+
+ IPEndPoint ip_endpoint;
+ bool is_https;
+
+ // Needed to be an element of std::set.
+ bool operator<(const IpAliasKey &other) const;
+ bool operator==(const IpAliasKey &other) const;
+ };
+
+ typedef std::map<QuicServerId, QuicClientSession*> SessionMap;
+ typedef std::map<QuicClientSession*, QuicServerId> SessionIdMap;
+ typedef std::set<QuicServerId> AliasSet;
typedef std::map<QuicClientSession*, AliasSet> SessionAliasMap;
typedef std::set<QuicClientSession*> SessionSet;
- typedef std::map<HostPortProxyPair, QuicCryptoClientConfig*> CryptoConfigMap;
- typedef std::map<HostPortPair, HostPortProxyPair> CanonicalHostMap;
- typedef std::map<HostPortProxyPair, Job*> JobMap;
+ typedef std::map<IpAliasKey, SessionSet> IPAliasMap;
+ typedef std::map<QuicServerId, QuicCryptoClientConfig*> CryptoConfigMap;
+ typedef std::map<QuicServerId, Job*> JobMap;
typedef std::map<QuicStreamRequest*, Job*> RequestMap;
typedef std::set<QuicStreamRequest*> RequestSet;
typedef std::map<Job*, RequestSet> JobRequestsMap;
+ // Returns a newly created QuicHttpStream owned by the caller, if a
+ // matching session already exists. Returns NULL otherwise.
+ scoped_ptr<QuicHttpStream> CreateIfSessionExists(const QuicServerId& key,
+ const BoundNetLog& net_log);
+
+ bool OnResolution(const QuicServerId& server_id,
+ const AddressList& address_list);
void OnJobComplete(Job* job, int rv);
- bool HasActiveSession(const HostPortProxyPair& host_port_proxy_pair);
- bool HasActiveJob(const HostPortProxyPair& host_port_proxy_pair);
- int CreateSession(const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
+ bool HasActiveSession(const QuicServerId& server_id) const;
+ bool HasActiveJob(const QuicServerId& server_id) const;
+ int CreateSession(const QuicServerId& server_id,
+ scoped_ptr<QuicServerInfo> quic_server_info,
const AddressList& address_list,
const BoundNetLog& net_log,
QuicClientSession** session);
- void ActivateSession(const HostPortProxyPair& host_port_proxy_pair,
+ void ActivateSession(const QuicServerId& key,
QuicClientSession* session);
- QuicCryptoClientConfig* GetOrCreateCryptoConfig(
- const HostPortProxyPair& host_port_proxy_pair);
+ // Initializes the cached state associated with |server_id| in
+ // |crypto_config_| with the information in |server_info|.
+ void InitializeCachedStateInCryptoConfig(
+ const QuicServerId& server_id,
+ const scoped_ptr<QuicServerInfo>& server_info);
- // If |host_port_proxy_pair| suffix contains ".c.youtube.com" (in future we
- // could support other suffixes), then populate |crypto_config| with a
- // canonical server config data from |canonical_hostname_to_origin_map_| for
- // that suffix.
- void PopulateFromCanonicalConfig(
- const HostPortProxyPair& host_port_proxy_pair,
- QuicCryptoClientConfig* crypto_config);
+ void ProcessGoingAwaySession(QuicClientSession* session,
+ const QuicServerId& server_id,
+ bool was_session_active);
bool require_confirmation_;
HostResolver* host_resolver_;
ClientSocketFactory* client_socket_factory_;
base::WeakPtr<HttpServerProperties> http_server_properties_;
+ CertVerifier* cert_verifier_;
+ QuicServerInfoFactory* quic_server_info_factory_;
QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory_;
QuicRandom* random_generator_;
scoped_ptr<QuicClock> clock_;
@@ -200,35 +246,31 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
scoped_ptr<QuicConnectionHelper> helper_;
// Contains owning pointers to all sessions that currently exist.
- SessionSet all_sessions_;
+ SessionIdMap all_sessions_;
// Contains non-owning pointers to currently active session
// (not going away session, once they're implemented).
SessionMap active_sessions_;
+ // Map from session to set of aliases that this session is known by.
SessionAliasMap session_aliases_;
+ // Map from IP address to sessions which are connected to this address.
+ IPAliasMap ip_aliases_;
- // Contains owning pointers to QuicCryptoClientConfig. QuicCryptoClientConfig
- // contains configuration and cached state about servers.
- // TODO(rtenneti): Persist all_crypto_configs_ to disk and decide when to
- // clear the data in the map.
- CryptoConfigMap all_crypto_configs_;
+ // Origins which have gone away recently.
+ AliasSet gone_away_aliases_;
- // Contains a map of servers which could share the same server config. Map
- // from a Canonical host/port (host is some postfix of host names) to an
- // actual origin, which has a plausible set of initial certificates (or at
- // least server public key).
- CanonicalHostMap canonical_hostname_to_origin_map_;
-
- // Contains list of suffixes (for exmaple ".c.youtube.com",
- // ".googlevideo.com") of cannoncial hostnames.
- std::vector<std::string> cannoncial_suffixes_;
-
- QuicConfig config_;
+ const QuicConfig config_;
+ QuicCryptoClientConfig crypto_config_;
JobMap active_jobs_;
JobRequestsMap job_requests_map_;
RequestMap active_requests_;
- base::WeakPtrFactory<QuicStreamFactory> weak_factory_;
+ QuicVersionVector supported_versions_;
+
+ // Determine if we should consistently select a client UDP port. If false,
+ // then we will just let the OS select a random client port for each new
+ // connection.
+ bool enable_port_selection_;
// Each profile will (probably) have a unique port_seed_ value. This value is
// used to help seed a pseudo-random number generator (PortSuggester) so that
@@ -238,6 +280,8 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
// port requests.
uint64 port_seed_;
+ base::WeakPtrFactory<QuicStreamFactory> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(QuicStreamFactory);
};
diff --git a/chromium/net/quic/quic_stream_factory_test.cc b/chromium/net/quic/quic_stream_factory_test.cc
index 03cc6f7e8bc..e88ea4b8052 100644
--- a/chromium/net/quic/quic_stream_factory_test.cc
+++ b/chromium/net/quic/quic_stream_factory_test.cc
@@ -6,20 +6,25 @@
#include "base/run_loop.h"
#include "base/strings/string_util.h"
+#include "net/base/test_data_directory.h"
#include "net/cert/cert_verifier.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/http/http_util.h"
#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/proof_verifier_chromium.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_http_stream.h"
+#include "net/quic/quic_server_id.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_packet_maker.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/socket/socket_test_util.h"
+#include "net/test/cert_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece;
@@ -29,152 +34,169 @@ using std::vector;
namespace net {
namespace test {
+namespace {
+const char kDefaultServerHostName[] = "www.google.com";
+const int kDefaultServerPort = 443;
+} // namespace anonymous
+
class QuicStreamFactoryPeer {
public:
- static QuicCryptoClientConfig* GetOrCreateCryptoConfig(
- QuicStreamFactory* factory,
- const HostPortProxyPair& host_port_proxy_pair) {
- return factory->GetOrCreateCryptoConfig(host_port_proxy_pair);
+ static QuicCryptoClientConfig* GetCryptoConfig(QuicStreamFactory* factory) {
+ return &factory->crypto_config_;
}
static bool HasActiveSession(QuicStreamFactory* factory,
- const HostPortProxyPair& host_port_proxy_pair) {
- return factory->HasActiveSession(host_port_proxy_pair);
+ const HostPortPair& host_port_pair,
+ bool is_https) {
+ QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED);
+ return factory->HasActiveSession(server_id);
}
static QuicClientSession* GetActiveSession(
QuicStreamFactory* factory,
- const HostPortProxyPair& host_port_proxy_pair) {
- DCHECK(factory->HasActiveSession(host_port_proxy_pair));
- return factory->active_sessions_[host_port_proxy_pair];
+ const HostPortPair& host_port_pair,
+ bool is_https) {
+ QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED);
+ DCHECK(factory->HasActiveSession(server_id));
+ return factory->active_sessions_[server_id];
+ }
+
+ static scoped_ptr<QuicHttpStream> CreateIfSessionExists(
+ QuicStreamFactory* factory,
+ const HostPortPair& host_port_pair,
+ bool is_https,
+ const BoundNetLog& net_log) {
+ QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED);
+ return factory->CreateIfSessionExists(server_id, net_log);
}
static bool IsLiveSession(QuicStreamFactory* factory,
QuicClientSession* session) {
- for (QuicStreamFactory::SessionSet::iterator it =
+ for (QuicStreamFactory::SessionIdMap::iterator it =
factory->all_sessions_.begin();
it != factory->all_sessions_.end(); ++it) {
- if (*it == session)
+ if (it->first == session)
return true;
}
return false;
}
};
-class QuicStreamFactoryTest : public ::testing::Test {
+class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> {
protected:
QuicStreamFactoryTest()
: random_generator_(0),
+ maker_(GetParam(), 0),
clock_(new MockClock()),
+ cert_verifier_(CertVerifier::CreateDefault()),
factory_(&host_resolver_, &socket_factory_,
- base::WeakPtr<HttpServerProperties>(),
- &crypto_client_stream_factory_,
- &random_generator_, clock_, kDefaultMaxPacketSize),
- host_port_proxy_pair_(HostPortPair("www.google.com", 443),
- ProxyServer::Direct()),
+ base::WeakPtr<HttpServerProperties>(), cert_verifier_.get(),
+ &crypto_client_stream_factory_, &random_generator_, clock_,
+ kDefaultMaxPacketSize, std::string(),
+ SupportedVersions(GetParam()), true, true, true),
+ host_port_pair_(kDefaultServerHostName, kDefaultServerPort),
is_https_(false),
- cert_verifier_(CertVerifier::CreateDefault()) {
+ privacy_mode_(PRIVACY_MODE_DISABLED) {
factory_.set_require_confirmation(false);
}
- scoped_ptr<QuicEncryptedPacket> ConstructRstPacket(
- QuicPacketSequenceNumber num,
- QuicStreamId stream_id) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = true;
- header.packet_sequence_number = num;
- header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicRstStreamFrame rst(stream_id, QUIC_STREAM_CANCELLED);
- return scoped_ptr<QuicEncryptedPacket>(
- ConstructPacket(header, QuicFrame(&rst)));
+ scoped_ptr<QuicHttpStream> CreateIfSessionExists(
+ const HostPortPair& host_port_pair,
+ const BoundNetLog& net_log) {
+ return QuicStreamFactoryPeer::CreateIfSessionExists(
+ &factory_, host_port_pair, false, net_log_);
}
- scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
- QuicPacketSequenceNumber largest_received,
- QuicPacketSequenceNumber least_unacked) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = false;
- header.packet_sequence_number = 2;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked);
- QuicCongestionFeedbackFrame feedback;
- feedback.type = kTCP;
- feedback.tcp.accumulated_number_of_lost_packets = 0;
- feedback.tcp.receive_window = 16000;
-
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
- QuicFrames frames;
- frames.push_back(QuicFrame(&ack));
- frames.push_back(QuicFrame(&feedback));
- scoped_ptr<QuicPacket> packet(
- framer.BuildUnsizedDataPacket(header, frames).packet);
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
- ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+ int GetSourcePortForNewSession(const HostPortPair& destination) {
+ return GetSourcePortForNewSessionInner(destination, false);
}
- // Returns a newly created packet to send congestion feedback data.
- scoped_ptr<QuicEncryptedPacket> ConstructFeedbackPacket(
- QuicPacketSequenceNumber sequence_number) {
- QuicPacketHeader header;
- header.public_header.guid = random_generator_.RandUint64();
- header.public_header.reset_flag = false;
- header.public_header.version_flag = false;
- header.packet_sequence_number = sequence_number;
- header.entropy_flag = false;
- header.fec_flag = false;
- header.fec_group = 0;
-
- QuicCongestionFeedbackFrame frame;
- frame.type = kTCP;
- frame.tcp.accumulated_number_of_lost_packets = 0;
- frame.tcp.receive_window = 16000;
-
- return scoped_ptr<QuicEncryptedPacket>(
- ConstructPacket(header, QuicFrame(&frame)));
+ int GetSourcePortForNewSessionAndGoAway(
+ const HostPortPair& destination) {
+ return GetSourcePortForNewSessionInner(destination, true);
}
- scoped_ptr<QuicEncryptedPacket> ConstructPacket(
- const QuicPacketHeader& header,
- const QuicFrame& frame) {
- QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
- QuicFrames frames;
- frames.push_back(frame);
- scoped_ptr<QuicPacket> packet(
- framer.BuildUnsizedDataPacket(header, frames).packet);
- return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
- ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+ int GetSourcePortForNewSessionInner(const HostPortPair& destination,
+ bool goaway_received) {
+ // Should only be called if there is no active session for this destination.
+ EXPECT_EQ(NULL, CreateIfSessionExists(destination, net_log_).get());
+ size_t socket_count = socket_factory_.udp_client_sockets().size();
+
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ socket_data.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(destination,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ stream.reset();
+
+ QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, destination, is_https_);
+
+ if (socket_count + 1 != socket_factory_.udp_client_sockets().size()) {
+ EXPECT_TRUE(false);
+ return 0;
+ }
+
+ IPEndPoint endpoint;
+ socket_factory_.
+ udp_client_sockets()[socket_count]->GetLocalAddress(&endpoint);
+ int port = endpoint.port();
+ if (goaway_received) {
+ QuicGoAwayFrame goaway(QUIC_NO_ERROR, 1, "");
+ session->OnGoAway(goaway);
+ }
+
+ factory_.OnSessionClosed(session);
+ EXPECT_EQ(NULL, CreateIfSessionExists(destination, net_log_).get());
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+ return port;
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructRstPacket() {
+ QuicStreamId stream_id = kClientDataStreamId1;
+ return maker_.MakeRstPacket(
+ 1, true, stream_id,
+ AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING, GetParam()));
}
MockHostResolver host_resolver_;
DeterministicMockClientSocketFactory socket_factory_;
MockCryptoClientStreamFactory crypto_client_stream_factory_;
MockRandom random_generator_;
+ QuicTestPacketMaker maker_;
MockClock* clock_; // Owned by factory_.
+ scoped_ptr<CertVerifier> cert_verifier_;
QuicStreamFactory factory_;
- HostPortProxyPair host_port_proxy_pair_;
+ HostPortPair host_port_pair_;
bool is_https_;
- scoped_ptr<CertVerifier> cert_verifier_;
+ PrivacyMode privacy_mode_;
BoundNetLog net_log_;
TestCompletionCallback callback_;
};
-TEST_F(QuicStreamFactoryTest, CreateIfSessionExists) {
- EXPECT_EQ(NULL, factory_.CreateIfSessionExists(host_port_proxy_pair_,
- net_log_).get());
+INSTANTIATE_TEST_CASE_P(Version, QuicStreamFactoryTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicStreamFactoryTest, CreateIfSessionExists) {
+ EXPECT_EQ(NULL, CreateIfSessionExists(host_port_pair_, net_log_).get());
}
-TEST_F(QuicStreamFactoryTest, Create) {
+TEST_P(QuicStreamFactoryTest, Create) {
MockRead reads[] = {
MockRead(ASYNC, OK, 0) // EOF
};
@@ -183,24 +205,32 @@ TEST_F(QuicStreamFactoryTest, Create) {
socket_data.StopAfter(1);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
EXPECT_TRUE(stream.get());
// Will reset stream 3.
- stream = factory_.CreateIfSessionExists(host_port_proxy_pair_, net_log_);
+ stream = CreateIfSessionExists(host_port_pair_, net_log_);
EXPECT_TRUE(stream.get());
// TODO(rtenneti): We should probably have a tests that HTTP and HTTPS result
// in streams on different sessions.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(OK, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(OK,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
stream = request2.ReleaseStream(); // Will reset stream 5.
stream.reset(); // Will reset stream 7.
@@ -208,36 +238,382 @@ TEST_F(QuicStreamFactoryTest, Create) {
EXPECT_TRUE(socket_data.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, FailedCreate) {
- MockConnect connect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
- DeterministicSocketData socket_data(NULL, 0, NULL, 0);
- socket_data.set_connect_data(connect);
+TEST_P(QuicStreamFactoryTest, CreateZeroRtt) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::ZERO_RTT);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(),
+ "192.168.0.1", "");
+
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
- EXPECT_EQ(ERR_ADDRESS_IN_USE, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, Goaway) {
+TEST_P(QuicStreamFactoryTest, CreateZeroRttPost) {
MockRead reads[] = {
MockRead(ASYNC, OK, 0) // EOF
};
DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
+
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::ZERO_RTT);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(),
+ "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ // Posts require handshake confirmation, so this will return asynchronously.
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "POST",
+ net_log_,
+ callback_.callback()));
+
+ // Confirm the handshake and verify that the stream is created.
+ crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
+ QuicSession::HANDSHAKE_CONFIRMED);
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, CreateHttpVsHttps) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ !is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ stream = request2.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ stream.reset();
+
+ EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, !is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+// TODO(rch): re-enable this.
+TEST_P(QuicStreamFactoryTest, DISABLED_Pooling) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ HostPortPair server2("mail.google.com", kDefaultServerPort);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(
+ kDefaultServerHostName, "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(
+ "mail.google.com", "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_EQ(
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0);
DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
socket_data2.StopAfter(1);
+
+ HostPortPair server2("mail.google.com", kDefaultServerPort);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(
+ kDefaultServerHostName, "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(
+ "mail.google.com", "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ factory_.OnSessionGoingAway(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_));
+ EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, host_port_pair_, is_https_));
+ EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, server2, is_https_));
+
+ TestCompletionCallback callback3;
+ QuicStreamRequest request3(&factory_);
+ EXPECT_EQ(OK,
+ request3.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback3.callback()));
+ scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream();
+ EXPECT_TRUE(stream3.get());
+
+ EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+// TODO(rch): re-enable this.
+TEST_P(QuicStreamFactoryTest, DISABLED_HttpsPooling) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ HostPortPair server1("www.example.org", 443);
+ HostPortPair server2("mail.example.org", 443);
+
+ // Load a cert that is valid for:
+ // www.example.org (server1)
+ // mail.example.org (server2)
+ // www.example.com
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
+ ProofVerifyDetailsChromium verify_details;
+ verify_details.cert_verify_result.verified_cert = test_cert;
+ crypto_client_stream_factory_.set_proof_verify_details(&verify_details);
+
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ is_https_ = true;
+ EXPECT_EQ(OK,
+ request.Request(server1,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_EQ(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server1, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithCertMismatch) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ HostPortPair server1("www.example.org", 443);
+ HostPortPair server2("mail.google.com", 443);
+
+ // Load a cert that is valid for:
+ // www.example.org (server1)
+ // mail.example.org
+ // www.example.com
+ // But is not valid for mail.google.com (server2).
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
+ ProofVerifyDetailsChromium verify_details;
+ verify_details.cert_verify_result.verified_cert = test_cert;
+ crypto_client_stream_factory_.set_proof_verify_details(&verify_details);
+
+
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ is_https_ = true;
+ EXPECT_EQ(OK,
+ request.Request(server1,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server1, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, Goaway) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ socket_data.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
+ socket_data2.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
@@ -246,29 +622,33 @@ TEST_F(QuicStreamFactoryTest, Goaway) {
// Mark the session as going away. Ensure that while it is still alive
// that it is no longer active.
QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession(
- &factory_, host_port_proxy_pair_);
+ &factory_, host_port_pair_, is_https_);
factory_.OnSessionGoingAway(session);
EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session));
- EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(&factory_,
- host_port_proxy_pair_));
- EXPECT_EQ(NULL, factory_.CreateIfSessionExists(host_port_proxy_pair_,
- net_log_).get());
+ EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, host_port_pair_, is_https_));
+ EXPECT_EQ(NULL, CreateIfSessionExists(host_port_pair_, net_log_).get());
// Create a new request for the same destination and verify that a
// new session is created.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
EXPECT_TRUE(stream2.get());
EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(&factory_,
- host_port_proxy_pair_));
+ host_port_pair_,
+ is_https_));
EXPECT_NE(session,
QuicStreamFactoryPeer::GetActiveSession(
- &factory_, host_port_proxy_pair_));
+ &factory_, host_port_pair_, is_https_));
EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session));
stream2.reset();
@@ -276,13 +656,17 @@ TEST_F(QuicStreamFactoryTest, Goaway) {
EXPECT_TRUE(socket_data.at_read_eof());
EXPECT_TRUE(socket_data.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, MaxOpenStream) {
+TEST_P(QuicStreamFactoryTest, MaxOpenStream) {
MockRead reads[] = {
MockRead(ASYNC, OK, 0) // EOF
};
- scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket(1, 3));
+ QuicStreamId stream_id = kClientDataStreamId1;
+ scoped_ptr<QuicEncryptedPacket> rst(
+ maker_.MakeRstPacket(1, true, stream_id, QUIC_STREAM_CANCELLED));
MockWrite writes[] = {
MockWrite(ASYNC, rst->data(), rst->length(), 1),
};
@@ -297,8 +681,11 @@ TEST_F(QuicStreamFactoryTest, MaxOpenStream) {
// 2 * kDefaultMaxStreamsPerConnection.
for (size_t i = 0; i < 2 * kDefaultMaxStreamsPerConnection; i++) {
QuicStreamRequest request(&factory_);
- int rv = request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
+ int rv = request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
callback_.callback());
if (i == 0) {
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -314,9 +701,13 @@ TEST_F(QuicStreamFactoryTest, MaxOpenStream) {
}
QuicStreamRequest request(&factory_);
- EXPECT_EQ(OK, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- CompletionCallback()));
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ CompletionCallback()));
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
EXPECT_TRUE(stream);
EXPECT_EQ(ERR_IO_PENDING, stream->InitializeStream(
@@ -334,16 +725,20 @@ TEST_F(QuicStreamFactoryTest, MaxOpenStream) {
STLDeleteElements(&streams);
}
-TEST_F(QuicStreamFactoryTest, CreateError) {
+TEST_P(QuicStreamFactoryTest, ResolutionErrorInCreate) {
DeterministicSocketData socket_data(NULL, 0, NULL, 0);
socket_factory_.AddSocketDataProvider(&socket_data);
- host_resolver_.rules()->AddSimulatedFailure("www.google.com");
+ host_resolver_.rules()->AddSimulatedFailure(kDefaultServerHostName);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(ERR_NAME_NOT_RESOLVED, callback_.WaitForResult());
@@ -351,7 +746,29 @@ TEST_F(QuicStreamFactoryTest, CreateError) {
EXPECT_TRUE(socket_data.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, CancelCreate) {
+TEST_P(QuicStreamFactoryTest, ConnectErrorInCreate) {
+ MockConnect connect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
+ DeterministicSocketData socket_data(NULL, 0, NULL, 0);
+ socket_data.set_connect_data(connect);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(ERR_ADDRESS_IN_USE, callback_.WaitForResult());
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, CancelCreate) {
MockRead reads[] = {
MockRead(ASYNC, OK, 0) // EOF
};
@@ -359,9 +776,13 @@ TEST_F(QuicStreamFactoryTest, CancelCreate) {
socket_factory_.AddSocketDataProvider(&socket_data);
{
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
}
socket_data.StopAfter(1);
@@ -369,7 +790,7 @@ TEST_F(QuicStreamFactoryTest, CancelCreate) {
run_loop.RunUntilIdle();
scoped_ptr<QuicHttpStream> stream(
- factory_.CreateIfSessionExists(host_port_proxy_pair_, net_log_));
+ CreateIfSessionExists(host_port_pair_, net_log_));
EXPECT_TRUE(stream.get());
stream.reset();
@@ -377,11 +798,40 @@ TEST_F(QuicStreamFactoryTest, CancelCreate) {
EXPECT_TRUE(socket_data.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, CloseAllSessions) {
+TEST_P(QuicStreamFactoryTest, CreateConsistentEphemeralPort) {
+ // Sequentially connect to the default host, then another host, and then the
+ // default host. Verify that the default host gets a consistent ephemeral
+ // port, that is different from the other host's connection.
+
+ std::string other_server_name = "other.google.com";
+ EXPECT_NE(kDefaultServerHostName, other_server_name);
+ HostPortPair host_port_pair2(other_server_name, kDefaultServerPort);
+
+ int original_port = GetSourcePortForNewSession(host_port_pair_);
+ EXPECT_NE(original_port, GetSourcePortForNewSession(host_port_pair2));
+ EXPECT_EQ(original_port, GetSourcePortForNewSession(host_port_pair_));
+}
+
+TEST_P(QuicStreamFactoryTest, GoAwayDisablesConsistentEphemeralPort) {
+ // Get a session to the host using the port suggester.
+ int original_port =
+ GetSourcePortForNewSessionAndGoAway(host_port_pair_);
+ // Verify that the port is different after the goaway.
+ EXPECT_NE(original_port, GetSourcePortForNewSession(host_port_pair_));
+ // Since the previous session did not goaway we should see the original port.
+ EXPECT_EQ(original_port, GetSourcePortForNewSession(host_port_pair_));
+}
+
+TEST_P(QuicStreamFactoryTest, CloseAllSessions) {
MockRead reads[] = {
MockRead(ASYNC, 0, 0) // EOF
};
- DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? NULL : &writes[0],
+ writes.size());
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
@@ -393,9 +843,13 @@ TEST_F(QuicStreamFactoryTest, CloseAllSessions) {
socket_data2.StopAfter(1);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
@@ -413,9 +867,13 @@ TEST_F(QuicStreamFactoryTest, CloseAllSessions) {
// a new session.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
stream = request2.ReleaseStream();
@@ -427,11 +885,16 @@ TEST_F(QuicStreamFactoryTest, CloseAllSessions) {
EXPECT_TRUE(socket_data2.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) {
+TEST_P(QuicStreamFactoryTest, OnIPAddressChanged) {
MockRead reads[] = {
MockRead(ASYNC, 0, 0) // EOF
};
- DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? NULL : &writes[0],
+ writes.size());
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
@@ -443,9 +906,13 @@ TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) {
socket_data2.StopAfter(1);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
@@ -464,9 +931,13 @@ TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) {
// a new session.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
stream = request2.ReleaseStream();
@@ -478,11 +949,16 @@ TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) {
EXPECT_TRUE(socket_data2.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, OnCertAdded) {
+TEST_P(QuicStreamFactoryTest, OnCertAdded) {
MockRead reads[] = {
MockRead(ASYNC, 0, 0) // EOF
};
- DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? NULL : &writes[0],
+ writes.size());
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
@@ -494,9 +970,13 @@ TEST_F(QuicStreamFactoryTest, OnCertAdded) {
socket_data2.StopAfter(1);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
@@ -515,9 +995,13 @@ TEST_F(QuicStreamFactoryTest, OnCertAdded) {
// a new session.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
stream = request2.ReleaseStream();
@@ -529,11 +1013,16 @@ TEST_F(QuicStreamFactoryTest, OnCertAdded) {
EXPECT_TRUE(socket_data2.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, OnCACertChanged) {
+TEST_P(QuicStreamFactoryTest, OnCACertChanged) {
MockRead reads[] = {
MockRead(ASYNC, 0, 0) // EOF
};
- DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? NULL : &writes[0],
+ writes.size());
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
@@ -545,9 +1034,13 @@ TEST_F(QuicStreamFactoryTest, OnCACertChanged) {
socket_data2.StopAfter(1);
QuicStreamRequest request(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
@@ -566,9 +1059,13 @@ TEST_F(QuicStreamFactoryTest, OnCACertChanged) {
// a new session.
QuicStreamRequest request2(&factory_);
- EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
- cert_verifier_.get(), net_log_,
- callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
stream = request2.ReleaseStream();
@@ -580,7 +1077,7 @@ TEST_F(QuicStreamFactoryTest, OnCACertChanged) {
EXPECT_TRUE(socket_data2.at_write_eof());
}
-TEST_F(QuicStreamFactoryTest, SharedCryptoConfig) {
+TEST_P(QuicStreamFactoryTest, SharedCryptoConfig) {
vector<string> cannoncial_suffixes;
cannoncial_suffixes.push_back(string(".c.youtube.com"));
cannoncial_suffixes.push_back(string(".googlevideo.com"));
@@ -591,15 +1088,12 @@ TEST_F(QuicStreamFactoryTest, SharedCryptoConfig) {
r1_host_name.append(cannoncial_suffixes[i]);
r2_host_name.append(cannoncial_suffixes[i]);
- HostPortProxyPair host_port_proxy_pair1(HostPortPair(r1_host_name, 80),
- ProxyServer::Direct());
-
- QuicCryptoClientConfig* crypto_config1 =
- QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
- host_port_proxy_pair1);
- DCHECK(crypto_config1);
+ HostPortPair host_port_pair1(r1_host_name, 80);
+ QuicCryptoClientConfig* crypto_config =
+ QuicStreamFactoryPeer::GetCryptoConfig(&factory_);
+ QuicServerId server_id1(host_port_pair1, is_https_, privacy_mode_);
QuicCryptoClientConfig::CachedState* cached1 =
- crypto_config1->LookupOrCreate(host_port_proxy_pair1.first.host());
+ crypto_config->LookupOrCreate(server_id1);
EXPECT_FALSE(cached1->proof_valid());
EXPECT_TRUE(cached1->source_address_token().empty());
@@ -608,20 +1102,16 @@ TEST_F(QuicStreamFactoryTest, SharedCryptoConfig) {
cached1->set_source_address_token(r1_host_name);
cached1->SetProofValid();
- HostPortProxyPair host_port_proxy_pair2(HostPortPair(r2_host_name, 80),
- ProxyServer::Direct());
- QuicCryptoClientConfig* crypto_config2 =
- QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
- host_port_proxy_pair2);
- DCHECK(crypto_config2);
+ HostPortPair host_port_pair2(r2_host_name, 80);
+ QuicServerId server_id2(host_port_pair2, is_https_, privacy_mode_);
QuicCryptoClientConfig::CachedState* cached2 =
- crypto_config2->LookupOrCreate(host_port_proxy_pair2.first.host());
+ crypto_config->LookupOrCreate(server_id2);
EXPECT_EQ(cached1->source_address_token(), cached2->source_address_token());
EXPECT_TRUE(cached2->proof_valid());
}
}
-TEST_F(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
+TEST_P(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
vector<string> cannoncial_suffixes;
cannoncial_suffixes.push_back(string(".c.youtube.com"));
cannoncial_suffixes.push_back(string(".googlevideo.com"));
@@ -632,15 +1122,12 @@ TEST_F(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
r3_host_name.append(cannoncial_suffixes[i]);
r4_host_name.append(cannoncial_suffixes[i]);
- HostPortProxyPair host_port_proxy_pair1(HostPortPair(r3_host_name, 80),
- ProxyServer::Direct());
-
- QuicCryptoClientConfig* crypto_config1 =
- QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
- host_port_proxy_pair1);
- DCHECK(crypto_config1);
+ HostPortPair host_port_pair1(r3_host_name, 80);
+ QuicCryptoClientConfig* crypto_config =
+ QuicStreamFactoryPeer::GetCryptoConfig(&factory_);
+ QuicServerId server_id1(host_port_pair1, is_https_, privacy_mode_);
QuicCryptoClientConfig::CachedState* cached1 =
- crypto_config1->LookupOrCreate(host_port_proxy_pair1.first.host());
+ crypto_config->LookupOrCreate(server_id1);
EXPECT_FALSE(cached1->proof_valid());
EXPECT_TRUE(cached1->source_address_token().empty());
@@ -649,14 +1136,10 @@ TEST_F(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
cached1->set_source_address_token(r3_host_name);
cached1->SetProofInvalid();
- HostPortProxyPair host_port_proxy_pair2(HostPortPair(r4_host_name, 80),
- ProxyServer::Direct());
- QuicCryptoClientConfig* crypto_config2 =
- QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
- host_port_proxy_pair2);
- DCHECK(crypto_config2);
+ HostPortPair host_port_pair2(r4_host_name, 80);
+ QuicServerId server_id2(host_port_pair2, is_https_, privacy_mode_);
QuicCryptoClientConfig::CachedState* cached2 =
- crypto_config2->LookupOrCreate(host_port_proxy_pair2.first.host());
+ crypto_config->LookupOrCreate(server_id2);
EXPECT_NE(cached1->source_address_token(), cached2->source_address_token());
EXPECT_TRUE(cached2->source_address_token().empty());
EXPECT_FALSE(cached2->proof_valid());
diff --git a/chromium/net/quic/quic_stream_sequencer.cc b/chromium/net/quic/quic_stream_sequencer.cc
index 02fec67f200..3a303957c2c 100644
--- a/chromium/net/quic/quic_stream_sequencer.cc
+++ b/chromium/net/quic/quic_stream_sequencer.cc
@@ -8,8 +8,10 @@
#include <limits>
#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
#include "net/quic/reliable_quic_stream.h"
+using std::make_pair;
using std::min;
using std::numeric_limits;
@@ -18,61 +20,29 @@ namespace net {
QuicStreamSequencer::QuicStreamSequencer(ReliableQuicStream* quic_stream)
: stream_(quic_stream),
num_bytes_consumed_(0),
- max_frame_memory_(numeric_limits<size_t>::max()),
- close_offset_(numeric_limits<QuicStreamOffset>::max()) {
-}
-
-QuicStreamSequencer::QuicStreamSequencer(size_t max_frame_memory,
- ReliableQuicStream* quic_stream)
- : stream_(quic_stream),
- num_bytes_consumed_(0),
- max_frame_memory_(max_frame_memory),
- close_offset_(numeric_limits<QuicStreamOffset>::max()) {
- if (max_frame_memory < kMaxPacketSize) {
- LOG(DFATAL) << "Setting max frame memory to " << max_frame_memory
- << ". Some frames will be impossible to handle.";
- }
+ close_offset_(numeric_limits<QuicStreamOffset>::max()),
+ blocked_(false),
+ num_bytes_buffered_(0),
+ num_frames_received_(0),
+ num_duplicate_frames_received_(0) {
}
QuicStreamSequencer::~QuicStreamSequencer() {
}
-bool QuicStreamSequencer::WillAcceptStreamFrame(
- const QuicStreamFrame& frame) const {
- size_t data_len = frame.data.TotalBufferSize();
- DCHECK_LE(data_len, max_frame_memory_);
-
+bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
+ ++num_frames_received_;
if (IsDuplicate(frame)) {
+ ++num_duplicate_frames_received_;
+ // Silently ignore duplicates.
return true;
}
- QuicStreamOffset byte_offset = frame.offset;
- if (data_len > max_frame_memory_) {
- // We're never going to buffer this frame and we can't pass it up.
- // The stream might only consume part of it and we'd need a partial ack.
- //
- // Ideally this should never happen, as we check that
- // max_frame_memory_ > kMaxPacketSize and lower levels should reject
- // frames larger than that.
- return false;
- }
- if (byte_offset + data_len - num_bytes_consumed_ > max_frame_memory_) {
- // We can buffer this but not right now. Toss it.
- // It might be worth trying an experiment where we try best-effort buffering
- return false;
- }
- return true;
-}
-bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
- if (!WillAcceptStreamFrame(frame)) {
- // This should not happen, as WillAcceptFrame should be called before
- // OnStreamFrame. Error handling should be done by the caller.
+ if (FrameOverlapsBufferedData(frame)) {
+ stream_->CloseConnectionWithDetails(
+ QUIC_INVALID_STREAM_FRAME, "Stream frame overlaps with buffered data.");
return false;
}
- if (IsDuplicate(frame)) {
- // Silently ignore duplicates.
- return true;
- }
QuicStreamOffset byte_offset = frame.offset;
size_t data_len = frame.data.TotalBufferSize();
@@ -92,7 +62,10 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
IOVector data;
data.AppendIovec(frame.data.iovec(), frame.data.Size());
- if (byte_offset == num_bytes_consumed_) {
+
+ // If the frame has arrived in-order then we can process it immediately, only
+ // buffering if the stream is unable to process it.
+ if (!blocked_ && byte_offset == num_bytes_consumed_) {
DVLOG(1) << "Processing byte offset " << byte_offset;
size_t bytes_consumed = 0;
for (size_t i = 0; i < data.Size(); ++i) {
@@ -101,6 +74,8 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
data.iovec()[i].iov_len);
}
num_bytes_consumed_ += bytes_consumed;
+ stream_->AddBytesConsumed(bytes_consumed);
+
if (MaybeCloseStream()) {
return true;
}
@@ -111,18 +86,21 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
FlushBufferedFrames();
return true; // it's safe to ack this frame.
} else {
- // Set ourselves up to buffer what's left
+ // Set ourselves up to buffer what's left.
data_len -= bytes_consumed;
data.Consume(bytes_consumed);
byte_offset += bytes_consumed;
}
}
+
+ // Buffer any remaining data to be consumed by the stream when ready.
for (size_t i = 0; i < data.Size(); ++i) {
DVLOG(1) << "Buffering stream data at offset " << byte_offset;
- frames_.insert(make_pair(byte_offset, string(
- static_cast<char*>(data.iovec()[i].iov_base),
- data.iovec()[i].iov_len)));
- byte_offset += data.iovec()[i].iov_len;
+ const iovec& iov = data.iovec()[i];
+ buffered_frames_.insert(make_pair(
+ byte_offset, string(static_cast<char*>(iov.iov_base), iov.iov_len)));
+ byte_offset += iov.iov_len;
+ num_bytes_buffered_ += iov.iov_len;
}
return true;
}
@@ -143,23 +121,26 @@ void QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) {
}
bool QuicStreamSequencer::MaybeCloseStream() {
- if (IsClosed()) {
+ if (!blocked_ && IsClosed()) {
DVLOG(1) << "Passing up termination, as we've processed "
<< num_bytes_consumed_ << " of " << close_offset_
<< " bytes.";
// Technically it's an error if num_bytes_consumed isn't exactly
// equal, but error handling seems silly at this point.
stream_->OnFinRead();
+ buffered_frames_.clear();
+ num_bytes_buffered_ = 0;
return true;
}
return false;
}
int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) {
- FrameMap::iterator it = frames_.begin();
+ DCHECK(!blocked_);
+ FrameMap::iterator it = buffered_frames_.begin();
size_t index = 0;
QuicStreamOffset offset = num_bytes_consumed_;
- while (it != frames_.end() && index < iov_len) {
+ while (it != buffered_frames_.end() && index < iov_len) {
if (it->first != offset) return index;
iov[index].iov_base = static_cast<void*>(
@@ -174,14 +155,15 @@ int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) {
}
int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) {
- FrameMap::iterator it = frames_.begin();
+ DCHECK(!blocked_);
+ FrameMap::iterator it = buffered_frames_.begin();
size_t iov_index = 0;
size_t iov_offset = 0;
size_t frame_offset = 0;
size_t initial_bytes_consumed = num_bytes_consumed_;
while (iov_index < iov_len &&
- it != frames_.end() &&
+ it != buffered_frames_.end() &&
it->first == num_bytes_consumed_) {
int bytes_to_read = min(iov[iov_index].iov_len - iov_offset,
it->second.size() - frame_offset);
@@ -199,79 +181,90 @@ int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) {
}
if (it->second.size() == frame_offset) {
// We've copied this whole frame
- num_bytes_consumed_ += it->second.size();
- frames_.erase(it);
- it = frames_.begin();
+ RecordBytesConsumed(it->second.size());
+ buffered_frames_.erase(it);
+ it = buffered_frames_.begin();
frame_offset = 0;
}
}
// We've finished copying. If we have a partial frame, update it.
if (frame_offset != 0) {
- frames_.insert(make_pair(it->first + frame_offset,
- it->second.substr(frame_offset)));
- frames_.erase(frames_.begin());
- num_bytes_consumed_ += frame_offset;
+ buffered_frames_.insert(
+ make_pair(it->first + frame_offset, it->second.substr(frame_offset)));
+ buffered_frames_.erase(buffered_frames_.begin());
+ RecordBytesConsumed(frame_offset);
}
return num_bytes_consumed_ - initial_bytes_consumed;
}
-void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) {
- size_t end_offset = num_bytes_consumed_ + num_bytes_consumed;
- while (!frames_.empty() && end_offset != num_bytes_consumed_) {
- FrameMap::iterator it = frames_.begin();
- if (it->first != num_bytes_consumed_) {
- LOG(DFATAL) << "Invalid argument to MarkConsumed. "
- << " num_bytes_consumed_: " << num_bytes_consumed_
- << " end_offset: " << end_offset
- << " offset: " << it->first
- << " length: " << it->second.length();
- stream_->Reset(QUIC_ERROR_PROCESSING_STREAM);
- return;
- }
-
- if (it->first + it->second.length() <= end_offset) {
- num_bytes_consumed_ += it->second.length();
- // This chunk is entirely consumed.
- frames_.erase(it);
- continue;
- }
-
- // Partially consume this frame.
- size_t delta = end_offset - it->first;
- num_bytes_consumed_ += delta;
- frames_.insert(make_pair(end_offset, it->second.substr(delta)));
- frames_.erase(it);
- break;
- }
-}
-
bool QuicStreamSequencer::HasBytesToRead() const {
- FrameMap::const_iterator it = frames_.begin();
+ FrameMap::const_iterator it = buffered_frames_.begin();
- return it != frames_.end() && it->first == num_bytes_consumed_;
+ return it != buffered_frames_.end() && it->first == num_bytes_consumed_;
}
bool QuicStreamSequencer::IsClosed() const {
return num_bytes_consumed_ >= close_offset_;
}
+bool QuicStreamSequencer::FrameOverlapsBufferedData(
+ const QuicStreamFrame& frame) const {
+ if (buffered_frames_.empty()) {
+ return false;
+ }
+
+ FrameMap::const_iterator next_frame =
+ buffered_frames_.lower_bound(frame.offset);
+ // Duplicate frames should have been dropped in IsDuplicate.
+ DCHECK(next_frame == buffered_frames_.end() ||
+ next_frame->first != frame.offset);
+
+ // If there is a buffered frame with a higher starting offset, then we check
+ // to see if the new frame runs into the higher frame.
+ if (next_frame != buffered_frames_.end() &&
+ (frame.offset + frame.data.TotalBufferSize()) > next_frame->first) {
+ DVLOG(1) << "New frame overlaps next frame: " << frame.offset << " + "
+ << frame.data.TotalBufferSize() << " > " << next_frame->first;
+ return true;
+ }
+
+ // If there is a buffered frame with a lower starting offset, then we check
+ // to see if the buffered frame runs into the new frame.
+ if (next_frame != buffered_frames_.begin()) {
+ FrameMap::const_iterator preceeding_frame = --next_frame;
+ QuicStreamOffset offset = preceeding_frame->first;
+ uint64 data_length = preceeding_frame->second.length();
+ if ((offset + data_length) > frame.offset) {
+ DVLOG(1) << "Preceeding frame overlaps new frame: " << offset << " + "
+ << data_length << " > " << frame.offset;
+ return true;
+ }
+ }
+ return false;
+}
+
bool QuicStreamSequencer::IsDuplicate(const QuicStreamFrame& frame) const {
// A frame is duplicate if the frame offset is smaller than our bytes consumed
// or we have stored the frame in our map.
// TODO(pwestin): Is it possible that a new frame contain more data even if
// the offset is the same?
return frame.offset < num_bytes_consumed_ ||
- frames_.find(frame.offset) != frames_.end();
+ buffered_frames_.find(frame.offset) != buffered_frames_.end();
+}
+
+void QuicStreamSequencer::SetBlockedUntilFlush() {
+ blocked_ = true;
}
void QuicStreamSequencer::FlushBufferedFrames() {
- FrameMap::iterator it = frames_.find(num_bytes_consumed_);
- while (it != frames_.end()) {
+ blocked_ = false;
+ FrameMap::iterator it = buffered_frames_.find(num_bytes_consumed_);
+ while (it != buffered_frames_.end()) {
DVLOG(1) << "Flushing buffered packet at offset " << it->first;
string* data = &it->second;
size_t bytes_consumed = stream_->ProcessRawData(data->c_str(),
data->size());
- num_bytes_consumed_ += bytes_consumed;
+ RecordBytesConsumed(bytes_consumed);
if (MaybeCloseStream()) {
return;
}
@@ -279,15 +272,23 @@ void QuicStreamSequencer::FlushBufferedFrames() {
stream_->Reset(QUIC_ERROR_PROCESSING_STREAM); // Programming error
return;
} else if (bytes_consumed == data->size()) {
- frames_.erase(it);
- it = frames_.find(num_bytes_consumed_);
+ buffered_frames_.erase(it);
+ it = buffered_frames_.find(num_bytes_consumed_);
} else {
string new_data = it->second.substr(bytes_consumed);
- frames_.erase(it);
- frames_.insert(make_pair(num_bytes_consumed_, new_data));
+ buffered_frames_.erase(it);
+ buffered_frames_.insert(make_pair(num_bytes_consumed_, new_data));
return;
}
}
+ MaybeCloseStream();
+}
+
+void QuicStreamSequencer::RecordBytesConsumed(size_t bytes_consumed) {
+ num_bytes_consumed_ += bytes_consumed;
+ num_bytes_buffered_ -= bytes_consumed;
+
+ stream_->AddBytesConsumed(bytes_consumed);
}
} // namespace net
diff --git a/chromium/net/quic/quic_stream_sequencer.h b/chromium/net/quic/quic_stream_sequencer.h
index e2601246059..a4580a9ca3f 100644
--- a/chromium/net/quic/quic_stream_sequencer.h
+++ b/chromium/net/quic/quic_stream_sequencer.h
@@ -8,7 +8,6 @@
#include <map>
#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
#include "net/base/iovec.h"
#include "net/quic/quic_protocol.h"
@@ -30,21 +29,13 @@ class ReliableQuicStream;
class NET_EXPORT_PRIVATE QuicStreamSequencer {
public:
explicit QuicStreamSequencer(ReliableQuicStream* quic_stream);
- QuicStreamSequencer(size_t max_frame_memory,
- ReliableQuicStream* quic_stream);
-
virtual ~QuicStreamSequencer();
- // Returns the expected value of OnStreamFrame for this frame.
- bool WillAcceptStreamFrame(const QuicStreamFrame& frame) const;
-
// If the frame is the next one we need in order to process in-order data,
// ProcessData will be immediately called on the stream until all buffered
// data is processed or the stream fails to consume data. Any unconsumed
- // data will be buffered.
- //
- // If the frame is not the next in line, it will either be buffered, and
- // this will return true, or it will be rejected and this will return false.
+ // data will be buffered. If the frame is not the next in line, it will be
+ // buffered.
bool OnStreamFrame(const QuicStreamFrame& frame);
// Once data is buffered, it's up to the stream to read it when the stream
@@ -58,10 +49,6 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer {
// bytes read. Any buffered data no longer in use will be released.
int Readv(const struct iovec* iov, size_t iov_len);
- // Consumes |num_bytes| data. Used in conjunction with |GetReadableRegions|
- // to do zero-copy reads.
- void MarkConsumed(size_t num_bytes);
-
// Returns true if the sequncer has bytes available for reading.
bool HasBytesToRead() const;
@@ -71,28 +58,74 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer {
// Returns true if the sequencer has received this frame before.
bool IsDuplicate(const QuicStreamFrame& frame) const;
+ // Returns true if |frame| contains data which overlaps buffered data
+ // (indicating an invalid stream frame has been received).
+ bool FrameOverlapsBufferedData(const QuicStreamFrame& frame) const;
+
// Calls |ProcessRawData| on |stream_| for each buffered frame that may
// be processed.
void FlushBufferedFrames();
+ // Blocks processing of frames until |FlushBufferedFrames| is called.
+ void SetBlockedUntilFlush();
+
+ size_t num_bytes_buffered() const { return num_bytes_buffered_; }
+ QuicStreamOffset num_bytes_consumed() const { return num_bytes_consumed_; }
+
+ int num_frames_received() const { return num_frames_received_; }
+
+ int num_duplicate_frames_received() const {
+ return num_duplicate_frames_received_;
+ }
+
private:
friend class test::QuicStreamSequencerPeer;
- // TODO(alyssar) use something better than strings.
- typedef map<QuicStreamOffset, string> FrameMap;
-
// Wait until we've seen 'offset' bytes, and then terminate the stream.
void CloseStreamAtOffset(QuicStreamOffset offset);
+ // If we've received a FIN and have processed all remaining data, then inform
+ // the stream of FIN, and clear buffers.
bool MaybeCloseStream();
- ReliableQuicStream* stream_; // The stream which owns this sequencer.
- QuicStreamOffset num_bytes_consumed_; // The last data consumed by the stream
- FrameMap frames_; // sequence number -> frame
- size_t max_frame_memory_; // the maximum memory the sequencer can buffer.
+ // Called whenever bytes are consumed by the stream. Updates
+ // num_bytes_consumed_ and num_bytes_buffered_.
+ void RecordBytesConsumed(size_t bytes_consumed);
+
+ // The stream which owns this sequencer.
+ ReliableQuicStream* stream_;
+
+ // The last data consumed by the stream.
+ QuicStreamOffset num_bytes_consumed_;
+
+ // TODO(alyssar) use something better than strings.
+ // TODO(rjshade): In future we may support retransmission of partial stream
+ // frames, in which case we will have to allow receipt of overlapping frames.
+ // Maybe write new frames into a ring buffer, and keep track of consumed
+ // bytes, and gaps.
+ typedef map<QuicStreamOffset, string> FrameMap;
+
+ // Stores buffered frames (maps from sequence number -> frame data as string).
+ FrameMap buffered_frames_;
+
// The offset, if any, we got a stream termination for. When this many bytes
// have been processed, the sequencer will be closed.
QuicStreamOffset close_offset_;
+
+ // If true, the sequencer is blocked from passing data to the stream and will
+ // buffer all new incoming data until FlushBufferedFrames is called.
+ bool blocked_;
+
+ // Tracks how many bytes the sequencer has buffered.
+ size_t num_bytes_buffered_;
+
+ // Count of the number of frames received.
+ int num_frames_received_;
+
+ // Count of the number of duplicate frames received.
+ int num_duplicate_frames_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicStreamSequencer);
};
} // namespace net
diff --git a/chromium/net/quic/quic_stream_sequencer_test.cc b/chromium/net/quic/quic_stream_sequencer_test.cc
index f898e6611f7..0d9e82bf443 100644
--- a/chromium/net/quic/quic_stream_sequencer_test.cc
+++ b/chromium/net/quic/quic_stream_sequencer_test.cc
@@ -7,14 +7,19 @@
#include <utility>
#include <vector>
+#include "base/logging.h"
#include "base/rand_util.h"
#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/reliable_quic_stream.h"
+#include "net/quic/test_tools/quic_stream_sequencer_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece;
+using std::map;
using std::min;
using std::pair;
using std::vector;
@@ -27,42 +32,6 @@ using testing::StrEq;
namespace net {
namespace test {
-class QuicStreamSequencerPeer : public QuicStreamSequencer {
- public:
- explicit QuicStreamSequencerPeer(ReliableQuicStream* stream)
- : QuicStreamSequencer(stream) {
- }
-
- QuicStreamSequencerPeer(int32 max_mem, ReliableQuicStream* stream)
- : QuicStreamSequencer(max_mem, stream) {
- }
-
- virtual bool OnFinFrame(QuicStreamOffset byte_offset, const char* data) {
- QuicStreamFrame frame;
- frame.stream_id = 1;
- frame.offset = byte_offset;
- frame.data.Append(const_cast<char*>(data), strlen(data));
- frame.fin = true;
- return OnStreamFrame(frame);
- }
-
- virtual bool OnFrame(QuicStreamOffset byte_offset, const char* data) {
- QuicStreamFrame frame;
- frame.stream_id = 1;
- frame.offset = byte_offset;
- frame.data.Append(const_cast<char*>(data), strlen(data));
- frame.fin = false;
- return OnStreamFrame(frame);
- }
-
- void SetMemoryLimit(size_t limit) {
- max_frame_memory_ = limit;
- }
- uint64 num_bytes_consumed() const { return num_bytes_consumed_; }
- const FrameMap* frames() const { return &frames_; }
- QuicStreamOffset close_offset() const { return close_offset_; }
-};
-
class MockStream : public ReliableQuicStream {
public:
MockStream(QuicSession* session, QuicStreamId id)
@@ -75,9 +44,12 @@ class MockStream : public ReliableQuicStream {
const string& details));
MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error));
MOCK_METHOD0(OnCanWrite, void());
- virtual QuicPriority EffectivePriority() const {
+ virtual QuicPriority EffectivePriority() const OVERRIDE {
return QuicUtils::HighestPriority();
}
+ virtual bool IsFlowControlEnabled() const {
+ return true;
+ }
};
namespace {
@@ -91,7 +63,9 @@ class QuicStreamSequencerTest : public ::testing::Test {
: connection_(new MockConnection(false)),
session_(connection_),
stream_(&session_, 1),
- sequencer_(new QuicStreamSequencerPeer(&stream_)) {
+ sequencer_(new QuicStreamSequencer(&stream_)),
+ buffered_frames_(
+ QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get())) {
}
bool VerifyReadableRegions(const char** expected, size_t num_expected) {
@@ -132,111 +106,145 @@ class QuicStreamSequencerTest : public ::testing::Test {
return true;
}
+ bool OnFinFrame(QuicStreamOffset byte_offset, const char* data) {
+ QuicStreamFrame frame;
+ frame.stream_id = 1;
+ frame.offset = byte_offset;
+ frame.data.Append(const_cast<char*>(data), strlen(data));
+ frame.fin = true;
+ return sequencer_->OnStreamFrame(frame);
+ }
+
+ bool OnFrame(QuicStreamOffset byte_offset, const char* data) {
+ QuicStreamFrame frame;
+ frame.stream_id = 1;
+ frame.offset = byte_offset;
+ frame.data.Append(const_cast<char*>(data), strlen(data));
+ frame.fin = false;
+ return sequencer_->OnStreamFrame(frame);
+ }
+
MockConnection* connection_;
MockSession session_;
testing::StrictMock<MockStream> stream_;
- scoped_ptr<QuicStreamSequencerPeer> sequencer_;
+ scoped_ptr<QuicStreamSequencer> sequencer_;
+ map<QuicStreamOffset, string>* buffered_frames_;
};
TEST_F(QuicStreamSequencerTest, RejectOldFrame) {
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3))
- .WillOnce(Return(3));
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(0u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(0u, buffered_frames_->size());
EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
// Ignore this - it matches a past sequence number and we should not see it
// again.
- EXPECT_TRUE(sequencer_->OnFrame(0, "def"));
- EXPECT_EQ(0u, sequencer_->frames()->size());
-}
-
-TEST_F(QuicStreamSequencerTest, RejectOverlyLargeFrame) {
- // TODO(rch): enable when chromium supports EXPECT_DFATAL.
- /*
- EXPECT_DFATAL(sequencer_.reset(new QuicStreamSequencerPeer(2, &stream_)),
- "Setting max frame memory to 2. "
- "Some frames will be impossible to handle.");
-
- EXPECT_DEBUG_DEATH(sequencer_->OnFrame(0, "abc"), "");
- */
-}
-
-TEST_F(QuicStreamSequencerTest, DropFramePastBuffering) {
- sequencer_->SetMemoryLimit(3);
-
- EXPECT_FALSE(sequencer_->OnFrame(3, "abc"));
+ EXPECT_TRUE(OnFrame(0, "def"));
+ EXPECT_EQ(0u, buffered_frames_->size());
}
TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
// Ignore this - it matches a buffered frame.
// Right now there's no checking that the payload is consistent.
- EXPECT_TRUE(sequencer_->OnFrame(0, "def"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "def"));
+ EXPECT_EQ(1u, buffered_frames_->size());
}
TEST_F(QuicStreamSequencerTest, FullFrameConsumed) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(0u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(0u, buffered_frames_->size());
+ EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
+}
+
+TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameConsumed) {
+ sequencer_->SetBlockedUntilFlush();
+
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+ sequencer_->FlushBufferedFrames();
+ EXPECT_EQ(0u, buffered_frames_->size());
+ EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, OnFinRead());
+ EXPECT_TRUE(OnFinFrame(3, "def"));
+}
+
+TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) {
+ sequencer_->SetBlockedUntilFlush();
+
+ EXPECT_TRUE(OnFinFrame(0, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, OnFinRead());
+ sequencer_->FlushBufferedFrames();
+ EXPECT_EQ(0u, buffered_frames_->size());
EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
}
TEST_F(QuicStreamSequencerTest, EmptyFrame) {
EXPECT_CALL(stream_,
CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _));
- EXPECT_FALSE(sequencer_->OnFrame(0, ""));
- EXPECT_EQ(0u, sequencer_->frames()->size());
+ EXPECT_FALSE(OnFrame(0, ""));
+ EXPECT_EQ(0u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
}
TEST_F(QuicStreamSequencerTest, EmptyFinFrame) {
EXPECT_CALL(stream_, OnFinRead());
- EXPECT_TRUE(sequencer_->OnFinFrame(0, ""));
- EXPECT_EQ(0u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFinFrame(0, ""));
+ EXPECT_EQ(0u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
}
TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(2));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
EXPECT_EQ(2u, sequencer_->num_bytes_consumed());
- EXPECT_EQ("c", sequencer_->frames()->find(2)->second);
+ EXPECT_EQ("c", buffered_frames_->find(2)->second);
}
TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(0, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
- EXPECT_EQ("abc", sequencer_->frames()->find(0)->second);
+ EXPECT_EQ("abc", buffered_frames_->find(0)->second);
}
TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) {
- EXPECT_TRUE(sequencer_->OnFrame(3, "abc"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(3, "abc"));
+ EXPECT_EQ(1u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
- EXPECT_EQ("abc", sequencer_->frames()->find(3)->second);
+ EXPECT_EQ("abc", buffered_frames_->find(3)->second);
}
TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) {
// Buffer the first
- EXPECT_TRUE(sequencer_->OnFrame(6, "ghi"));
- EXPECT_EQ(1u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(6, "ghi"));
+ EXPECT_EQ(1u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ(3u, sequencer_->num_bytes_buffered());
// Buffer the second
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_EQ(2u, sequencer_->frames()->size());
+ EXPECT_TRUE(OnFrame(3, "def"));
+ EXPECT_EQ(2u, buffered_frames_->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ(6u, sequencer_->num_bytes_buffered());
InSequence s;
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
@@ -244,190 +252,11 @@ TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("ghi"), 3)).WillOnce(Return(3));
// Ack right away
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
+ EXPECT_TRUE(OnFrame(0, "abc"));
EXPECT_EQ(9u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ(0u, sequencer_->num_bytes_buffered());
- EXPECT_EQ(0u, sequencer_->frames()->size());
-}
-
-TEST_F(QuicStreamSequencerTest, OutOfOrderFramesProcessedWithBuffering) {
- sequencer_->SetMemoryLimit(9);
-
- // Too far to buffer.
- EXPECT_FALSE(sequencer_->OnFrame(9, "jkl"));
-
- // We can afford to buffer this.
- EXPECT_TRUE(sequencer_->OnFrame(6, "ghi"));
- EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
-
- InSequence s;
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
-
- // Ack right away
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
-
- // We should be willing to buffer this now.
- EXPECT_TRUE(sequencer_->OnFrame(9, "jkl"));
- EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
-
- EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("ghi"), 3)).WillOnce(Return(3));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("jkl"), 3)).WillOnce(Return(3));
-
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_EQ(12u, sequencer_->num_bytes_consumed());
- EXPECT_EQ(0u, sequencer_->frames()->size());
-}
-
-TEST_F(QuicStreamSequencerTest, OutOfOrderFramesBlockignWithReadv) {
- sequencer_->SetMemoryLimit(9);
- char buffer[20];
- iovec iov[2];
- iov[0].iov_base = &buffer[0];
- iov[0].iov_len = 1;
- iov[1].iov_base = &buffer[1];
- iov[1].iov_len = 2;
-
- // Push abc - process.
- // Push jkl - buffer (not next data)
- // Push def - don't process.
- // Push mno - drop (too far out)
- // Push ghi - buffer (def not processed)
- // Read 2.
- // Push mno - buffer (not all read)
- // Read all
- // Push pqr - process
-
- InSequence s;
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(0));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("pqr"), 3)).WillOnce(Return(3));
-
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_TRUE(sequencer_->OnFrame(9, "jkl"));
- EXPECT_FALSE(sequencer_->OnFrame(12, "mno"));
- EXPECT_TRUE(sequencer_->OnFrame(6, "ghi"));
-
- // Read 3 bytes.
- EXPECT_EQ(3, sequencer_->Readv(iov, 2));
- EXPECT_EQ(0, strncmp(buffer, "def", 3));
-
- // Now we have space to bufer this.
- EXPECT_TRUE(sequencer_->OnFrame(12, "mno"));
-
- // Read the remaining 9 bytes.
- iov[1].iov_len = 19;
- EXPECT_EQ(9, sequencer_->Readv(iov, 2));
- EXPECT_EQ(0, strncmp(buffer, "ghijklmno", 9));
-
- EXPECT_TRUE(sequencer_->OnFrame(15, "pqr"));
-}
-
-// Same as above, just using a different method for reading.
-TEST_F(QuicStreamSequencerTest, OutOfOrderFramesBlockignWithGetReadableRegion) {
- sequencer_->SetMemoryLimit(9);
-
- InSequence s;
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(0));
- EXPECT_CALL(stream_, ProcessRawData(StrEq("pqr"), 3)).WillOnce(Return(3));
-
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_TRUE(sequencer_->OnFrame(9, "jkl"));
- EXPECT_FALSE(sequencer_->OnFrame(12, "mno"));
- EXPECT_TRUE(sequencer_->OnFrame(6, "ghi"));
-
- // Read 3 bytes.
- const char* expected[] = {"def", "ghi", "jkl"};
- ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected)));
- char buffer[9];
- iovec read_iov = { &buffer[0], 3 };
- ASSERT_EQ(3, sequencer_->Readv(&read_iov, 1));
-
- // Now we have space to bufer this.
- EXPECT_TRUE(sequencer_->OnFrame(12, "mno"));
-
- // Read the remaining 9 bytes.
- const char* expected2[] = {"ghi", "jkl", "mno"};
- ASSERT_TRUE(VerifyReadableRegions(expected2, arraysize(expected2)));
- read_iov.iov_len = 9;
- ASSERT_EQ(9, sequencer_->Readv(&read_iov, 1));
-
- EXPECT_TRUE(sequencer_->OnFrame(15, "pqr"));
-}
-
-// Same as above, just using a different method for reading.
-TEST_F(QuicStreamSequencerTest, MarkConsumed) {
- sequencer_->SetMemoryLimit(9);
-
- InSequence s;
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
-
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_TRUE(sequencer_->OnFrame(6, "ghi"));
-
- // Peek into the data.
- const char* expected[] = {"abc", "def", "ghi"};
- ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected)));
-
- // Consume 1 byte.
- sequencer_->MarkConsumed(1);
- // Verify data.
- const char* expected2[] = {"bc", "def", "ghi"};
- ASSERT_TRUE(VerifyReadableRegions(expected2, arraysize(expected2)));
-
- // Consume 2 bytes.
- sequencer_->MarkConsumed(2);
- // Verify data.
- const char* expected3[] = {"def", "ghi"};
- ASSERT_TRUE(VerifyReadableRegions(expected3, arraysize(expected3)));
-
- // Consume 5 bytes.
- sequencer_->MarkConsumed(5);
- // Verify data.
- const char* expected4[] = {"i"};
- ASSERT_TRUE(VerifyReadableRegions(expected4, arraysize(expected4)));
-}
-
-TEST_F(QuicStreamSequencerTest, MarkConsumedError) {
- // TODO(rch): enable when chromium supports EXPECT_DFATAL.
- /*
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
-
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_TRUE(sequencer_->OnFrame(9, "jklmnopqrstuvwxyz"));
-
- // Peek into the data. Only the first chunk should be readable
- // because of the missing data.
- const char* expected[] = {"abc"};
- ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected)));
-
- // Now, attempt to mark consumed more data than was readable
- // and expect the stream to be closed.
- EXPECT_CALL(stream_, Reset(QUIC_ERROR_PROCESSING_STREAM));
- EXPECT_DFATAL(sequencer_->MarkConsumed(4),
- "Invalid argument to MarkConsumed. num_bytes_consumed_: 3 "
- "end_offset: 4 offset: 9 length: 17");
- */
-}
-
-TEST_F(QuicStreamSequencerTest, MarkConsumedWithMissingPacket) {
- InSequence s;
- EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
-
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- // Missing packet: 6, ghi
- EXPECT_TRUE(sequencer_->OnFrame(9, "jkl"));
-
- const char* expected[] = {"abc", "def"};
- ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected)));
-
- sequencer_->MarkConsumed(6);
+ EXPECT_EQ(0u, buffered_frames_->size());
}
TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) {
@@ -435,64 +264,64 @@ TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) {
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
EXPECT_CALL(stream_, OnFinRead());
- EXPECT_TRUE(sequencer_->OnFinFrame(0, "abc"));
+ EXPECT_TRUE(OnFinFrame(0, "abc"));
- EXPECT_EQ(3u, sequencer_->close_offset());
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
}
TEST_F(QuicStreamSequencerTest, BasicHalfCloseUnorderedWithFlush) {
- sequencer_->OnFinFrame(6, "");
- EXPECT_EQ(6u, sequencer_->close_offset());
+ OnFinFrame(6, "");
+ EXPECT_EQ(6u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
InSequence s;
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3));
EXPECT_CALL(stream_, OnFinRead());
- EXPECT_TRUE(sequencer_->OnFrame(3, "def"));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
+ EXPECT_TRUE(OnFrame(3, "def"));
+ EXPECT_TRUE(OnFrame(0, "abc"));
}
TEST_F(QuicStreamSequencerTest, BasicHalfUnordered) {
- sequencer_->OnFinFrame(3, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
InSequence s;
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
EXPECT_CALL(stream_, OnFinRead());
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
+ EXPECT_TRUE(OnFrame(0, "abc"));
}
TEST_F(QuicStreamSequencerTest, TerminateWithReadv) {
char buffer[3];
- sequencer_->OnFinFrame(3, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
EXPECT_FALSE(sequencer_->IsClosed());
EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
- EXPECT_TRUE(sequencer_->OnFrame(0, "abc"));
+ EXPECT_TRUE(OnFrame(0, "abc"));
- iovec iov = { &buffer[0], 3 };
+ iovec iov = {&buffer[0], 3};
int bytes_read = sequencer_->Readv(&iov, 1);
EXPECT_EQ(3, bytes_read);
EXPECT_TRUE(sequencer_->IsClosed());
}
TEST_F(QuicStreamSequencerTest, MutipleOffsets) {
- sequencer_->OnFinFrame(3, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
- sequencer_->OnFinFrame(5, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(5, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
- sequencer_->OnFinFrame(1, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(1, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
- sequencer_->OnFinFrame(3, "");
- EXPECT_EQ(3u, sequencer_->close_offset());
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
}
class QuicSequencerRandomTest : public QuicStreamSequencerTest {
@@ -544,38 +373,71 @@ TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) {
while (!list_.empty()) {
int index = OneToN(list_.size()) - 1;
- LOG(ERROR) << "Sending index " << index << " "
- << list_[index].second.data();
- EXPECT_TRUE(sequencer_->OnFrame(list_[index].first,
- list_[index].second.data()));
+ LOG(ERROR) << "Sending index " << index << " " << list_[index].second;
+ EXPECT_TRUE(OnFrame(list_[index].first, list_[index].second.data()));
list_.erase(list_.begin() + index);
}
}
-// All frames are processed as soon as we have sequential data.
-// Buffering, so some frames are rejected.
-TEST_F(QuicSequencerRandomTest, RandomFramesDroppingNoBackup) {
- sequencer_->SetMemoryLimit(26);
+TEST_F(QuicStreamSequencerTest, FrameOverlapsBufferedData) {
+ // Ensure that FrameOverlapsBufferedData returns appropriate responses when
+ // there is existing data buffered.
+
+ map<QuicStreamOffset, string>* buffered_frames =
+ QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get());
+
+ const int kBufferedOffset = 10;
+ const int kBufferedDataLength = 3;
+ const int kNewDataLength = 3;
+ IOVector data = MakeIOVector(string(kNewDataLength, '.'));
+
+ // No overlap if no buffered frames.
+ EXPECT_TRUE(buffered_frames_->empty());
+ EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - 1, data)));
+
+ // Add a buffered frame.
+ buffered_frames->insert(
+ make_pair(kBufferedOffset, string(kBufferedDataLength, '.')));
+
+ // New byte range partially overlaps with buffered frame, start offset
+ // preceeding buffered frame.
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - 1, data)));
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - kNewDataLength + 1, data)));
+
+ // New byte range partially overlaps with buffered frame, start offset
+ // inside existing buffered frame.
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset + 1, data)));
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame(
+ 1, false, kBufferedOffset + kBufferedDataLength - 1, data)));
+
+ // New byte range entirely outside of buffered frames, start offset preceeding
+ // buffered frame.
+ EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - kNewDataLength, data)));
+
+ // New byte range entirely outside of buffered frames, start offset later than
+ // buffered frame.
+ EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame(
+ 1, false, kBufferedOffset + kBufferedDataLength, data)));
+}
- InSequence s;
- for (size_t i = 0; i < list_.size(); ++i) {
- string* data = &list_[i].second;
- EXPECT_CALL(stream_, ProcessRawData(StrEq(*data), data->size()))
- .WillOnce(Return(data->size()));
- }
+TEST_F(QuicStreamSequencerTest, DontAcceptOverlappingFrames) {
+ // The peer should never send us non-identical stream frames which contain
+ // overlapping byte ranges - if they do, we close the connection.
- while (!list_.empty()) {
- int index = OneToN(list_.size()) - 1;
- LOG(ERROR) << "Sending index " << index << " "
- << list_[index].second.data();
- bool acked = sequencer_->OnFrame(list_[index].first,
- list_[index].second.data());
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 1, MakeIOVector("hello"));
+ sequencer_->OnStreamFrame(frame1);
- if (acked) {
- list_.erase(list_.begin() + index);
- }
- }
+ QuicStreamFrame frame2(kClientDataStreamId1, false, 2, MakeIOVector("hello"));
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(frame2));
+ EXPECT_CALL(stream_, CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _))
+ .Times(1);
+ sequencer_->OnStreamFrame(frame2);
}
} // namespace
diff --git a/chromium/net/quic/quic_time.cc b/chromium/net/quic/quic_time.cc
index a56775663e4..d467980c43d 100644
--- a/chromium/net/quic/quic_time.cc
+++ b/chromium/net/quic/quic_time.cc
@@ -62,6 +62,20 @@ QuicTime::Delta QuicTime::Delta::Subtract(const Delta& delta) const {
delta.ToMicroseconds());
}
+QuicTime::Delta QuicTime::Delta::Multiply(int i) const {
+ return QuicTime::Delta::FromMicroseconds(ToMicroseconds() * i);
+}
+
+QuicTime::Delta QuicTime::Delta::Multiply(double d) const {
+ return QuicTime::Delta::FromMicroseconds(ToMicroseconds() * d);
+}
+
+// static
+QuicTime::Delta QuicTime::Delta::Max(QuicTime::Delta delta1,
+ QuicTime::Delta delta2) {
+ return delta1 < delta2 ? delta2 : delta1;
+}
+
bool QuicTime::Delta::IsZero() const {
return delta_.InMicroseconds() == 0;
}
@@ -75,6 +89,11 @@ QuicTime QuicTime::Zero() {
return QuicTime(base::TimeTicks());
}
+// static
+QuicTime QuicTime::Max(QuicTime time1, QuicTime time2) {
+ return time1 > time2 ? time1 : time2;
+}
+
QuicTime::QuicTime(base::TimeTicks ticks)
: ticks_(ticks) {
}
diff --git a/chromium/net/quic/quic_time.h b/chromium/net/quic/quic_time.h
index cdb7f83e81e..20cc3088f72 100644
--- a/chromium/net/quic/quic_time.h
+++ b/chromium/net/quic/quic_time.h
@@ -58,6 +58,12 @@ class NET_EXPORT_PRIVATE QuicTime {
Delta Subtract(const Delta& delta) const;
+ Delta Multiply(int i) const;
+ Delta Multiply(double d) const;
+
+ // Returns the later delta of time1 and time2.
+ static Delta Max(Delta delta1, Delta delta2);
+
bool IsZero() const;
bool IsInfinite() const;
@@ -75,6 +81,9 @@ class NET_EXPORT_PRIVATE QuicTime {
// will return false for these times.
static QuicTime Zero();
+ // Returns the later time of time1 and time2.
+ static QuicTime Max(QuicTime time1, QuicTime time2);
+
// Produce the internal value to be used when logging. This value
// represents the number of microseconds since some epoch. It may
// be the UNIX epoch on some platforms. On others, it may
diff --git a/chromium/net/quic/quic_time_test.cc b/chromium/net/quic/quic_time_test.cc
index 18b1de4f3d3..a2e355050c7 100644
--- a/chromium/net/quic/quic_time_test.cc
+++ b/chromium/net/quic/quic_time_test.cc
@@ -48,6 +48,21 @@ TEST(QuicTimeDeltaTest, Subtract) {
QuicTime::Delta::FromMilliseconds(1)));
}
+TEST(QuicTimeDeltaTest, Multiply) {
+ int i = 2;
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000),
+ QuicTime::Delta::FromMilliseconds(2).Multiply(i));
+ double d = 2;
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000),
+ QuicTime::Delta::FromMilliseconds(2).Multiply(d));
+}
+
+TEST(QuicTimeDeltaTest, Max) {
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2000),
+ QuicTime::Delta::Max(QuicTime::Delta::FromMicroseconds(1000),
+ QuicTime::Delta::FromMicroseconds(2000)));
+}
+
TEST(QuicTimeDeltaTest, NotEqual) {
EXPECT_TRUE(QuicTime::Delta::FromSeconds(0) !=
QuicTime::Delta::FromSeconds(1));
@@ -95,6 +110,15 @@ TEST_F(QuicTimeTest, SubtractDelta) {
time.Subtract(QuicTime::Delta::FromMilliseconds(1)));
}
+TEST_F(QuicTimeTest, Max) {
+ QuicTime time_1 = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(1));
+ QuicTime time_2 = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(2));
+
+ EXPECT_EQ(time_2, QuicTime::Max(time_1, time_2));
+}
+
TEST_F(QuicTimeTest, MockClock) {
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
diff --git a/chromium/net/quic/quic_time_wait_list_manager.cc b/chromium/net/quic/quic_time_wait_list_manager.cc
new file mode 100644
index 00000000000..d1f3419e77d
--- /dev/null
+++ b/chromium/net/quic/quic_time_wait_list_manager.cc
@@ -0,0 +1,283 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_time_wait_list_manager.h"
+
+#include <errno.h>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_session.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using std::make_pair;
+
+namespace net {
+
+namespace {
+
+// Time period for which the connection_id should live in time wait state..
+const int kTimeWaitSeconds = 5;
+
+} // namespace
+
+// A very simple alarm that just informs the QuicTimeWaitListManager to clean
+// up old connection_ids. This alarm should be unregistered and deleted before
+// the QuicTimeWaitListManager is deleted.
+class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit ConnectionIdCleanUpAlarm(
+ QuicTimeWaitListManager* time_wait_list_manager)
+ : time_wait_list_manager_(time_wait_list_manager) {}
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ time_wait_list_manager_->CleanUpOldConnectionIds();
+ // Let the time wait manager register the alarm at appropriate time.
+ return QuicTime::Zero();
+ }
+
+ private:
+ // Not owned.
+ QuicTimeWaitListManager* time_wait_list_manager_;
+};
+
+// This class stores pending public reset packets to be sent to clients.
+// server_address - server address on which a packet what was received for
+// a connection_id in time wait state.
+// client_address - address of the client that sent that packet. Needed to send
+// the public reset packet back to the client.
+// packet - the pending public reset packet that is to be sent to the client.
+// created instance takes the ownership of this packet.
+class QuicTimeWaitListManager::QueuedPacket {
+ public:
+ QueuedPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicEncryptedPacket* packet)
+ : server_address_(server_address),
+ client_address_(client_address),
+ packet_(packet) {}
+
+ const IPEndPoint& server_address() const { return server_address_; }
+ const IPEndPoint& client_address() const { return client_address_; }
+ QuicEncryptedPacket* packet() { return packet_.get(); }
+
+ private:
+ const IPEndPoint server_address_;
+ const IPEndPoint client_address_;
+ scoped_ptr<QuicEncryptedPacket> packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
+};
+
+QuicTimeWaitListManager::QuicTimeWaitListManager(
+ QuicPacketWriter* writer,
+ QuicServerSessionVisitor* visitor,
+ QuicConnectionHelperInterface* helper,
+ const QuicVersionVector& supported_versions)
+ : helper_(helper),
+ kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),
+ connection_id_clean_up_alarm_(
+ helper_->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
+ writer_(writer),
+ visitor_(visitor) {
+ SetConnectionIdCleanUpAlarm();
+}
+
+QuicTimeWaitListManager::~QuicTimeWaitListManager() {
+ connection_id_clean_up_alarm_->Cancel();
+ STLDeleteElements(&pending_packets_queue_);
+ for (ConnectionIdMap::iterator it = connection_id_map_.begin();
+ it != connection_id_map_.end();
+ ++it) {
+ delete it->second.close_packet;
+ }
+}
+
+void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
+ QuicConnectionId connection_id,
+ QuicVersion version,
+ QuicEncryptedPacket* close_packet) {
+ DVLOG(1) << "Adding " << connection_id << " to the time wait list.";
+ int num_packets = 0;
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ if (it != connection_id_map_.end()) { // Replace record if it is reinserted.
+ num_packets = it->second.num_packets;
+ delete it->second.close_packet;
+ connection_id_map_.erase(it);
+ }
+ ConnectionIdData data(num_packets,
+ version,
+ helper_->GetClock()->ApproximateNow(),
+ close_packet);
+ connection_id_map_.insert(make_pair(connection_id, data));
+}
+
+bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
+ QuicConnectionId connection_id) const {
+ return ContainsKey(connection_id_map_, connection_id);
+}
+
+QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
+ QuicConnectionId connection_id) {
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ DCHECK(it != connection_id_map_.end());
+ return (it->second).version;
+}
+
+void QuicTimeWaitListManager::OnCanWrite() {
+ while (!pending_packets_queue_.empty()) {
+ QueuedPacket* queued_packet = pending_packets_queue_.front();
+ if (!WriteToWire(queued_packet)) {
+ return;
+ }
+ pending_packets_queue_.pop_front();
+ delete queued_packet;
+ }
+}
+
+void QuicTimeWaitListManager::ProcessPacket(
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber sequence_number,
+ const QuicEncryptedPacket& /*packet*/) {
+ DCHECK(IsConnectionIdInTimeWait(connection_id));
+ DVLOG(1) << "Processing " << connection_id << " in time wait state.";
+ // TODO(satyamshekhar): Think about handling packets from different client
+ // addresses.
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ DCHECK(it != connection_id_map_.end());
+ // Increment the received packet count.
+ ++((it->second).num_packets);
+ if (!ShouldSendResponse((it->second).num_packets)) {
+ return;
+ }
+ if (it->second.close_packet) {
+ QueuedPacket* queued_packet =
+ new QueuedPacket(server_address,
+ client_address,
+ it->second.close_packet->Clone());
+ // Takes ownership of the packet.
+ SendOrQueuePacket(queued_packet);
+ } else {
+ SendPublicReset(server_address,
+ client_address,
+ connection_id,
+ sequence_number);
+ }
+}
+
+// Returns true if the number of packets received for this connection_id is a
+// power of 2 to throttle the number of public reset packets we send to a
+// client.
+bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
+ return (received_packet_count & (received_packet_count - 1)) == 0;
+}
+
+void QuicTimeWaitListManager::SendPublicReset(
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber rejected_sequence_number) {
+ QuicPublicResetPacket packet;
+ packet.public_header.connection_id = connection_id;
+ packet.public_header.reset_flag = true;
+ packet.public_header.version_flag = false;
+ packet.rejected_sequence_number = rejected_sequence_number;
+ // TODO(satyamshekhar): generate a valid nonce for this connection_id.
+ packet.nonce_proof = 1010101;
+ packet.client_address = client_address;
+ QueuedPacket* queued_packet = new QueuedPacket(
+ server_address,
+ client_address,
+ BuildPublicReset(packet));
+ // Takes ownership of the packet.
+ SendOrQueuePacket(queued_packet);
+}
+
+QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset(
+ const QuicPublicResetPacket& packet) {
+ return QuicFramer::BuildPublicResetPacket(packet);
+}
+
+// Either sends the packet and deletes it or makes pending queue the
+// owner of the packet.
+void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
+ if (WriteToWire(packet)) {
+ delete packet;
+ } else {
+ // pending_packets_queue takes the ownership of the queued packet.
+ pending_packets_queue_.push_back(packet);
+ }
+}
+
+bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked(this);
+ return false;
+ }
+ WriteResult result = writer_->WritePacket(
+ queued_packet->packet()->data(),
+ queued_packet->packet()->length(),
+ queued_packet->server_address().address(),
+ queued_packet->client_address());
+ if (result.status == WRITE_STATUS_BLOCKED) {
+ // If blocked and unbuffered, return false to retry sending.
+ DCHECK(writer_->IsWriteBlocked());
+ visitor_->OnWriteBlocked(this);
+ return writer_->IsWriteBlockedDataBuffered();
+ } else if (result.status == WRITE_STATUS_ERROR) {
+ LOG(WARNING) << "Received unknown error while sending reset packet to "
+ << queued_packet->client_address().ToString() << ": "
+ << strerror(result.error_code);
+ }
+ return true;
+}
+
+void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
+ connection_id_clean_up_alarm_->Cancel();
+ QuicTime now = helper_->GetClock()->ApproximateNow();
+ QuicTime next_alarm_time = now;
+ if (!connection_id_map_.empty()) {
+ QuicTime oldest_connection_id =
+ connection_id_map_.begin()->second.time_added;
+ if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
+ next_alarm_time = oldest_connection_id.Add(kTimeWaitPeriod_);
+ } else {
+ LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
+ }
+ } else {
+ // No connection_ids added so none will expire before kTimeWaitPeriod_.
+ next_alarm_time = now.Add(kTimeWaitPeriod_);
+ }
+
+ connection_id_clean_up_alarm_->Set(next_alarm_time);
+}
+
+void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
+ QuicTime now = helper_->GetClock()->ApproximateNow();
+ while (!connection_id_map_.empty()) {
+ ConnectionIdMap::iterator it = connection_id_map_.begin();
+ QuicTime oldest_connection_id = it->second.time_added;
+ if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
+ break;
+ }
+ // This connection_id has lived its age, retire it now.
+ delete it->second.close_packet;
+ connection_id_map_.erase(it);
+ }
+ SetConnectionIdCleanUpAlarm();
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_time_wait_list_manager.h b/chromium/net/quic/quic_time_wait_list_manager.h
new file mode 100644
index 00000000000..4a5fd42b0a3
--- /dev/null
+++ b/chromium/net/quic/quic_time_wait_list_manager.h
@@ -0,0 +1,174 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Handles packets for connection_ids in time wait state by discarding the
+// packet and sending the clients a public reset packet with exponential
+// backoff.
+
+#ifndef NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
+#define NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
+
+#include <deque>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/strings/string_piece.h"
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_packet_writer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class ConnectionIdCleanUpAlarm;
+class QuicServerSessionVisitor;
+
+namespace test {
+class QuicTimeWaitListManagerPeer;
+} // namespace test
+
+// Maintains a list of all connection_ids that have been recently closed. A
+// connection_id lives in this state for kTimeWaitPeriod. All packets received
+// for connection_ids in this state are handed over to the
+// QuicTimeWaitListManager by the QuicDispatcher. Decides whether to send a
+// public reset packet, a copy of the previously sent connection close packet,
+// or nothing to the client which sent a packet with the connection_id in time
+// wait state. After the connection_id expires its time wait period, a new
+// connection/session will be created if a packet is received for this
+// connection_id.
+class QuicTimeWaitListManager : public QuicBlockedWriterInterface {
+ public:
+ // writer - the entity that writes to the socket. (Owned by the dispatcher)
+ // visitor - the entity that manages blocked writers. (The dispatcher)
+ // helper - used to run clean up alarms. (Owned by the owner of the server)
+ QuicTimeWaitListManager(QuicPacketWriter* writer,
+ QuicServerSessionVisitor* visitor,
+ QuicConnectionHelperInterface* helper,
+ const QuicVersionVector& supported_versions);
+ virtual ~QuicTimeWaitListManager();
+
+ // Adds the given connection_id to time wait state for kTimeWaitPeriod.
+ // Henceforth, any packet bearing this connection_id should not be processed
+ // while the connection_id remains in this list. If a non-NULL |close_packet|
+ // is provided, it is sent again when packets are received for added
+ // connection_ids. If NULL, a public reset packet is sent with the specified
+ // |version|. DCHECKs that connection_id is not already on the list.
+ void AddConnectionIdToTimeWait(QuicConnectionId connection_id,
+ QuicVersion version,
+ QuicEncryptedPacket* close_packet); // Owned.
+
+ // Returns true if the connection_id is in time wait state, false otherwise.
+ // Packets received for this connection_id should not lead to creation of new
+ // QuicSessions.
+ bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) const;
+
+ // Called when a packet is received for a connection_id that is in time wait
+ // state. Sends a public reset packet to the client which sent this
+ // connection_id. Sending of the public reset packet is throttled by using
+ // exponential back off. DCHECKs for the connection_id to be in time wait
+ // state. virtual to override in tests.
+ virtual void ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber sequence_number,
+ const QuicEncryptedPacket& packet);
+
+ // Called by the dispatcher when the underlying socket becomes writable again,
+ // since we might need to send pending public reset packets which we didn't
+ // send because the underlying socket was write blocked.
+ virtual void OnCanWrite() OVERRIDE;
+
+ // Used to delete connection_id entries that have outlived their time wait
+ // period.
+ void CleanUpOldConnectionIds();
+
+ // Given a ConnectionId that exists in the time wait list, returns the
+ // QuicVersion associated with it.
+ QuicVersion GetQuicVersionFromConnectionId(QuicConnectionId connection_id);
+
+ protected:
+ virtual QuicEncryptedPacket* BuildPublicReset(
+ const QuicPublicResetPacket& packet);
+
+ private:
+ friend class test::QuicTimeWaitListManagerPeer;
+
+ // Internal structure to store pending public reset packets.
+ class QueuedPacket;
+
+ // Decides if a packet should be sent for this connection_id based on the
+ // number of received packets.
+ bool ShouldSendResponse(int received_packet_count);
+
+ // Creates a public reset packet and sends it or queues it to be sent later.
+ void SendPublicReset(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber rejected_sequence_number);
+
+ // Either sends the packet and deletes it or makes pending_packets_queue_ the
+ // owner of the packet.
+ void SendOrQueuePacket(QueuedPacket* packet);
+
+ // Sends the packet out. Returns true if the packet was successfully consumed.
+ // If the writer got blocked and did not buffer the packet, we'll need to keep
+ // the packet and retry sending. In case of all other errors we drop the
+ // packet.
+ bool WriteToWire(QueuedPacket* packet);
+
+ // Register the alarm to wake up at appropriate time.
+ void SetConnectionIdCleanUpAlarm();
+
+ // A map from a recently closed connection_id to the number of packets
+ // received after the termination of the connection bound to the
+ // connection_id.
+ struct ConnectionIdData {
+ ConnectionIdData(int num_packets_,
+ QuicVersion version_,
+ QuicTime time_added_,
+ QuicEncryptedPacket* close_packet)
+ : num_packets(num_packets_),
+ version(version_),
+ time_added(time_added_),
+ close_packet(close_packet) {}
+ int num_packets;
+ QuicVersion version;
+ QuicTime time_added;
+ QuicEncryptedPacket* close_packet;
+ };
+
+ // linked_hash_map allows lookup by ConnectionId and traversal in add order.
+ typedef linked_hash_map<QuicConnectionId, ConnectionIdData> ConnectionIdMap;
+ ConnectionIdMap connection_id_map_;
+
+ // Pending public reset packets that need to be sent out to the client
+ // when we are given a chance to write by the dispatcher.
+ std::deque<QueuedPacket*> pending_packets_queue_;
+
+ // Used to schedule alarms to delete old connection_ids which have been in the
+ // list for too long.
+ QuicConnectionHelperInterface* helper_;
+
+ // Time period for which connection_ids should remain in time wait state.
+ const QuicTime::Delta kTimeWaitPeriod_;
+
+ // Alarm registered with the connection helper to clean up connection_ids that
+ // have
+ // out lived their duration in time wait state.
+ scoped_ptr<QuicAlarm> connection_id_clean_up_alarm_;
+
+ // Interface that writes given buffer to the socket.
+ QuicPacketWriter* writer_;
+
+ // Interface that manages blocked writers.
+ QuicServerSessionVisitor* visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicTimeWaitListManager);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
diff --git a/chromium/net/quic/quic_types.cc b/chromium/net/quic/quic_types.cc
new file mode 100644
index 00000000000..cdfb36dbccb
--- /dev/null
+++ b/chromium/net/quic/quic_types.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_types.h"
+
+using std::ostream;
+
+namespace net {
+
+QuicConsumedData::QuicConsumedData(size_t bytes_consumed,
+ bool fin_consumed)
+ : bytes_consumed(bytes_consumed),
+ fin_consumed(fin_consumed) {
+}
+
+ostream& operator<<(ostream& os, const QuicConsumedData& s) {
+ os << "bytes_consumed: " << s.bytes_consumed
+ << " fin_consumed: " << s.fin_consumed;
+ return os;
+}
+
+WriteResult::WriteResult()
+ : status(WRITE_STATUS_ERROR),
+ bytes_written(0) {
+}
+
+WriteResult::WriteResult(WriteStatus status,
+ int bytes_written_or_error_code)
+ : status(status),
+ bytes_written(bytes_written_or_error_code) {
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_types.h b/chromium/net/quic/quic_types.h
new file mode 100644
index 00000000000..01415bc3607
--- /dev/null
+++ b/chromium/net/quic/quic_types.h
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_TYPES_H_
+#define NET_QUIC_QUIC_TYPES_H_
+
+// This header defines some basic types that don't depend on quic_protocol.h,
+// so that classes not directly related to the protocol wire format can avoid
+// including quic_protocol.h.
+
+#include <stddef.h>
+#include <ostream>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// A struct for functions which consume data payloads and fins.
+struct NET_EXPORT_PRIVATE QuicConsumedData {
+ QuicConsumedData(size_t bytes_consumed, bool fin_consumed);
+
+ // By default, gtest prints the raw bytes of an object. The bool data
+ // member causes this object to have padding bytes, which causes the
+ // default gtest object printer to read uninitialize memory. So we need
+ // to teach gtest how to print this object.
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicConsumedData& s);
+
+ // How many bytes were consumed.
+ size_t bytes_consumed;
+
+ // True if an incoming fin was consumed.
+ bool fin_consumed;
+};
+
+// QuicAsyncStatus enumerates the possible results of an asynchronous
+// operation.
+enum QuicAsyncStatus {
+ QUIC_SUCCESS = 0,
+ QUIC_FAILURE = 1,
+ // QUIC_PENDING results from an operation that will occur asynchonously. When
+ // the operation is complete, a callback's |Run| method will be called.
+ QUIC_PENDING = 2,
+};
+
+// TODO(wtc): see if WriteStatus can be replaced by QuicAsyncStatus.
+enum WriteStatus {
+ WRITE_STATUS_OK,
+ WRITE_STATUS_BLOCKED,
+ WRITE_STATUS_ERROR,
+};
+
+// A struct used to return the result of write calls including either the number
+// of bytes written or the error code, depending upon the status.
+struct NET_EXPORT_PRIVATE WriteResult {
+ WriteResult(WriteStatus status, int bytes_written_or_error_code);
+ WriteResult();
+
+ WriteStatus status;
+ union {
+ int bytes_written; // only valid when status is WRITE_STATUS_OK
+ int error_code; // only valid when status is WRITE_STATUS_ERROR
+ };
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_TYPES_H_
diff --git a/chromium/net/quic/quic_unacked_packet_map.cc b/chromium/net/quic/quic_unacked_packet_map.cc
new file mode 100644
index 00000000000..e9dd1368f6a
--- /dev/null
+++ b/chromium/net/quic/quic_unacked_packet_map.cc
@@ -0,0 +1,323 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_unacked_packet_map.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_utils_chromium.h"
+
+using std::max;
+
+namespace net {
+
+QuicUnackedPacketMap::QuicUnackedPacketMap()
+ : largest_sent_packet_(0),
+ largest_observed_(0),
+ bytes_in_flight_(0),
+ pending_crypto_packet_count_(0) {
+}
+
+QuicUnackedPacketMap::~QuicUnackedPacketMap() {
+ for (UnackedPacketMap::iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ delete it->second.retransmittable_frames;
+ // Only delete all_transmissions once, for the newest packet.
+ if (it->first == *it->second.all_transmissions->rbegin()) {
+ delete it->second.all_transmissions;
+ }
+ }
+}
+
+// TODO(ianswett): Combine this method with OnPacketSent once packets are always
+// sent in order and the connection tracks RetransmittableFrames for longer.
+void QuicUnackedPacketMap::AddPacket(
+ const SerializedPacket& serialized_packet) {
+ if (!unacked_packets_.empty()) {
+ bool is_old_packet = unacked_packets_.rbegin()->first >=
+ serialized_packet.sequence_number;
+ LOG_IF(DFATAL, is_old_packet) << "Old packet serialized: "
+ << serialized_packet.sequence_number
+ << " vs: "
+ << unacked_packets_.rbegin()->first;
+ }
+
+ unacked_packets_[serialized_packet.sequence_number] =
+ TransmissionInfo(serialized_packet.retransmittable_frames,
+ serialized_packet.sequence_number,
+ serialized_packet.sequence_number_length);
+ if (serialized_packet.retransmittable_frames != NULL &&
+ serialized_packet.retransmittable_frames->HasCryptoHandshake()
+ == IS_HANDSHAKE) {
+ ++pending_crypto_packet_count_;
+ }
+}
+
+void QuicUnackedPacketMap::OnRetransmittedPacket(
+ QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number,
+ TransmissionType transmission_type) {
+ DCHECK(ContainsKey(unacked_packets_, old_sequence_number));
+ DCHECK(unacked_packets_.empty() ||
+ unacked_packets_.rbegin()->first < new_sequence_number);
+
+ // TODO(ianswett): Discard and lose the packet lazily instead of immediately.
+ TransmissionInfo* transmission_info =
+ FindOrNull(unacked_packets_, old_sequence_number);
+ RetransmittableFrames* frames = transmission_info->retransmittable_frames;
+ LOG_IF(DFATAL, frames == NULL) << "Attempt to retransmit packet with no "
+ << "retransmittable frames: "
+ << old_sequence_number;
+
+ // We keep the old packet in the unacked packet list until it, or one of
+ // the retransmissions of it are acked.
+ transmission_info->retransmittable_frames = NULL;
+ unacked_packets_[new_sequence_number] =
+ TransmissionInfo(frames,
+ new_sequence_number,
+ transmission_info->sequence_number_length,
+ transmission_type,
+ transmission_info->all_transmissions);
+}
+
+void QuicUnackedPacketMap::ClearPreviousRetransmissions(size_t num_to_clear) {
+ UnackedPacketMap::iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end() && num_to_clear > 0) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ // If this packet is in flight, or has retransmittable data, then there is
+ // no point in clearing out any further packets, because they would not
+ // affect the high water mark.
+ if (it->second.in_flight || it->second.retransmittable_frames != NULL) {
+ break;
+ }
+
+ it->second.all_transmissions->erase(sequence_number);
+ LOG_IF(DFATAL, it->second.all_transmissions->empty())
+ << "Previous retransmissions must have a newer transmission.";
+ ++it;
+ unacked_packets_.erase(sequence_number);
+ --num_to_clear;
+ }
+}
+
+bool QuicUnackedPacketMap::HasRetransmittableFrames(
+ QuicPacketSequenceNumber sequence_number) const {
+ const TransmissionInfo* transmission_info =
+ FindOrNull(unacked_packets_, sequence_number);
+ if (transmission_info == NULL) {
+ return false;
+ }
+
+ return transmission_info->retransmittable_frames != NULL;
+}
+
+void QuicUnackedPacketMap::NackPacket(QuicPacketSequenceNumber sequence_number,
+ size_t min_nacks) {
+ UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number);
+ if (it == unacked_packets_.end()) {
+ LOG(DFATAL) << "NackPacket called for packet that is not unacked: "
+ << sequence_number;
+ return;
+ }
+
+ it->second.nack_count = max(min_nacks, it->second.nack_count);
+}
+
+void QuicUnackedPacketMap::RemoveRetransmittability(
+ QuicPacketSequenceNumber sequence_number) {
+ UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number);
+ if (it == unacked_packets_.end()) {
+ DVLOG(1) << "packet is not in unacked_packets: " << sequence_number;
+ return;
+ }
+ SequenceNumberSet* all_transmissions = it->second.all_transmissions;
+ // TODO(ianswett): Consider optimizing this for lone packets.
+ // TODO(ianswett): Consider adding a check to ensure there are retransmittable
+ // frames associated with this packet.
+ for (SequenceNumberSet::reverse_iterator it = all_transmissions->rbegin();
+ it != all_transmissions->rend(); ++it) {
+ TransmissionInfo* transmission_info = FindOrNull(unacked_packets_, *it);
+ if (transmission_info == NULL) {
+ LOG(DFATAL) << "All transmissions in all_transmissions must be present "
+ << "in the unacked packet map.";
+ continue;
+ }
+ MaybeRemoveRetransmittableFrames(transmission_info);
+ if (*it <= largest_observed_ && !transmission_info->in_flight) {
+ unacked_packets_.erase(*it);
+ } else {
+ transmission_info->all_transmissions = new SequenceNumberSet();
+ transmission_info->all_transmissions->insert(*it);
+ }
+ }
+
+ delete all_transmissions;
+}
+
+void QuicUnackedPacketMap::MaybeRemoveRetransmittableFrames(
+ TransmissionInfo* transmission_info) {
+ if (transmission_info->retransmittable_frames != NULL) {
+ if (transmission_info->retransmittable_frames->HasCryptoHandshake()
+ == IS_HANDSHAKE) {
+ --pending_crypto_packet_count_;
+ }
+ delete transmission_info->retransmittable_frames;
+ transmission_info->retransmittable_frames = NULL;
+ }
+}
+
+void QuicUnackedPacketMap::IncreaseLargestObserved(
+ QuicPacketSequenceNumber largest_observed) {
+ DCHECK_LT(largest_observed_, largest_observed);
+ largest_observed_ = largest_observed;
+ UnackedPacketMap::iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end() && it->first <= largest_observed_) {
+ if (!IsPacketUseless(it)) {
+ ++it;
+ continue;
+ }
+ delete it->second.all_transmissions;
+ QuicPacketSequenceNumber sequence_number = it->first;
+ ++it;
+ unacked_packets_.erase(sequence_number);
+ }
+}
+
+bool QuicUnackedPacketMap::IsPacketUseless(
+ UnackedPacketMap::const_iterator it) const {
+ return it->first <= largest_observed_ &&
+ !it->second.in_flight &&
+ it->second.retransmittable_frames == NULL &&
+ it->second.all_transmissions->size() == 1;
+}
+
+bool QuicUnackedPacketMap::IsUnacked(
+ QuicPacketSequenceNumber sequence_number) const {
+ return ContainsKey(unacked_packets_, sequence_number);
+}
+
+void QuicUnackedPacketMap::RemoveFromInFlight(
+ QuicPacketSequenceNumber sequence_number) {
+ UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number);
+ if (it == unacked_packets_.end()) {
+ LOG(DFATAL) << "RemoveFromFlight called for packet that is not unacked: "
+ << sequence_number;
+ return;
+ }
+ if (it->second.in_flight) {
+ LOG_IF(DFATAL, bytes_in_flight_ < it->second.bytes_sent);
+ bytes_in_flight_ -= it->second.bytes_sent;
+ it->second.in_flight = false;
+ }
+ if (IsPacketUseless(it)) {
+ delete it->second.all_transmissions;
+ unacked_packets_.erase(it);
+ }
+}
+
+bool QuicUnackedPacketMap::HasUnackedPackets() const {
+ return !unacked_packets_.empty();
+}
+
+bool QuicUnackedPacketMap::HasInFlightPackets() const {
+ return bytes_in_flight_ > 0;
+}
+
+const TransmissionInfo& QuicUnackedPacketMap::GetTransmissionInfo(
+ QuicPacketSequenceNumber sequence_number) const {
+ return unacked_packets_.find(sequence_number)->second;
+}
+
+QuicTime QuicUnackedPacketMap::GetLastPacketSentTime() const {
+ UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin();
+ while (it != unacked_packets_.rend()) {
+ if (it->second.in_flight) {
+ LOG_IF(DFATAL, it->second.sent_time == QuicTime::Zero())
+ << "Sent time can never be zero for a packet in flight.";
+ return it->second.sent_time;
+ }
+ ++it;
+ }
+ LOG(DFATAL) << "GetLastPacketSentTime requires in flight packets.";
+ return QuicTime::Zero();
+}
+
+QuicTime QuicUnackedPacketMap::GetFirstInFlightPacketSentTime() const {
+ UnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end() && !it->second.in_flight) {
+ ++it;
+ }
+ if (it == unacked_packets_.end()) {
+ LOG(DFATAL) << "GetFirstInFlightPacketSentTime requires in flight packets.";
+ return QuicTime::Zero();
+ }
+ return it->second.sent_time;
+}
+
+size_t QuicUnackedPacketMap::GetNumUnackedPackets() const {
+ return unacked_packets_.size();
+}
+
+bool QuicUnackedPacketMap::HasMultipleInFlightPackets() const {
+ size_t num_in_flight = 0;
+ for (UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin();
+ it != unacked_packets_.rend(); ++it) {
+ if (it->second.in_flight) {
+ ++num_in_flight;
+ }
+ if (num_in_flight > 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QuicUnackedPacketMap::HasPendingCryptoPackets() const {
+ return pending_crypto_packet_count_ > 0;
+}
+
+bool QuicUnackedPacketMap::HasUnackedRetransmittableFrames() const {
+ for (UnackedPacketMap::const_reverse_iterator it =
+ unacked_packets_.rbegin(); it != unacked_packets_.rend(); ++it) {
+ if (it->second.in_flight && it->second.retransmittable_frames) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QuicPacketSequenceNumber
+QuicUnackedPacketMap::GetLeastUnackedSentPacket() const {
+ if (unacked_packets_.empty()) {
+ // If there are no unacked packets, return 0.
+ return 0;
+ }
+
+ return unacked_packets_.begin()->first;
+}
+
+void QuicUnackedPacketMap::SetSent(QuicPacketSequenceNumber sequence_number,
+ QuicTime sent_time,
+ QuicByteCount bytes_sent,
+ bool set_in_flight) {
+ DCHECK_LT(0u, sequence_number);
+ UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number);
+ if (it == unacked_packets_.end()) {
+ LOG(DFATAL) << "OnPacketSent called for packet that is not unacked: "
+ << sequence_number;
+ return;
+ }
+ DCHECK(!it->second.in_flight);
+
+ largest_sent_packet_ = max(sequence_number, largest_sent_packet_);
+ it->second.sent_time = sent_time;
+ if (set_in_flight) {
+ bytes_in_flight_ += bytes_sent;
+ it->second.bytes_sent = bytes_sent;
+ it->second.in_flight = true;
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_unacked_packet_map.h b/chromium/net/quic/quic_unacked_packet_map.h
new file mode 100644
index 00000000000..ae72548b9b4
--- /dev/null
+++ b/chromium/net/quic/quic_unacked_packet_map.h
@@ -0,0 +1,152 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_UNACKED_PACKET_MAP_H_
+#define NET_QUIC_QUIC_UNACKED_PACKET_MAP_H_
+
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+// Class which tracks unacked packets for three purposes:
+// 1) Track retransmittable data, including multiple transmissions of frames.
+// 2) Track packets and bytes in flight for congestion control.
+// 3) Track sent time of packets to provide RTT measurements from acks.
+class NET_EXPORT_PRIVATE QuicUnackedPacketMap {
+ public:
+ QuicUnackedPacketMap();
+ ~QuicUnackedPacketMap();
+
+ // Adds |serialized_packet| to the map. Does not mark it in flight.
+ void AddPacket(const SerializedPacket& serialized_packet);
+
+ // Called when a packet is retransmitted with a new sequence number.
+ // |old_sequence_number| will remain unacked, but will have no
+ // retransmittable data associated with it. |new_sequence_number| will
+ // be both unacked and associated with retransmittable data.
+ void OnRetransmittedPacket(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number,
+ TransmissionType transmission_type);
+
+ // Returns true if the packet |sequence_number| is unacked.
+ bool IsUnacked(QuicPacketSequenceNumber sequence_number) const;
+
+ // Sets the nack count to the max of the current nack count and |min_nacks|.
+ void NackPacket(QuicPacketSequenceNumber sequence_number,
+ size_t min_nacks);
+
+ // Marks |sequence_number| as no longer in flight.
+ void RemoveFromInFlight(QuicPacketSequenceNumber sequence_number);
+
+ // Returns true if the unacked packet |sequence_number| has retransmittable
+ // frames. This will return false if the packet has been acked, if a
+ // previous transmission of this packet was ACK'd, or if this packet has been
+ // retransmitted as with different sequence number, or if the packet never
+ // had any retransmittable packets in the first place.
+ bool HasRetransmittableFrames(QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns true if there are any unacked packets.
+ bool HasUnackedPackets() const;
+
+ // Returns true if there are any unacked packets which have retransmittable
+ // frames.
+ bool HasUnackedRetransmittableFrames() const;
+
+ // Returns the largest sequence number that has been sent.
+ QuicPacketSequenceNumber largest_sent_packet() const {
+ return largest_sent_packet_;
+ }
+
+ // Returns the sum of bytes from all packets in flight.
+ QuicByteCount bytes_in_flight() const {
+ return bytes_in_flight_;
+ }
+
+ // Returns the smallest sequence number of a serialized packet which has not
+ // been acked by the peer. If there are no unacked packets, returns 0.
+ QuicPacketSequenceNumber GetLeastUnackedSentPacket() const;
+
+ // Sets a packet as sent with the sent time |sent_time|. Marks the packet
+ // as in flight if |set_in_flight| is true.
+ // Packets marked as in flight are expected to be marked as missing when they
+ // don't arrive, indicating the need for retransmission.
+ void SetSent(QuicPacketSequenceNumber sequence_number,
+ QuicTime sent_time,
+ QuicByteCount bytes_sent,
+ bool set_in_flight);
+
+ // Clears up to |num_to_clear| previous transmissions in order to make room
+ // in the ack frame for new acks.
+ void ClearPreviousRetransmissions(size_t num_to_clear);
+
+ typedef linked_hash_map<QuicPacketSequenceNumber,
+ TransmissionInfo> UnackedPacketMap;
+
+ typedef UnackedPacketMap::const_iterator const_iterator;
+
+ const_iterator begin() const { return unacked_packets_.begin(); }
+ const_iterator end() const { return unacked_packets_.end(); }
+
+ // Returns true if there are unacked packets that are in flight.
+ bool HasInFlightPackets() const;
+
+ // Returns the TransmissionInfo associated with |sequence_number|, which
+ // must be unacked.
+ const TransmissionInfo& GetTransmissionInfo(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns the time that the last unacked packet was sent.
+ QuicTime GetLastPacketSentTime() const;
+
+ // Returns the time that the first in flight packet was sent.
+ QuicTime GetFirstInFlightPacketSentTime() const;
+
+ // Returns the number of unacked packets.
+ size_t GetNumUnackedPackets() const;
+
+ // Returns true if there are multiple packets in flight.
+ bool HasMultipleInFlightPackets() const;
+
+ // Returns true if there are any pending crypto packets.
+ bool HasPendingCryptoPackets() const;
+
+ // Removes any retransmittable frames from this transmission or an associated
+ // transmission. It removes now useless transmissions, and disconnects any
+ // other packets from other transmissions.
+ void RemoveRetransmittability(QuicPacketSequenceNumber sequence_number);
+
+ // Increases the largest observed. Any packets less or equal to
+ // |largest_acked_packet| are discarded if they are only for the RTT purposes.
+ void IncreaseLargestObserved(QuicPacketSequenceNumber largest_observed);
+
+ private:
+ void MaybeRemoveRetransmittableFrames(TransmissionInfo* transmission_info);
+
+ // Returns true if the packet no longer has a purpose in the map.
+ bool IsPacketUseless(UnackedPacketMap::const_iterator it) const;
+
+ QuicPacketSequenceNumber largest_sent_packet_;
+ QuicPacketSequenceNumber largest_observed_;
+
+ // Newly serialized retransmittable and fec packets are added to this map,
+ // which contains owning pointers to any contained frames. If a packet is
+ // retransmitted, this map will contain entries for both the old and the new
+ // packet. The old packet's retransmittable frames entry will be NULL, while
+ // the new packet's entry will contain the frames to retransmit.
+ // If the old packet is acked before the new packet, then the old entry will
+ // be removed from the map and the new entry's retransmittable frames will be
+ // set to NULL.
+ UnackedPacketMap unacked_packets_;
+
+ size_t bytes_in_flight_;
+ // Number of retransmittable crypto handshake packets.
+ size_t pending_crypto_packet_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicUnackedPacketMap);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_UNACKED_PACKET_MAP_H_
diff --git a/chromium/net/quic/quic_unacked_packet_map_test.cc b/chromium/net/quic/quic_unacked_packet_map_test.cc
new file mode 100644
index 00000000000..c5264fd16e9
--- /dev/null
+++ b/chromium/net/quic/quic_unacked_packet_map_test.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_unacked_packet_map.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+// Default packet length.
+const uint32 kDefaultAckLength = 50;
+const uint32 kDefaultLength = 1000;
+
+class QuicUnackedPacketMapTest : public ::testing::Test {
+ protected:
+ QuicUnackedPacketMapTest()
+ : now_(QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1000))) {
+ }
+
+ SerializedPacket CreateRetransmittablePacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return SerializedPacket(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER, NULL,
+ 0, new RetransmittableFrames());
+ }
+
+ SerializedPacket CreateNonRetransmittablePacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return SerializedPacket(
+ sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL);
+ }
+
+ void VerifyPendingPackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ if (num_packets == 0) {
+ EXPECT_FALSE(unacked_packets_.HasInFlightPackets());
+ EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets());
+ return;
+ }
+ if (num_packets == 1) {
+ EXPECT_TRUE(unacked_packets_.HasInFlightPackets());
+ EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets());
+ }
+ for (size_t i = 0; i < num_packets; ++i) {
+ ASSERT_TRUE(unacked_packets_.IsUnacked(packets[i]));
+ EXPECT_TRUE(unacked_packets_.GetTransmissionInfo(packets[i]).in_flight);
+ }
+ }
+
+ void VerifyUnackedPackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ if (num_packets == 0) {
+ EXPECT_FALSE(unacked_packets_.HasUnackedPackets());
+ EXPECT_FALSE(unacked_packets_.HasUnackedRetransmittableFrames());
+ return;
+ }
+ EXPECT_TRUE(unacked_packets_.HasUnackedPackets());
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(unacked_packets_.IsUnacked(packets[i])) << packets[i];
+ }
+ }
+
+ void VerifyRetransmittablePackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ size_t num_retransmittable_packets = 0;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ if (it->second.retransmittable_frames != NULL) {
+ ++num_retransmittable_packets;
+ }
+ }
+ EXPECT_EQ(num_packets, num_retransmittable_packets);
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(unacked_packets_.HasRetransmittableFrames(packets[i]))
+ << " packets[" << i << "]:" << packets[i];
+ }
+ }
+
+ QuicUnackedPacketMap unacked_packets_;
+ QuicTime now_;
+};
+
+TEST_F(QuicUnackedPacketMapTest, RttOnly) {
+ // Acks are only tracked for RTT measurement purposes.
+ unacked_packets_.AddPacket(CreateNonRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultAckLength, false);
+
+ QuicPacketSequenceNumber unacked[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(NULL, 0);
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.IncreaseLargestObserved(1);
+ VerifyUnackedPackets(NULL, 0);
+ VerifyPendingPackets(NULL, 0);
+ VerifyRetransmittablePackets(NULL, 0);
+}
+
+TEST_F(QuicUnackedPacketMapTest, RetransmittableInflightAndRtt) {
+ // Simulate a retransmittable packet being sent and acked.
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(unacked, arraysize(unacked));
+
+ unacked_packets_.RemoveRetransmittability(1);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.IncreaseLargestObserved(1);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.RemoveFromInFlight(1);
+ VerifyUnackedPackets(NULL, 0);
+ VerifyPendingPackets(NULL, 0);
+ VerifyRetransmittablePackets(NULL, 0);
+}
+
+TEST_F(QuicUnackedPacketMapTest, RetransmittedPacket) {
+ // Simulate a retransmittable packet being sent, retransmitted, and the first
+ // transmission being acked.
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultLength, true);
+ unacked_packets_.OnRetransmittedPacket(1, 2, LOSS_RETRANSMISSION);
+ unacked_packets_.SetSent(2, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 2 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+
+ unacked_packets_.RemoveRetransmittability(1);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.IncreaseLargestObserved(2);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyPendingPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.RemoveFromInFlight(2);
+ QuicPacketSequenceNumber unacked2[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked2));
+ VerifyPendingPackets(unacked, arraysize(unacked2));
+ VerifyRetransmittablePackets(NULL, 0);
+
+ unacked_packets_.RemoveFromInFlight(1);
+ VerifyUnackedPackets(NULL, 0);
+ VerifyPendingPackets(NULL, 0);
+ VerifyRetransmittablePackets(NULL, 0);
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_utils.cc b/chromium/net/quic/quic_utils.cc
index 0aff3772c26..ae4bb2eedc9 100644
--- a/chromium/net/quic/quic_utils.cc
+++ b/chromium/net/quic/quic_utils.cc
@@ -8,11 +8,12 @@
#include <algorithm>
+#include "base/basictypes.h"
#include "base/logging.h"
#include "base/port.h"
#include "base/strings/stringprintf.h"
#include "base/strings/string_number_conversions.h"
-#include "net/spdy/write_blocked_list.h"
+#include "net/quic/quic_write_blocked_list.h"
using base::StringPiece;
using std::string;
@@ -137,6 +138,7 @@ const char* QuicUtils::StreamErrorToString(QuicRstStreamErrorCode error) {
RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD);
RETURN_STRING_LITERAL(QUIC_STREAM_PEER_GOING_AWAY);
RETURN_STRING_LITERAL(QUIC_STREAM_CANCELLED);
+ RETURN_STRING_LITERAL(QUIC_RST_FLOW_CONTROL_ACCOUNTING);
RETURN_STRING_LITERAL(QUIC_STREAM_LAST_ERROR);
}
// Return a default value so that we return this when |error| doesn't match
@@ -156,9 +158,13 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_MISSING_PAYLOAD);
RETURN_STRING_LITERAL(QUIC_INVALID_FEC_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_UNENCRYPTED_STREAM_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_RST_STREAM_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_CONNECTION_CLOSE_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_GOAWAY_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_WINDOW_UPDATE_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_BLOCKED_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STOP_WAITING_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
@@ -187,7 +193,6 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS);
RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET);
RETURN_STRING_LITERAL(QUIC_INVALID_VERSION);
- RETURN_STRING_LITERAL(QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID);
RETURN_STRING_LITERAL(QUIC_INVALID_NEGOTIATED_VALUE);
RETURN_STRING_LITERAL(QUIC_DECOMPRESSION_FAILURE);
@@ -196,6 +201,11 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_PACKET_WRITE_ERROR);
RETURN_STRING_LITERAL(QUIC_PACKET_READ_ERROR);
RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_FRAME);
+ RETURN_STRING_LITERAL(QUIC_INVALID_HEADERS_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_IP_POOLED);
RETURN_STRING_LITERAL(QUIC_PROOF_INVALID);
RETURN_STRING_LITERAL(QUIC_CRYPTO_DUPLICATE_TAG);
RETURN_STRING_LITERAL(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT);
@@ -226,6 +236,19 @@ const char* QuicUtils::EncryptionLevelToString(EncryptionLevel level) {
}
// static
+const char* QuicUtils::TransmissionTypeToString(TransmissionType type) {
+ switch (type) {
+ RETURN_STRING_LITERAL(NOT_RETRANSMISSION);
+ RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMISSION);
+ RETURN_STRING_LITERAL(LOSS_RETRANSMISSION);
+ RETURN_STRING_LITERAL(ALL_UNACKED_RETRANSMISSION);
+ RETURN_STRING_LITERAL(RTO_RETRANSMISSION);
+ RETURN_STRING_LITERAL(TLP_RETRANSMISSION);
+ }
+ return "INVALID_TRANSMISSION_TYPE";
+}
+
+// static
string QuicUtils::TagToString(QuicTag tag) {
char chars[4];
bool ascii = true;
@@ -284,12 +307,12 @@ string QuicUtils::StringToHexASCIIDump(StringPiece in_buffer) {
// static
QuicPriority QuicUtils::LowestPriority() {
- return static_cast<QuicPriority>(kLowestPriority);
+ return QuicWriteBlockedList::kLowestPriority;
}
// static
QuicPriority QuicUtils::HighestPriority() {
- return static_cast<QuicPriority>(kHighestPriority);
+ return QuicWriteBlockedList::kHighestPriority;
}
} // namespace net
diff --git a/chromium/net/quic/quic_utils.h b/chromium/net/quic/quic_utils.h
index 23f3b53a611..43f8d350ea6 100644
--- a/chromium/net/quic/quic_utils.h
+++ b/chromium/net/quic/quic_utils.h
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Some helpers for quic
+// Some helpers for quic.
#ifndef NET_QUIC_QUIC_UTILS_H_
#define NET_QUIC_QUIC_UTILS_H_
@@ -20,7 +20,7 @@ class NET_EXPORT_PRIVATE QuicUtils {
PEER_PRIORITY,
};
- // returns the 64 bit FNV1a hash of the data. See
+ // Returns the 64 bit FNV1a hash of the data. See
// http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
static uint64 FNV1a_64_Hash(const char* data, int len);
@@ -59,6 +59,9 @@ class NET_EXPORT_PRIVATE QuicUtils {
// Returns the level of encryption as a char*
static const char* EncryptionLevelToString(EncryptionLevel level);
+ // Returns TransmissionType as a char*
+ static const char* TransmissionTypeToString(TransmissionType type);
+
// TagToString is a utility function for pretty-printing handshake messages
// that converts a tag to a string. It will try to maintain the human friendly
// name if possible (i.e. kABCD -> "ABCD"), or will just treat it as a number
@@ -79,6 +82,9 @@ class NET_EXPORT_PRIVATE QuicUtils {
static QuicPriority LowestPriority();
static QuicPriority HighestPriority();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicUtils);
};
// Utility function that returns an IOVector object wrapped around |str|.
diff --git a/chromium/net/quic/quic_utils_chromium.h b/chromium/net/quic/quic_utils_chromium.h
new file mode 100644
index 00000000000..0229d1d0400
--- /dev/null
+++ b/chromium/net/quic/quic_utils_chromium.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Some helpers for quic that are for chromium codebase.
+
+#ifndef NET_QUIC_QUIC_UTILS_CHROMIUM_H_
+#define NET_QUIC_QUIC_UTILS_CHROMIUM_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace net {
+
+//
+// Find*()
+//
+
+// Returns a const reference to the value associated with the given key if it
+// exists. Crashes otherwise.
+//
+// This is intended as a replacement for operator[] as an rvalue (for reading)
+// when the key is guaranteed to exist.
+//
+// operator[] for lookup is discouraged for several reasons:
+// * It has a side-effect of inserting missing keys
+// * It is not thread-safe (even when it is not inserting, it can still
+// choose to resize the underlying storage)
+// * It invalidates iterators (when it chooses to resize)
+// * It default constructs a value object even if it doesn't need to
+//
+// This version assumes the key is printable, and includes it in the fatal log
+// message.
+template <class Collection>
+const typename Collection::value_type::second_type&
+FindOrDie(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ CHECK(it != collection.end()) << "Map key not found: " << key;
+ return it->second;
+}
+
+// Same as above, but returns a non-const reference.
+template <class Collection>
+typename Collection::value_type::second_type&
+FindOrDie(Collection& collection, // NOLINT
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::iterator it = collection.find(key);
+ CHECK(it != collection.end()) << "Map key not found: " << key;
+ return it->second;
+}
+
+// Returns a pointer to the const value associated with the given key if it
+// exists, or NULL otherwise.
+template <class Collection>
+const typename Collection::value_type::second_type*
+FindOrNull(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return &it->second;
+}
+
+// Same as above but returns a pointer to the non-const value.
+template <class Collection>
+typename Collection::value_type::second_type*
+FindOrNull(Collection& collection, // NOLINT
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return &it->second;
+}
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_UTILS_CHROMIUM_H_
diff --git a/chromium/net/quic/quic_utils_chromium_test.cc b/chromium/net/quic/quic_utils_chromium_test.cc
new file mode 100644
index 00000000000..247b8613c49
--- /dev/null
+++ b/chromium/net/quic/quic_utils_chromium_test.cc
@@ -0,0 +1,50 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_utils_chromium.h"
+
+#include <map>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::map;
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(QuicUtilsChromiumTest, FindOrNullTest) {
+ map<int, int> m;
+ m[0] = 2;
+
+ // Check FindOrNull
+ int* p1 = FindOrNull(m, 0);
+ CHECK_EQ(*p1, 2);
+ ++(*p1);
+ const map<int, int>& const_m = m;
+ const int* p2 = FindOrNull(const_m, 0);
+ CHECK_EQ(*p2, 3);
+ CHECK(FindOrNull(m, 1) == NULL);
+}
+
+TEST(QuicUtilsChromiumTest, FindOrDieTest) {
+ std::map<int, int> m;
+ m[10] = 15;
+ EXPECT_EQ(15, FindOrDie(m, 10));
+ // TODO(rtenneti): Use the latest DEATH macros after merging with latest rch's
+ // changes.
+ // ASSERT_DEATH(FindOrDie(m, 8), "Map key not found: 8");
+
+ // Make sure the non-const reference returning version works.
+ FindOrDie(m, 10) = 20;
+ EXPECT_EQ(20, FindOrDie(m, 10));
+
+ // Make sure we can lookup values in a const map.
+ const map<int, int>& const_m = m;
+ EXPECT_EQ(20, FindOrDie(const_m, 10));
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/quic_write_blocked_list.cc b/chromium/net/quic/quic_write_blocked_list.cc
new file mode 100644
index 00000000000..10f9c343bfa
--- /dev/null
+++ b/chromium/net/quic/quic_write_blocked_list.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_write_blocked_list.h"
+
+namespace net {
+
+const QuicPriority QuicWriteBlockedList::kHighestPriority =
+ static_cast<QuicPriority>(net::kHighestPriority);
+const QuicPriority QuicWriteBlockedList::kLowestPriority =
+ static_cast<QuicPriority>(net::kLowestPriority);
+
+QuicWriteBlockedList::QuicWriteBlockedList()
+ : crypto_stream_blocked_(false),
+ headers_stream_blocked_(false) {}
+
+QuicWriteBlockedList::~QuicWriteBlockedList() {}
+
+} // namespace net
diff --git a/chromium/net/quic/quic_write_blocked_list.h b/chromium/net/quic/quic_write_blocked_list.h
new file mode 100644
index 00000000000..6727fb4e826
--- /dev/null
+++ b/chromium/net/quic/quic_write_blocked_list.h
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#ifndef NET_QUIC_QUIC_WRITE_BLOCKED_LIST_H_
+#define NET_QUIC_QUIC_WRITE_BLOCKED_LIST_H_
+
+#include <set>
+
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+#include "net/spdy/write_blocked_list.h"
+
+namespace net {
+
+// Keeps tracks of the QUIC streams that have data to write, sorted by
+// priority. QUIC stream priority order is:
+// Crypto stream > Headers stream > Data streams by requested priority.
+class NET_EXPORT_PRIVATE QuicWriteBlockedList {
+ private:
+ typedef WriteBlockedList<QuicStreamId> QuicWriteBlockedListBase;
+
+ public:
+ static const QuicPriority kHighestPriority;
+ static const QuicPriority kLowestPriority;
+
+ QuicWriteBlockedList();
+ ~QuicWriteBlockedList();
+
+ bool HasWriteBlockedDataStreams() const {
+ return base_write_blocked_list_.HasWriteBlockedStreams();
+ }
+
+ bool HasWriteBlockedCryptoOrHeadersStream() const {
+ return crypto_stream_blocked_ || headers_stream_blocked_;
+ }
+
+ size_t NumBlockedStreams() const {
+ size_t num_blocked = base_write_blocked_list_.NumBlockedStreams();
+ if (crypto_stream_blocked_) {
+ ++num_blocked;
+ }
+ if (headers_stream_blocked_) {
+ ++num_blocked;
+ }
+
+ return num_blocked;
+ }
+
+ QuicStreamId PopFront() {
+ if (crypto_stream_blocked_) {
+ crypto_stream_blocked_ = false;
+ return kCryptoStreamId;
+ }
+
+ if (headers_stream_blocked_) {
+ headers_stream_blocked_ = false;
+ return kHeadersStreamId;
+ }
+
+ SpdyPriority priority =
+ base_write_blocked_list_.GetHighestPriorityWriteBlockedList();
+ QuicStreamId id = base_write_blocked_list_.PopFront(priority);
+ blocked_streams_.erase(id);
+ return id;
+ }
+
+ void PushBack(QuicStreamId stream_id, QuicPriority priority) {
+ if (stream_id == kCryptoStreamId) {
+ DCHECK_EQ(kHighestPriority, priority);
+ // TODO(avd) Add DCHECK(!crypto_stream_blocked_)
+ crypto_stream_blocked_ = true;
+ return;
+ }
+
+ if (stream_id == kHeadersStreamId) {
+ DCHECK_EQ(kHighestPriority, priority);
+ // TODO(avd) Add DCHECK(!headers_stream_blocked_);
+ headers_stream_blocked_ = true;
+ return;
+ }
+
+ if (blocked_streams_.find(stream_id) != blocked_streams_.end()) {
+ DVLOG(1) << "Stream " << stream_id << " already in write blocked list.";
+ return;
+ }
+
+ base_write_blocked_list_.PushBack(
+ stream_id, static_cast<SpdyPriority>(priority));
+ blocked_streams_.insert(stream_id);
+ return;
+ }
+
+ bool crypto_stream_blocked() const { return crypto_stream_blocked_; }
+ bool headers_stream_blocked() const { return headers_stream_blocked_; }
+
+ private:
+ QuicWriteBlockedListBase base_write_blocked_list_;
+ bool crypto_stream_blocked_;
+ bool headers_stream_blocked_;
+
+ // Keep track of write blocked streams in a set for faster membership checking
+ // than iterating over the base_write_blocked_list_. The contents of this set
+ // should mirror the contents of base_write_blocked_list_.
+ std::set<QuicStreamId> blocked_streams_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicWriteBlockedList);
+};
+
+} // namespace net
+
+
+#endif // NET_QUIC_QUIC_WRITE_BLOCKED_LIST_H_
diff --git a/chromium/net/quic/quic_write_blocked_list_test.cc b/chromium/net/quic/quic_write_blocked_list_test.cc
new file mode 100644
index 00000000000..0633f633636
--- /dev/null
+++ b/chromium/net/quic/quic_write_blocked_list_test.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#include "net/quic/quic_write_blocked_list.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(QuicWriteBlockedListTest, PriorityOrder) {
+ QuicWriteBlockedList write_blocked_list;
+
+ // Mark streams blocked in roughly reverse priority order, and
+ // verify that streams are sorted.
+ write_blocked_list.PushBack(40,
+ QuicWriteBlockedList::kLowestPriority);
+ write_blocked_list.PushBack(23,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(17,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kHeadersStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kCryptoStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(5u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+ // The Crypto stream is highest priority.
+ EXPECT_EQ(kCryptoStreamId, write_blocked_list.PopFront());
+ // Followed by the Headers stream.
+ EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront());
+ // Streams with same priority are popped in the order they were inserted.
+ EXPECT_EQ(23u, write_blocked_list.PopFront());
+ EXPECT_EQ(17u, write_blocked_list.PopFront());
+ // Low priority stream appears last.
+ EXPECT_EQ(40u, write_blocked_list.PopFront());
+
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+TEST(QuicWriteBlockedListTest, CryptoStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.PushBack(kCryptoStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_EQ(kCryptoStreamId, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+}
+
+TEST(QuicWriteBlockedListTest, HeadersStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.PushBack(kHeadersStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+}
+
+TEST(QuicWriteBlockedListTest, VerifyHeadersStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.PushBack(5,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kHeadersStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+ // In newer QUIC versions, there is a headers stream which is
+ // higher priority than data streams.
+ EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront());
+ EXPECT_EQ(5u, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+TEST(QuicWriteBlockedListTest, NoDuplicateEntries) {
+ // Test that QuicWriteBlockedList doesn't allow duplicate entries.
+ QuicWriteBlockedList write_blocked_list;
+
+ // Try to add a stream to the write blocked list multiple times at the same
+ // priority.
+ const QuicStreamId kBlockedId = kClientDataStreamId1;
+ write_blocked_list.PushBack(kBlockedId,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kBlockedId,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kBlockedId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ // This should only result in one blocked stream being added.
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+
+ // There should only be one stream to pop off the front.
+ EXPECT_EQ(kBlockedId, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/reliable_quic_stream.cc b/chromium/net/quic/reliable_quic_stream.cc
index 5d9c3cce49c..44d3a036fdd 100644
--- a/chromium/net/quic/reliable_quic_stream.cc
+++ b/chromium/net/quic/reliable_quic_stream.cc
@@ -4,9 +4,11 @@
#include "net/quic/reliable_quic_stream.h"
+#include "base/logging.h"
+#include "net/quic/iovector.h"
+#include "net/quic/quic_flow_controller.h"
#include "net/quic/quic_session.h"
-#include "net/quic/quic_spdy_decompressor.h"
-#include "net/spdy/write_blocked_list.h"
+#include "net/quic/quic_write_blocked_list.h"
using base::StringPiece;
using std::min;
@@ -23,10 +25,119 @@ struct iovec MakeIovec(StringPiece data) {
return iov;
}
+size_t GetInitialStreamFlowControlWindowToSend(QuicSession* session) {
+ QuicVersion version = session->connection()->version();
+ if (version <= QUIC_VERSION_19) {
+ return session->config()->GetInitialFlowControlWindowToSend();
+ }
+
+ return session->config()->GetInitialStreamFlowControlWindowToSend();
+}
+
+size_t GetReceivedFlowControlWindow(QuicSession* session) {
+ QuicVersion version = session->connection()->version();
+ if (version <= QUIC_VERSION_19) {
+ if (session->config()->HasReceivedInitialFlowControlWindowBytes()) {
+ return session->config()->ReceivedInitialFlowControlWindowBytes();
+ }
+
+ return kDefaultFlowControlSendWindow;
+ }
+
+ // Version must be >= QUIC_VERSION_20, so we check for stream specific flow
+ // control window.
+ if (session->config()->HasReceivedInitialStreamFlowControlWindowBytes()) {
+ return session->config()->ReceivedInitialStreamFlowControlWindowBytes();
+ }
+
+ return kDefaultFlowControlSendWindow;
+}
+
} // namespace
-ReliableQuicStream::ReliableQuicStream(QuicStreamId id,
- QuicSession* session)
+// Wrapper that aggregates OnAckNotifications for packets sent using
+// WriteOrBufferData and delivers them to the original
+// QuicAckNotifier::DelegateInterface after all bytes written using
+// WriteOrBufferData are acked. This level of indirection is
+// necessary because the delegate interface provides no mechanism that
+// WriteOrBufferData can use to inform it that the write required
+// multiple WritevData calls or that only part of the data has been
+// sent out by the time ACKs start arriving.
+class ReliableQuicStream::ProxyAckNotifierDelegate
+ : public QuicAckNotifier::DelegateInterface {
+ public:
+ explicit ProxyAckNotifierDelegate(DelegateInterface* delegate)
+ : delegate_(delegate),
+ pending_acks_(0),
+ wrote_last_data_(false),
+ num_original_packets_(0),
+ num_original_bytes_(0),
+ num_retransmitted_packets_(0),
+ num_retransmitted_bytes_(0) {
+ }
+
+ virtual void OnAckNotification(int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed)
+ OVERRIDE {
+ DCHECK_LT(0, pending_acks_);
+ --pending_acks_;
+ num_original_packets_ += num_original_packets;
+ num_original_bytes_ += num_original_bytes;
+ num_retransmitted_packets_ += num_retransmitted_packets;
+ num_retransmitted_bytes_ += num_retransmitted_bytes;
+
+ if (wrote_last_data_ && pending_acks_ == 0) {
+ delegate_->OnAckNotification(num_original_packets_,
+ num_original_bytes_,
+ num_retransmitted_packets_,
+ num_retransmitted_bytes_,
+ delta_largest_observed);
+ }
+ }
+
+ void WroteData(bool last_data) {
+ DCHECK(!wrote_last_data_);
+ ++pending_acks_;
+ wrote_last_data_ = last_data;
+ }
+
+ protected:
+ // Delegates are ref counted.
+ virtual ~ProxyAckNotifierDelegate() OVERRIDE {
+ }
+
+ private:
+ // Original delegate. delegate_->OnAckNotification will be called when:
+ // wrote_last_data_ == true and pending_acks_ == 0
+ scoped_refptr<DelegateInterface> delegate_;
+
+ // Number of outstanding acks.
+ int pending_acks_;
+
+ // True if no pending writes remain.
+ bool wrote_last_data_;
+
+ // Accumulators.
+ int num_original_packets_;
+ int num_original_bytes_;
+ int num_retransmitted_packets_;
+ int num_retransmitted_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyAckNotifierDelegate);
+};
+
+ReliableQuicStream::PendingData::PendingData(
+ string data_in, scoped_refptr<ProxyAckNotifierDelegate> delegate_in)
+ : data(data_in), delegate(delegate_in) {
+}
+
+ReliableQuicStream::PendingData::~PendingData() {
+}
+
+ReliableQuicStream::ReliableQuicStream(QuicStreamId id, QuicSession* session)
: sequencer_(this),
id_(id),
session_(session),
@@ -38,41 +149,70 @@ ReliableQuicStream::ReliableQuicStream(QuicStreamId id,
write_side_closed_(false),
fin_buffered_(false),
fin_sent_(false),
- is_server_(session_->is_server()) {
+ fin_received_(false),
+ rst_sent_(false),
+ rst_received_(false),
+ fec_policy_(FEC_PROTECT_OPTIONAL),
+ is_server_(session_->is_server()),
+ flow_controller_(
+ session_->connection(), id_, is_server_,
+ GetReceivedFlowControlWindow(session),
+ GetInitialStreamFlowControlWindowToSend(session),
+ GetInitialStreamFlowControlWindowToSend(session)),
+ connection_flow_controller_(session_->flow_controller()) {
}
ReliableQuicStream::~ReliableQuicStream() {
}
-bool ReliableQuicStream::WillAcceptStreamFrame(
- const QuicStreamFrame& frame) const {
+bool ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
if (read_side_closed_) {
+ DVLOG(1) << ENDPOINT << "Ignoring frame " << frame.stream_id;
+ // We don't want to be reading: blackhole the data.
return true;
}
+
if (frame.stream_id != id_) {
LOG(ERROR) << "Error!";
return false;
}
- return sequencer_.WillAcceptStreamFrame(frame);
-}
-bool ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
- DCHECK_EQ(frame.stream_id, id_);
- if (read_side_closed_) {
- DVLOG(1) << ENDPOINT << "Ignoring frame " << frame.stream_id;
- // We don't want to be reading: blackhole the data.
- return true;
+ if (frame.fin) {
+ fin_received_ = true;
}
- // Note: This count include duplicate data received.
- stream_bytes_read_ += frame.data.TotalBufferSize();
- bool accepted = sequencer_.OnStreamFrame(frame);
+ // This count include duplicate data received.
+ size_t frame_payload_size = frame.data.TotalBufferSize();
+ stream_bytes_read_ += frame_payload_size;
+
+ // Flow control is interested in tracking highest received offset.
+ if (MaybeIncreaseHighestReceivedOffset(frame.offset + frame_payload_size)) {
+ // As the highest received offset has changed, we should check to see if
+ // this is a violation of flow control.
+ if (flow_controller_.FlowControlViolation() ||
+ connection_flow_controller_->FlowControlViolation()) {
+ session_->connection()->SendConnectionClose(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA);
+ return false;
+ }
+ }
- return accepted;
+ return sequencer_.OnStreamFrame(frame);
}
-void ReliableQuicStream::OnStreamReset(QuicRstStreamErrorCode error) {
- stream_error_ = error;
+int ReliableQuicStream::num_frames_received() const {
+ return sequencer_.num_frames_received();
+}
+
+int ReliableQuicStream::num_duplicate_frames_received() const {
+ return sequencer_.num_duplicate_frames_received();
+}
+
+void ReliableQuicStream::OnStreamReset(const QuicRstStreamFrame& frame) {
+ rst_received_ = true;
+ MaybeIncreaseHighestReceivedOffset(frame.byte_offset);
+
+ stream_error_ = frame.error_code;
CloseWriteSide();
CloseReadSide();
}
@@ -100,7 +240,8 @@ void ReliableQuicStream::Reset(QuicRstStreamErrorCode error) {
DCHECK_NE(QUIC_STREAM_NO_ERROR, error);
stream_error_ = error;
// Sending a RstStream results in calling CloseStream.
- session()->SendRstStream(id(), error);
+ session()->SendRstStream(id(), error, stream_bytes_written_);
+ rst_sent_ = true;
}
void ReliableQuicStream::CloseConnection(QuicErrorCode error) {
@@ -112,51 +253,95 @@ void ReliableQuicStream::CloseConnectionWithDetails(QuicErrorCode error,
session()->connection()->SendConnectionCloseWithDetails(error, details);
}
-QuicVersion ReliableQuicStream::version() {
+QuicVersion ReliableQuicStream::version() const {
return session()->connection()->version();
}
-void ReliableQuicStream::WriteOrBufferData(StringPiece data, bool fin) {
- DCHECK(data.size() > 0 || fin);
- DCHECK(!fin_buffered_);
+void ReliableQuicStream::WriteOrBufferData(
+ StringPiece data,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ if (data.empty() && !fin) {
+ LOG(DFATAL) << "data.empty() && !fin";
+ return;
+ }
+
+ if (fin_buffered_) {
+ LOG(DFATAL) << "Fin already buffered";
+ return;
+ }
+
+ scoped_refptr<ProxyAckNotifierDelegate> proxy_delegate;
+ if (ack_notifier_delegate != NULL) {
+ proxy_delegate = new ProxyAckNotifierDelegate(ack_notifier_delegate);
+ }
QuicConsumedData consumed_data(0, false);
fin_buffered_ = fin;
if (queued_data_.empty()) {
struct iovec iov(MakeIovec(data));
- consumed_data = WritevData(&iov, 1, fin, NULL);
+ consumed_data = WritevData(&iov, 1, fin, proxy_delegate.get());
DCHECK_LE(consumed_data.bytes_consumed, data.length());
}
+ bool write_completed;
// If there's unconsumed data or an unconsumed fin, queue it.
if (consumed_data.bytes_consumed < data.length() ||
(fin && !consumed_data.fin_consumed)) {
- queued_data_.push_back(
- string(data.data() + consumed_data.bytes_consumed,
- data.length() - consumed_data.bytes_consumed));
+ StringPiece remainder(data.substr(consumed_data.bytes_consumed));
+ queued_data_.push_back(PendingData(remainder.as_string(), proxy_delegate));
+ write_completed = false;
+ } else {
+ write_completed = true;
+ }
+
+ if ((proxy_delegate.get() != NULL) &&
+ (consumed_data.bytes_consumed > 0 || consumed_data.fin_consumed)) {
+ proxy_delegate->WroteData(write_completed);
}
}
void ReliableQuicStream::OnCanWrite() {
bool fin = false;
while (!queued_data_.empty()) {
- const string& data = queued_data_.front();
+ PendingData* pending_data = &queued_data_.front();
+ ProxyAckNotifierDelegate* delegate = pending_data->delegate.get();
if (queued_data_.size() == 1 && fin_buffered_) {
fin = true;
}
- struct iovec iov(MakeIovec(data));
- QuicConsumedData consumed_data = WritevData(&iov, 1, fin, NULL);
- if (consumed_data.bytes_consumed == data.size() &&
+ struct iovec iov(MakeIovec(pending_data->data));
+ QuicConsumedData consumed_data = WritevData(&iov, 1, fin, delegate);
+ if (consumed_data.bytes_consumed == pending_data->data.size() &&
fin == consumed_data.fin_consumed) {
queued_data_.pop_front();
+ if (delegate != NULL) {
+ delegate->WroteData(true);
+ }
} else {
- queued_data_.front().erase(0, consumed_data.bytes_consumed);
+ if (consumed_data.bytes_consumed > 0) {
+ pending_data->data.erase(0, consumed_data.bytes_consumed);
+ if (delegate != NULL) {
+ delegate->WroteData(false);
+ }
+ }
break;
}
}
}
+void ReliableQuicStream::MaybeSendBlocked() {
+ flow_controller_.MaybeSendBlocked();
+ connection_flow_controller_->MaybeSendBlocked();
+ // If we are connection level flow control blocked, then add the stream
+ // to the write blocked list. It will be given a chance to write when a
+ // connection level WINDOW_UPDATE arrives.
+ if (connection_flow_controller_->IsBlocked() &&
+ !flow_controller_.IsBlocked()) {
+ session_->MarkWriteBlocked(id(), EffectivePriority());
+ }
+}
+
QuicConsumedData ReliableQuicStream::WritevData(
const struct iovec* iov,
int iov_count,
@@ -167,14 +352,50 @@ QuicConsumedData ReliableQuicStream::WritevData(
return QuicConsumedData(0, false);
}
- size_t write_length = 0u;
- for (int i = 0; i < iov_count; ++i) {
- write_length += iov[i].iov_len;
+ // How much data we want to write.
+ size_t write_length = TotalIovecLength(iov, iov_count);
+
+ // A FIN with zero data payload should not be flow control blocked.
+ bool fin_with_zero_data = (fin && write_length == 0);
+
+ if (flow_controller_.IsEnabled()) {
+ // How much data we are allowed to write from flow control.
+ uint64 send_window = flow_controller_.SendWindowSize();
+ if (connection_flow_controller_->IsEnabled()) {
+ send_window =
+ min(send_window, connection_flow_controller_->SendWindowSize());
+ }
+
+ if (send_window == 0 && !fin_with_zero_data) {
+ // Quick return if we can't send anything.
+ MaybeSendBlocked();
+ return QuicConsumedData(0, false);
+ }
+
+ if (write_length > send_window) {
+ // Don't send the FIN if we aren't going to send all the data.
+ fin = false;
+
+ // Writing more data would be a violation of flow control.
+ write_length = send_window;
+ }
}
+
+ // Fill an IOVector with bytes from the iovec.
+ IOVector data;
+ data.AppendIovecAtMostBytes(iov, iov_count, write_length);
+
QuicConsumedData consumed_data = session()->WritevData(
- id(), iov, iov_count, stream_bytes_written_, fin, ack_notifier_delegate);
+ id(), data, stream_bytes_written_, fin, GetFecProtection(),
+ ack_notifier_delegate);
stream_bytes_written_ += consumed_data.bytes_consumed;
+
+ AddBytesSent(consumed_data.bytes_consumed);
+
if (consumed_data.bytes_consumed == write_length) {
+ if (!fin_with_zero_data) {
+ MaybeSendBlocked();
+ }
if (fin && consumed_data.fin_consumed) {
fin_sent_ = true;
CloseWriteSide();
@@ -187,6 +408,10 @@ QuicConsumedData ReliableQuicStream::WritevData(
return consumed_data;
}
+FecProtection ReliableQuicStream::GetFecProtection() {
+ return fec_policy_ == FEC_PROTECT_ALWAYS ? MUST_FEC_PROTECT : MAY_FEC_PROTECT;
+}
+
void ReliableQuicStream::CloseReadSide() {
if (read_side_closed_) {
return;
@@ -213,13 +438,88 @@ void ReliableQuicStream::CloseWriteSide() {
}
}
-bool ReliableQuicStream::HasBufferedData() {
+bool ReliableQuicStream::HasBufferedData() const {
return !queued_data_.empty();
}
void ReliableQuicStream::OnClose() {
CloseReadSide();
CloseWriteSide();
+
+ if (!fin_sent_ && !rst_sent_) {
+ // For flow control accounting, we must tell the peer how many bytes we have
+ // written on this stream before termination. Done here if needed, using a
+ // RST frame.
+ DVLOG(1) << ENDPOINT << "Sending RST in OnClose: " << id();
+ session_->SendRstStream(id(), QUIC_RST_FLOW_CONTROL_ACCOUNTING,
+ stream_bytes_written_);
+ rst_sent_ = true;
+ }
+
+ // We are closing the stream and will not process any further incoming bytes.
+ // As there may be more bytes in flight and we need to ensure that both
+ // endpoints have the same connection level flow control state, mark all
+ // unreceived or buffered bytes as consumed.
+ uint64 bytes_to_consume = flow_controller_.highest_received_byte_offset() -
+ flow_controller_.bytes_consumed();
+ AddBytesConsumed(bytes_to_consume);
+}
+
+void ReliableQuicStream::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ if (!flow_controller_.IsEnabled()) {
+ DLOG(DFATAL) << "Flow control not enabled! " << version();
+ return;
+ }
+
+ if (flow_controller_.UpdateSendWindowOffset(frame.byte_offset)) {
+ // We can write again!
+ // TODO(rjshade): This does not respect priorities (e.g. multiple
+ // outstanding POSTs are unblocked on arrival of
+ // SHLO with initial window).
+ // As long as the connection is not flow control blocked, we can write!
+ OnCanWrite();
+ }
+}
+
+bool ReliableQuicStream::MaybeIncreaseHighestReceivedOffset(uint64 new_offset) {
+ if (flow_controller_.IsEnabled()) {
+ uint64 increment =
+ new_offset - flow_controller_.highest_received_byte_offset();
+ if (flow_controller_.UpdateHighestReceivedOffset(new_offset)) {
+ // If |new_offset| increased the stream flow controller's highest received
+ // offset, then we need to increase the connection flow controller's value
+ // by the incremental difference.
+ connection_flow_controller_->UpdateHighestReceivedOffset(
+ connection_flow_controller_->highest_received_byte_offset() +
+ increment);
+ return true;
+ }
+ }
+ return false;
+}
+
+void ReliableQuicStream::AddBytesSent(uint64 bytes) {
+ if (flow_controller_.IsEnabled()) {
+ flow_controller_.AddBytesSent(bytes);
+ connection_flow_controller_->AddBytesSent(bytes);
+ }
+}
+
+void ReliableQuicStream::AddBytesConsumed(uint64 bytes) {
+ if (flow_controller_.IsEnabled()) {
+ // Only adjust stream level flow controller if we are still reading.
+ if (!read_side_closed_) {
+ flow_controller_.AddBytesConsumed(bytes);
+ }
+
+ connection_flow_controller_->AddBytesConsumed(bytes);
+ }
+}
+
+bool ReliableQuicStream::IsFlowControlBlocked() {
+ return flow_controller_.IsBlocked() ||
+ connection_flow_controller_->IsBlocked();
}
} // namespace net
diff --git a/chromium/net/quic/reliable_quic_stream.h b/chromium/net/quic/reliable_quic_stream.h
index 7b0a79068d5..be89a284e4a 100644
--- a/chromium/net/quic/reliable_quic_stream.h
+++ b/chromium/net/quic/reliable_quic_stream.h
@@ -11,13 +11,16 @@
#include <list>
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
#include "base/strings/string_piece.h"
#include "net/base/iovec.h"
#include "net/base/net_export.h"
#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_flow_controller.h"
#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_spdy_compressor.h"
#include "net/quic/quic_stream_sequencer.h"
+#include "net/quic/quic_types.h"
namespace net {
@@ -25,9 +28,7 @@ namespace test {
class ReliableQuicStreamPeer;
} // namespace test
-class IPEndPoint;
class QuicSession;
-class SSLInfo;
class NET_EXPORT_PRIVATE ReliableQuicStream {
public:
@@ -36,8 +37,6 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
virtual ~ReliableQuicStream();
- bool WillAcceptStreamFrame(const QuicStreamFrame& frame) const;
-
// Called when a (potentially duplicate) stream frame has been received
// for this stream. Returns false if this frame can not be accepted
// because there is too much data already buffered.
@@ -51,7 +50,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
virtual void OnClose();
// Called when we get a stream reset from the peer.
- virtual void OnStreamReset(QuicRstStreamErrorCode error);
+ virtual void OnStreamReset(const QuicRstStreamFrame& frame);
// Called when we get or send a connection close, and should immediately
// close the stream. This is not passed through the sequencer,
@@ -83,15 +82,52 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
bool read_side_closed() const { return read_side_closed_; }
bool write_side_closed() const { return write_side_closed_; }
- uint64 stream_bytes_read() { return stream_bytes_read_; }
- uint64 stream_bytes_written() { return stream_bytes_written_; }
+ uint64 stream_bytes_read() const { return stream_bytes_read_; }
+ uint64 stream_bytes_written() const { return stream_bytes_written_; }
+
+ QuicVersion version() const;
+
+ void set_fin_sent(bool fin_sent) { fin_sent_ = fin_sent; }
+ void set_rst_sent(bool rst_sent) { rst_sent_ = rst_sent; }
+
+ // Adjust our flow control windows according to new offset in |frame|.
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame);
+
+ int num_frames_received() const;
+
+ int num_duplicate_frames_received() const;
- QuicVersion version();
+ QuicFlowController* flow_controller() { return &flow_controller_; }
+
+ // Called when we see a frame which could increase the highest offset.
+ // Returns true if the highest offset did increase.
+ bool MaybeIncreaseHighestReceivedOffset(uint64 new_offset);
+ // Called when bytese are sent to the peer.
+ void AddBytesSent(uint64 bytes);
+ // Called by the stream sequencer as bytes are consumed from the buffer.
+ // If our receive window has dropped below the threshold, then send a
+ // WINDOW_UPDATE frame.
+ void AddBytesConsumed(uint64 bytes);
+
+ // Returns true if the stream is flow control blocked, by the stream flow
+ // control window or the connection flow control window.
+ bool IsFlowControlBlocked();
+
+ // Returns true if we have received either a RST or a FIN - either of which
+ // gives a definitive number of bytes which the peer has sent. If this is not
+ // true on stream termination the session must keep track of the stream's byte
+ // offset until a definitive final value arrives.
+ bool HasFinalReceivedByteOffset() const {
+ return fin_received_ || rst_received_;
+ }
protected:
// Sends as much of 'data' to the connection as the connection will consume,
// and then buffers any remaining data in queued_data_.
- void WriteOrBufferData(base::StringPiece data, bool fin);
+ void WriteOrBufferData(
+ base::StringPiece data,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
// Sends as many bytes in the first |count| buffers of |iov| to the connection
// as the connection will consume.
@@ -104,26 +140,53 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
bool fin,
QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+ // Helper method that returns FecProtection to use for writes to the session.
+ FecProtection GetFecProtection();
+
// Close the read side of the socket. Further frames will not be accepted.
virtual void CloseReadSide();
// Close the write side of the socket. Further writes will fail.
void CloseWriteSide();
- bool HasBufferedData();
+ bool HasBufferedData() const;
- bool fin_buffered() { return fin_buffered_; }
+ bool fin_buffered() const { return fin_buffered_; }
+ void set_fec_policy(FecPolicy fec_policy) { fec_policy_ = fec_policy; }
+
+ const QuicSession* session() const { return session_; }
QuicSession* session() { return session_; }
const QuicStreamSequencer* sequencer() const { return &sequencer_; }
QuicStreamSequencer* sequencer() { return &sequencer_; }
+ void DisableFlowControl() {
+ flow_controller_.Disable();
+ }
+
private:
friend class test::ReliableQuicStreamPeer;
friend class QuicStreamUtils;
+ class ProxyAckNotifierDelegate;
+
+ struct PendingData {
+ PendingData(string data_in,
+ scoped_refptr<ProxyAckNotifierDelegate> delegate_in);
+ ~PendingData();
- std::list<string> queued_data_;
+ string data;
+ // Delegate that should be notified when the pending data is acked.
+ // Can be nullptr.
+ scoped_refptr<ProxyAckNotifierDelegate> delegate;
+ };
+
+ // Calls MaybeSendBlocked on our flow controller, and connection level flow
+ // controller. If we are flow control blocked, marks this stream as write
+ // blocked.
+ void MaybeSendBlocked();
+
+ std::list<PendingData> queued_data_;
QuicStreamSequencer sequencer_;
QuicStreamId id_;
@@ -149,9 +212,28 @@ class NET_EXPORT_PRIVATE ReliableQuicStream {
bool fin_buffered_;
bool fin_sent_;
+ // True if this stream has received (and the sequencer has accepted) a
+ // StreamFrame with the FIN set.
+ bool fin_received_;
+
+ // In combination with fin_sent_, used to ensure that a FIN and/or a RST is
+ // always sent before stream termination.
+ bool rst_sent_;
+
+ // True if this stream has received a RST stream frame.
+ bool rst_received_;
+
+ // FEC policy to be used for this stream.
+ FecPolicy fec_policy_;
+
// True if the session this stream is running under is a server session.
bool is_server_;
+ QuicFlowController flow_controller_;
+
+ // The connection level flow controller. Not owned.
+ QuicFlowController* connection_flow_controller_;
+
DISALLOW_COPY_AND_ASSIGN(ReliableQuicStream);
};
diff --git a/chromium/net/quic/reliable_quic_stream_test.cc b/chromium/net/quic/reliable_quic_stream_test.cc
index 7e82f533142..b673a4eef5d 100644
--- a/chromium/net/quic/reliable_quic_stream_test.cc
+++ b/chromium/net/quic/reliable_quic_stream_test.cc
@@ -6,22 +6,30 @@
#include "net/quic/quic_ack_notifier.h"
#include "net/quic/quic_connection.h"
-#include "net/quic/quic_spdy_compressor.h"
-#include "net/quic/quic_spdy_decompressor.h"
+#include "net/quic/quic_flags.h"
#include "net/quic/quic_utils.h"
+#include "net/quic/quic_write_blocked_list.h"
#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/quic_config_peer.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
#include "net/quic/test_tools/quic_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock_mutant.h"
using base::StringPiece;
using std::min;
-using testing::_;
+using testing::CreateFunctor;
using testing::InSequence;
+using testing::Invoke;
using testing::Return;
using testing::SaveArg;
-using testing::StrEq;
using testing::StrictMock;
+using testing::WithArgs;
+using testing::_;
namespace net {
namespace test {
@@ -30,7 +38,6 @@ namespace {
const char kData1[] = "FooAndBar";
const char kData2[] = "EepAndBaz";
const size_t kDataLen = 9;
-const QuicGuid kStreamId = 3;
const bool kIsServer = true;
const bool kShouldProcessData = true;
@@ -56,8 +63,7 @@ class TestStream : public ReliableQuicStream {
using ReliableQuicStream::WriteOrBufferData;
using ReliableQuicStream::CloseReadSide;
using ReliableQuicStream::CloseWriteSide;
-
- const string& data() const { return data_; }
+ using ReliableQuicStream::OnClose;
private:
bool should_process_data_;
@@ -66,7 +72,10 @@ class TestStream : public ReliableQuicStream {
class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> {
public:
- ReliableQuicStreamTest() {
+ ReliableQuicStreamTest()
+ : initial_flow_control_window_bytes_(kMaxPacketSize),
+ zero_(QuicTime::Delta::Zero()),
+ supported_versions_(QuicSupportedVersions()) {
headers_[":host"] = "www.google.com";
headers_[":path"] = "/index.hml";
headers_[":scheme"] = "https";
@@ -96,71 +105,86 @@ class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> {
"JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
}
+ void set_supported_versions(const QuicVersionVector& versions) {
+ supported_versions_ = versions;
+ }
+
void Initialize(bool stream_should_process_data) {
- connection_ = new StrictMock<MockConnection>(kIsServer);
+ connection_ =
+ new StrictMock<MockConnection>(kIsServer, supported_versions_);
session_.reset(new StrictMock<MockSession>(connection_));
- stream_.reset(new TestStream(kStreamId, session_.get(),
- stream_should_process_data));
- stream2_.reset(new TestStream(kStreamId + 2, session_.get(),
+
+ // New streams rely on having the peer's flow control receive window
+ // negotiated in the config.
+ QuicConfigPeer::SetReceivedInitialFlowControlWindow(
+ session_->config(), initial_flow_control_window_bytes_);
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ session_->config(), initial_flow_control_window_bytes_);
+
+ stream_.reset(new TestStream(kHeadersStreamId, session_.get(),
stream_should_process_data));
- compressor_.reset(new QuicSpdyCompressor());
- decompressor_.reset(new QuicSpdyDecompressor);
write_blocked_list_ =
- QuicSessionPeer::GetWriteblockedStreams(session_.get());
+ QuicSessionPeer::GetWriteBlockedStreams(session_.get());
+ }
+
+ bool fin_sent() { return ReliableQuicStreamPeer::FinSent(stream_.get()); }
+ bool rst_sent() { return ReliableQuicStreamPeer::RstSent(stream_.get()); }
+
+ void set_initial_flow_control_window_bytes(uint32 val) {
+ initial_flow_control_window_bytes_ = val;
+ }
+
+ bool HasWriteBlockedStreams() {
+ return write_blocked_list_->HasWriteBlockedCryptoOrHeadersStream() ||
+ write_blocked_list_->HasWriteBlockedDataStreams();
}
protected:
MockConnection* connection_;
scoped_ptr<MockSession> session_;
scoped_ptr<TestStream> stream_;
- scoped_ptr<TestStream> stream2_;
- scoped_ptr<QuicSpdyCompressor> compressor_;
- scoped_ptr<QuicSpdyDecompressor> decompressor_;
SpdyHeaderBlock headers_;
- WriteBlockedList<QuicStreamId>* write_blocked_list_;
+ QuicWriteBlockedList* write_blocked_list_;
+ uint32 initial_flow_control_window_bytes_;
+ QuicTime::Delta zero_;
+ QuicVersionVector supported_versions_;
};
TEST_F(ReliableQuicStreamTest, WriteAllData) {
Initialize(kShouldProcessData);
- connection_->options()->max_packet_length =
- 1 + QuicPacketCreator::StreamFramePacketOverhead(
- connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion,
- PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
- EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce(
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen, true)));
- stream_->WriteOrBufferData(kData1, false);
- EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams());
+ stream_->WriteOrBufferData(kData1, false, NULL);
+ EXPECT_FALSE(HasWriteBlockedStreams());
}
-// TODO(rtenneti): Death tests crash on OS_ANDROID.
-#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) && !defined(OS_ANDROID)
TEST_F(ReliableQuicStreamTest, NoBlockingIfNoDataOrFin) {
Initialize(kShouldProcessData);
// Write no data and no fin. If we consume nothing we should not be write
// blocked.
- EXPECT_DEBUG_DEATH({
- EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce(
- Return(QuicConsumedData(0, false)));
- stream_->WriteOrBufferData(StringPiece(), false);
- EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams());
- }, "");
+ EXPECT_DFATAL(stream_->WriteOrBufferData(StringPiece(), false, NULL), "");
+ EXPECT_FALSE(HasWriteBlockedStreams());
}
-#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) && !defined(OS_ANDROID)
TEST_F(ReliableQuicStreamTest, BlockIfOnlySomeDataConsumed) {
Initialize(kShouldProcessData);
// Write some data and no fin. If we consume some but not all of the data,
// we should be write blocked a not all the data was consumed.
- EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce(
- Return(QuicConsumedData(1, false)));
- stream_->WriteOrBufferData(StringPiece(kData1, 2), false);
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(1, false)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 2), false, NULL);
ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
}
-
TEST_F(ReliableQuicStreamTest, BlockIfFinNotConsumedWithData) {
Initialize(kShouldProcessData);
@@ -168,9 +192,9 @@ TEST_F(ReliableQuicStreamTest, BlockIfFinNotConsumedWithData) {
// we should be write blocked because the fin was not consumed.
// (This should never actually happen as the fin should be sent out with the
// last data)
- EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce(
- Return(QuicConsumedData(2, false)));
- stream_->WriteOrBufferData(StringPiece(kData1, 2), true);
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(2, false)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 2), true, NULL);
ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
}
@@ -179,38 +203,112 @@ TEST_F(ReliableQuicStreamTest, BlockIfSoloFinNotConsumed) {
// Write no data and a fin. If we consume nothing we should be write blocked,
// as the fin was not consumed.
- EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce(
- Return(QuicConsumedData(0, false)));
- stream_->WriteOrBufferData(StringPiece(), true);
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->WriteOrBufferData(StringPiece(), true, NULL);
ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
}
TEST_F(ReliableQuicStreamTest, WriteOrBufferData) {
Initialize(kShouldProcessData);
- EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams());
- connection_->options()->max_packet_length =
- 1 + QuicPacketCreator::StreamFramePacketOverhead(
- connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion,
- PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
- EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)).WillOnce(
+ EXPECT_FALSE(HasWriteBlockedStreams());
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen - 1, false)));
- stream_->WriteOrBufferData(kData1, false);
- EXPECT_TRUE(write_blocked_list_->HasWriteBlockedStreams());
+ stream_->WriteOrBufferData(kData1, false, NULL);
+ EXPECT_TRUE(HasWriteBlockedStreams());
// Queue a bytes_consumed write.
- stream_->WriteOrBufferData(kData2, false);
+ stream_->WriteOrBufferData(kData2, false, NULL);
// Make sure we get the tail of the first write followed by the bytes_consumed
InSequence s;
- EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)).
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).
WillOnce(Return(QuicConsumedData(1, false)));
- EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)).
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).
WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
stream_->OnCanWrite();
// And finally the end of the bytes_consumed.
- EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)).
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).
+ WillOnce(Return(QuicConsumedData(2, true)));
+ stream_->OnCanWrite();
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectAlways) {
+ Initialize(kShouldProcessData);
+
+ // Set FEC policy on stream.
+ ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_ALWAYS);
+
+ EXPECT_FALSE(HasWriteBlockedStreams());
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ // Write first data onto stream, which will cause one session write.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).WillOnce(
+ Return(QuicConsumedData(kDataLen - 1, false)));
+ stream_->WriteOrBufferData(kData1, false, NULL);
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Queue a bytes_consumed write.
+ stream_->WriteOrBufferData(kData2, false, NULL);
+
+ // Make sure we get the tail of the first write followed by the bytes_consumed
+ InSequence s;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(1, false)));
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
+ stream_->OnCanWrite();
+
+ // And finally the end of the bytes_consumed.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(2, true)));
+ stream_->OnCanWrite();
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectOptional) {
+ Initialize(kShouldProcessData);
+
+ // Set FEC policy on stream.
+ ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_OPTIONAL);
+
+ EXPECT_FALSE(HasWriteBlockedStreams());
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ // Write first data onto stream, which will cause one session write.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).WillOnce(
+ Return(QuicConsumedData(kDataLen - 1, false)));
+ stream_->WriteOrBufferData(kData1, false, NULL);
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Queue a bytes_consumed write.
+ stream_->WriteOrBufferData(kData2, false, NULL);
+
+ // Make sure we get the tail of the first write followed by the bytes_consumed
+ InSequence s;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(1, false)));
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
+ stream_->OnCanWrite();
+
+ // And finally the end of the bytes_consumed.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
WillOnce(Return(QuicConsumedData(2, true)));
stream_->OnCanWrite();
}
@@ -227,6 +325,363 @@ TEST_F(ReliableQuicStreamTest, ConnectionCloseAfterStreamClose) {
EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error());
}
+TEST_F(ReliableQuicStreamTest, RstAlwaysSentIfNoFinSent) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if no FIN has been sent, we send a RST.
+
+ Initialize(kShouldProcessData);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Write some data, with no FIN.
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(1, false)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 1), false, NULL);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Now close the stream, and expect that we send a RST.
+ EXPECT_CALL(*session_, SendRstStream(_, _, _));
+ stream_->OnClose();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+}
+
+TEST_F(ReliableQuicStreamTest, RstNotSentIfFinSent) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if a FIN has been sent, we don't also send a RST.
+
+ Initialize(kShouldProcessData);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Write some data, with FIN.
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(1, true)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 1), true, NULL);
+ EXPECT_TRUE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Now close the stream, and expect that we do not send a RST.
+ stream_->OnClose();
+ EXPECT_TRUE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+}
+
+TEST_F(ReliableQuicStreamTest, OnlySendOneRst) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if a stream sends a RST, it doesn't send an additional RST during
+ // OnClose() (this shouldn't be harmful, but we shouldn't do it anyway...)
+
+ Initialize(kShouldProcessData);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Reset the stream.
+ const int expected_resets = 1;
+ EXPECT_CALL(*session_, SendRstStream(_, _, _)).Times(expected_resets);
+ stream_->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+
+ // Now close the stream (any further resets being sent would break the
+ // expectation above).
+ stream_->OnClose();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+}
+
+TEST_F(ReliableQuicStreamTest, StreamFlowControlMultipleWindowUpdates) {
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+ set_initial_flow_control_window_bytes(1000);
+
+ Initialize(kShouldProcessData);
+
+ // If we receive multiple WINDOW_UPDATES (potentially out of order), then we
+ // want to make sure we latch the largest offset we see.
+
+ // Initially should be default.
+ EXPECT_EQ(
+ initial_flow_control_window_bytes_,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+
+ // Check a single WINDOW_UPDATE results in correct offset.
+ QuicWindowUpdateFrame window_update_1(stream_->id(), 1234);
+ stream_->OnWindowUpdateFrame(window_update_1);
+ EXPECT_EQ(
+ window_update_1.byte_offset,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+
+ // Now send a few more WINDOW_UPDATES and make sure that only the largest is
+ // remembered.
+ QuicWindowUpdateFrame window_update_2(stream_->id(), 1);
+ QuicWindowUpdateFrame window_update_3(stream_->id(), 9999);
+ QuicWindowUpdateFrame window_update_4(stream_->id(), 5678);
+ stream_->OnWindowUpdateFrame(window_update_2);
+ stream_->OnWindowUpdateFrame(window_update_3);
+ stream_->OnWindowUpdateFrame(window_update_4);
+ EXPECT_EQ(
+ window_update_3.byte_offset,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+}
+
+TEST_F(ReliableQuicStreamTest, StreamFlowControlShouldNotBlockInLessThanQ017) {
+ // TODO(rjshade): Remove this test when we no longer have any versions <
+ // QUIC_VERSION_17.
+ ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+ // Make sure we are using a version which does not support flow control.
+ QuicVersion kTestQuicVersions[] = {QUIC_VERSION_16};
+ QuicVersionVector versions;
+ for (size_t i = 0; i < arraysize(kTestQuicVersions); ++i) {
+ versions.push_back(kTestQuicVersions[i]);
+ }
+ set_supported_versions(versions);
+
+ // Peer is not talking QUIC_VERSION_17 so assumes that it can send a zero
+ // length flow control receive window with no consequences.
+ set_initial_flow_control_window_bytes(0);
+
+ Initialize(kShouldProcessData);
+
+ // The stream should _not_ be flow control blocked, because we are not talking
+ // a version which has flow control enabled.
+ EXPECT_FALSE(stream_->flow_controller()->IsBlocked());
+}
+
+void SaveProxyAckNotifierDelegate(
+ scoped_refptr<QuicAckNotifier::DelegateInterface>* delegate_out,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ *delegate_out = delegate;
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithQuicAckNotifier) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ const int kDataSize = 16 * 1024;
+ const string kData(kDataSize, 'a');
+
+ const int kFirstWriteSize = 100;
+ const int kSecondWriteSize = 50;
+ const int kLastWriteSize = kDataSize - kFirstWriteSize - kSecondWriteSize;
+
+ // Set a large flow control send window so this doesn't interfere with test.
+ stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+ if (FLAGS_enable_quic_connection_flow_control_2) {
+ session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+ }
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kFirstWriteSize, false))));
+ stream_->WriteOrBufferData(kData, false, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ EXPECT_CALL(*session_,
+ WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get()))
+ .WillOnce(Return(QuicConsumedData(kSecondWriteSize, false)));
+ stream_->OnCanWrite();
+
+ // No ack expected for an empty write.
+ EXPECT_CALL(*session_,
+ WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get()))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->OnCanWrite();
+
+ EXPECT_CALL(*session_,
+ WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get()))
+ .WillOnce(Return(QuicConsumedData(kLastWriteSize, false)));
+ stream_->OnCanWrite();
+
+ // There were two writes, so OnAckNotification is not propagated
+ // until the third Ack arrives.
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+ proxy_delegate->OnAckNotification(10, 20, 30, 40, zero_);
+
+ // The arguments to delegate->OnAckNotification are the sum of the
+ // arguments to proxy_delegate OnAckNotification calls.
+ EXPECT_CALL(*delegate, OnAckNotification(111, 222, 333, 444, zero_));
+ proxy_delegate->OnAckNotification(100, 200, 300, 400, zero_);
+}
+
+// Verify delegate behavior when packets are acked before the
+// WritevData call that sends out the last byte.
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataAckNotificationBeforeFlush) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ const int kDataSize = 16 * 1024;
+ const string kData(kDataSize, 'a');
+
+ const int kInitialWriteSize = 100;
+
+ // Set a large flow control send window so this doesn't interfere with test.
+ stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+ if (FLAGS_enable_quic_connection_flow_control_2) {
+ session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+ }
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kInitialWriteSize, false))));
+ stream_->WriteOrBufferData(kData, false, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Handle the ack of the first write.
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+ proxy_delegate = NULL;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)).WillOnce(
+ DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataSize - kInitialWriteSize, false))));
+ stream_->OnCanWrite();
+
+ // Handle the ack for the second write.
+ EXPECT_CALL(*delegate, OnAckNotification(101, 202, 303, 404, zero_));
+ proxy_delegate->OnAckNotification(100, 200, 300, 400, zero_);
+}
+
+// Verify delegate behavior when WriteOrBufferData does not buffer.
+TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferNoBuffer) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataLen, true))));
+ stream_->WriteOrBufferData(kData1, true, delegate.get());
+ EXPECT_FALSE(HasWriteBlockedStreams());
+
+ // Handle the ack.
+ EXPECT_CALL(*delegate, OnAckNotification(1, 2, 3, 4, zero_));
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+}
+
+// Verify delegate behavior when WriteOrBufferData buffers all the data.
+TEST_F(ReliableQuicStreamTest, BufferOnWriteAndBufferDataWithAckNotifer) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->WriteOrBufferData(kData1, true, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataLen, true))));
+ stream_->OnCanWrite();
+
+ // Handle the ack.
+ EXPECT_CALL(*delegate, OnAckNotification(1, 2, 3, 4, zero_));
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+}
+
+// Verify delegate behavior when WriteOrBufferData when the FIN is
+// sent out in a different packet.
+TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferOnlyFinRemains) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataLen, false))));
+ stream_->WriteOrBufferData(kData1, true, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(0, true))));
+ stream_->OnCanWrite();
+
+ // Handle the acks.
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+ EXPECT_CALL(*delegate, OnAckNotification(11, 22, 33, 44, zero_));
+ proxy_delegate->OnAckNotification(10, 20, 30, 40, zero_);
+}
+
+// Verify that when we receive a packet which violates flow control (i.e. sends
+// too much data on the stream) that the stream sequencer never sees this frame,
+// as we check for violation and close the connection early.
+TEST_F(ReliableQuicStreamTest,
+ StreamSequencerNeverSeesPacketsViolatingFlowControl) {
+ ValueRestore<bool> old_stream_flag(
+ &FLAGS_enable_quic_stream_flow_control_2, true);
+ ValueRestore<bool> old_connection_flag(
+ &FLAGS_enable_quic_connection_flow_control_2, true);
+
+ Initialize(kShouldProcessData);
+
+ // Receive a stream frame that violates flow control: the byte offset is
+ // higher than the receive window offset.
+ QuicStreamFrame frame(stream_->id(), false,
+ kInitialSessionFlowControlWindowForTest + 1,
+ MakeIOVector("."));
+ EXPECT_GT(frame.offset, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream should not accept the frame, and the connection should be closed.
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA));
+ EXPECT_FALSE(stream_->OnStreamFrame(frame));
+}
+
+TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromFin) {
+ Initialize(kShouldProcessData);
+
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+
+ QuicStreamFrame stream_frame_no_fin(stream_->id(), false, 1234,
+ MakeIOVector("."));
+ stream_->OnStreamFrame(stream_frame_no_fin);
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+
+ QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234,
+ MakeIOVector("."));
+ stream_->OnStreamFrame(stream_frame_with_fin);
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+}
+
+TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromRst) {
+ Initialize(kShouldProcessData);
+
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+ QuicRstStreamFrame rst_frame(stream_->id(), QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/spdy_utils.cc b/chromium/net/quic/spdy_utils.cc
index 350819e9143..c0894d54e6d 100644
--- a/chromium/net/quic/spdy_utils.cc
+++ b/chromium/net/quic/spdy_utils.cc
@@ -16,7 +16,7 @@ namespace net {
// static
string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) {
int length = SpdyFramer::GetSerializedLength(SPDY3, &headers);
- SpdyFrameBuilder builder(length);
+ SpdyFrameBuilder builder(length, SPDY3);
SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers);
scoped_ptr<SpdyFrame> block(builder.take());
return string(block->data(), length);
diff --git a/chromium/net/quic/spdy_utils.h b/chromium/net/quic/spdy_utils.h
index ffc78f08da2..14d33002216 100644
--- a/chromium/net/quic/spdy_utils.h
+++ b/chromium/net/quic/spdy_utils.h
@@ -16,6 +16,9 @@ class NET_EXPORT_PRIVATE SpdyUtils {
public:
static std::string SerializeUncompressedHeaders(
const SpdyHeaderBlock& headers);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SpdyUtils);
};
} // namespace net
diff --git a/chromium/net/quic/test_tools/crypto_test_utils.cc b/chromium/net/quic/test_tools/crypto_test_utils.cc
index a6b6cc1ed85..0dbf906c868 100644
--- a/chromium/net/quic/test_tools/crypto_test_utils.cc
+++ b/chromium/net/quic/test_tools/crypto_test_utils.cc
@@ -15,6 +15,7 @@
#include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/quic_crypto_server_stream.h"
#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_server_id.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/simple_quic_framer.h"
@@ -30,6 +31,9 @@ namespace test {
namespace {
+const char kServerHostname[] = "test.example.com";
+const uint16 kServerPort = 80;
+
// CryptoFramerVisitor is a framer visitor that records handshake messages.
class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
public:
@@ -37,9 +41,7 @@ class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
: error_(false) {
}
- virtual void OnError(CryptoFramer* framer) OVERRIDE {
- error_ = true;
- }
+ virtual void OnError(CryptoFramer* framer) OVERRIDE { error_ = true; }
virtual void OnHandshakeMessage(
const CryptoHandshakeMessage& message) OVERRIDE {
@@ -67,7 +69,7 @@ void MovePackets(PacketSavingConnection* source_conn,
size_t *inout_packet_index,
QuicCryptoStream* dest_stream,
PacketSavingConnection* dest_conn) {
- SimpleQuicFramer framer;
+ SimpleQuicFramer framer(source_conn->supported_versions());
CryptoFramer crypto_framer;
CryptoFramerVisitor crypto_visitor;
@@ -133,7 +135,8 @@ CryptoTestUtils::FakeClientOptions::FakeClientOptions()
int CryptoTestUtils::HandshakeWithFakeServer(
PacketSavingConnection* client_conn,
QuicCryptoClientStream* client) {
- PacketSavingConnection* server_conn = new PacketSavingConnection(true);
+ PacketSavingConnection* server_conn =
+ new PacketSavingConnection(true, client_conn->supported_versions());
TestSession server_session(server_conn, DefaultQuicConfig());
QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING,
@@ -162,7 +165,7 @@ int CryptoTestUtils::HandshakeWithFakeClient(
QuicCryptoServerStream* server,
const FakeClientOptions& options) {
PacketSavingConnection* client_conn = new PacketSavingConnection(false);
- TestSession client_session(client_conn, DefaultQuicConfig());
+ TestClientSession client_session(client_conn, DefaultQuicConfig());
QuicCryptoClientConfig crypto_config;
client_session.config()->SetDefaults();
@@ -172,9 +175,11 @@ int CryptoTestUtils::HandshakeWithFakeClient(
// crypto_config.SetProofVerifier(ProofVerifierForTesting());
// }
if (options.channel_id_enabled) {
- crypto_config.SetChannelIDSigner(ChannelIDSignerForTesting());
+ crypto_config.SetChannelIDSource(ChannelIDSourceForTesting());
}
- QuicCryptoClientStream client("test.example.com", &client_session,
+ QuicServerId server_id(kServerHostname, kServerPort, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientStream client(server_id, &client_session, NULL,
&crypto_config);
client_session.SetCryptoStream(&client);
@@ -186,8 +191,13 @@ int CryptoTestUtils::HandshakeWithFakeClient(
CompareClientAndServerKeys(&client, server);
if (options.channel_id_enabled) {
- EXPECT_EQ(crypto_config.channel_id_signer()->GetKeyForHostname(
- "test.example.com"),
+ scoped_ptr<ChannelIDKey> channel_id_key;
+ QuicAsyncStatus status =
+ crypto_config.channel_id_source()->GetChannelIDKey(kServerHostname,
+ &channel_id_key,
+ NULL);
+ EXPECT_EQ(QUIC_SUCCESS, status);
+ EXPECT_EQ(channel_id_key->SerializeKey(),
server->crypto_negotiated_params().channel_id);
}
@@ -230,6 +240,7 @@ void CryptoTestUtils::CommunicateHandshakeMessages(
}
}
+// static
pair<size_t, size_t> CryptoTestUtils::AdvanceHandshake(
PacketSavingConnection* a_conn,
QuicCryptoStream* a,
@@ -486,7 +497,7 @@ CryptoHandshakeMessage CryptoTestUtils::BuildMessage(const char* message_tag,
valuestr++;
len--;
- CHECK(len % 2 == 0);
+ CHECK_EQ(0u, len % 2);
scoped_ptr<uint8[]> buf(new uint8[len/2]);
for (size_t i = 0; i < len/2; i++) {
diff --git a/chromium/net/quic/test_tools/crypto_test_utils.h b/chromium/net/quic/test_tools/crypto_test_utils.h
index 60674a9e63d..a226c85ad9c 100644
--- a/chromium/net/quic/test_tools/crypto_test_utils.h
+++ b/chromium/net/quic/test_tools/crypto_test_utils.h
@@ -10,6 +10,7 @@
#include <utility>
#include <vector>
+#include "base/basictypes.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "net/quic/crypto/crypto_framer.h"
@@ -18,10 +19,11 @@
namespace net {
-class ChannelIDSigner;
+class ChannelIDSource;
class CommonCertSets;
class ProofSource;
class ProofVerifier;
+class ProofVerifyContext;
class QuicClock;
class QuicConfig;
class QuicCryptoClientStream;
@@ -46,8 +48,7 @@ class CryptoTestUtils {
bool dont_verify_certs;
// If channel_id_enabled is true then the client will attempt to send a
- // ChannelID. The key will be the same as is returned by
- // ChannelIDSigner's |GetKeyForHostname|.
+ // ChannelID.
bool channel_id_enabled;
};
@@ -95,6 +96,10 @@ class CryptoTestUtils {
// Returns a |ProofVerifier| that uses the QUIC testing root CA.
static ProofVerifier* ProofVerifierForTesting();
+ // Returns a |ProofVerifyContext| that must be used with the verifier
+ // returned by |ProofVerifierForTesting|.
+ static ProofVerifyContext* ProofVerifyContextForTesting();
+
// MockCommonCertSets returns a CommonCertSets that contains a single set with
// hash |hash|, consisting of the certificate |cert| at index |index|.
static CommonCertSets* MockCommonCertSets(base::StringPiece cert,
@@ -127,13 +132,17 @@ class CryptoTestUtils {
static CryptoHandshakeMessage BuildMessage(const char* message_tag,
va_list ap);
- // ChannelIDSignerForTesting returns a ChannelIDSigner that generates keys
- // deterministically based on the hostname given in the Sign call.
- static ChannelIDSigner* ChannelIDSignerForTesting();
+ // ChannelIDSourceForTesting returns a ChannelIDSource that generates keys
+ // deterministically based on the hostname given in the GetChannelIDKey call.
+ // This ChannelIDSource works in synchronous mode, i.e., its GetChannelIDKey
+ // method never returns QUIC_PENDING.
+ static ChannelIDSource* ChannelIDSourceForTesting();
private:
static void CompareClientAndServerKeys(QuicCryptoClientStream* client,
QuicCryptoServerStream* server);
+
+ DISALLOW_COPY_AND_ASSIGN(CryptoTestUtils);
};
} // namespace test
diff --git a/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc b/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc
index 8aaef425dac..9deee8388b7 100644
--- a/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc
+++ b/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc
@@ -22,14 +22,14 @@ class TestProofVerifierChromium : public ProofVerifierChromium {
public:
TestProofVerifierChromium(CertVerifier* cert_verifier,
const std::string& cert_file)
- : ProofVerifierChromium(cert_verifier, BoundNetLog()),
+ : ProofVerifierChromium(cert_verifier),
cert_verifier_(cert_verifier) {
// Load and install the root for the validated chain.
scoped_refptr<X509Certificate> root_cert =
ImportCertFromFile(GetTestCertsDirectory(), cert_file);
scoped_root_.Reset(root_cert.get());
}
- virtual ~TestProofVerifierChromium() { }
+ virtual ~TestProofVerifierChromium() {}
private:
ScopedTestRoot scoped_root_;
@@ -48,6 +48,11 @@ ProofVerifier* CryptoTestUtils::ProofVerifierForTesting() {
return proof_verifier;
}
+// static
+ProofVerifyContext* CryptoTestUtils::ProofVerifyContextForTesting() {
+ return new ProofVerifyContextChromium(BoundNetLog());
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/crypto_test_utils_nss.cc b/chromium/net/quic/test_tools/crypto_test_utils_nss.cc
index 88c87679b93..c5aede89efd 100644
--- a/chromium/net/quic/test_tools/crypto_test_utils_nss.cc
+++ b/chromium/net/quic/test_tools/crypto_test_utils_nss.cc
@@ -20,31 +20,18 @@ namespace net {
namespace test {
-// TODO(rtenneti): Implement NSS support ChannelIDSigner. Convert Sign() to be
-// asynchronous using completion callback. After porting TestChannelIDSigner,
-// implement real ChannelIDSigner.
-class TestChannelIDSigner : public ChannelIDSigner {
+// TODO(rtenneti): Convert Sign() to be asynchronous using a completion
+// callback.
+class TestChannelIDKey : public ChannelIDKey {
public:
- virtual ~TestChannelIDSigner() {
- STLDeleteValues(&hostname_to_key_);
- }
-
- // ChannelIDSigner implementation.
-
- virtual bool Sign(const string& hostname,
- StringPiece signed_data,
- string* out_key,
- string* out_signature) OVERRIDE {
- crypto::ECPrivateKey* ecdsa_keypair = HostnameToKey(hostname);
- if (!ecdsa_keypair) {
- return false;
- }
+ explicit TestChannelIDKey(crypto::ECPrivateKey* ecdsa_keypair)
+ : ecdsa_keypair_(ecdsa_keypair) {}
+ virtual ~TestChannelIDKey() {}
- *out_key = SerializeKey(ecdsa_keypair->public_key());
- if (out_key->empty()) {
- return false;
- }
+ // ChannelIDKey implementation.
+ virtual bool Sign(StringPiece signed_data,
+ string* out_signature) const OVERRIDE {
unsigned char hash_buf[SHA256_LENGTH];
SECItem hash_item = { siBuffer, hash_buf, sizeof(hash_buf) };
@@ -77,19 +64,49 @@ class TestChannelIDSigner : public ChannelIDSigner {
kSignatureLength
};
- if (PK11_Sign(ecdsa_keypair->key(), &sig_item, &hash_item) != SECSuccess) {
+ if (PK11_Sign(ecdsa_keypair_->key(), &sig_item, &hash_item) != SECSuccess) {
return false;
}
*out_signature = signature;
return true;
}
- virtual string GetKeyForHostname(const string& hostname) OVERRIDE {
- crypto::ECPrivateKey* ecdsa_keypair = HostnameToKey(hostname);
- if (!ecdsa_keypair) {
+ virtual string SerializeKey() const OVERRIDE {
+ const SECKEYPublicKey* public_key = ecdsa_keypair_->public_key();
+
+ // public_key->u.ec.publicValue is an ANSI X9.62 public key which, for
+ // a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field
+ // elements as 32-byte, big-endian numbers.
+ static const unsigned int kExpectedKeyLength = 65;
+
+ const unsigned char* const data = public_key->u.ec.publicValue.data;
+ const unsigned int len = public_key->u.ec.publicValue.len;
+ if (len != kExpectedKeyLength || data[0] != 0x04) {
return "";
}
- return SerializeKey(ecdsa_keypair->public_key());
+
+ string key(reinterpret_cast<const char*>(data + 1), kExpectedKeyLength - 1);
+ return key;
+ }
+
+ private:
+ crypto::ECPrivateKey* ecdsa_keypair_;
+};
+
+class TestChannelIDSource : public ChannelIDSource {
+ public:
+ virtual ~TestChannelIDSource() {
+ STLDeleteValues(&hostname_to_key_);
+ }
+
+ // ChannelIDSource implementation.
+
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) OVERRIDE {
+ channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname)));
+ return QUIC_SUCCESS;
}
private:
@@ -109,28 +126,12 @@ class TestChannelIDSigner : public ChannelIDSigner {
return keypair;
}
- static string SerializeKey(const SECKEYPublicKey* public_key) {
- // public_key->u.ec.publicValue is an ANSI X9.62 public key which, for
- // a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field
- // elements as 32-byte, big-endian numbers.
- static const unsigned int kExpectedKeyLength = 65;
-
- const unsigned char* const data = public_key->u.ec.publicValue.data;
- const unsigned int len = public_key->u.ec.publicValue.len;
- if (len != kExpectedKeyLength || data[0] != 0x04) {
- return "";
- }
-
- string key(reinterpret_cast<const char*>(data + 1), kExpectedKeyLength - 1);
- return key;
- }
-
HostnameToKeyMap hostname_to_key_;
};
// static
-ChannelIDSigner* CryptoTestUtils::ChannelIDSignerForTesting() {
- return new TestChannelIDSigner();
+ChannelIDSource* CryptoTestUtils::ChannelIDSourceForTesting() {
+ return new TestChannelIDSource();
}
} // namespace test
diff --git a/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc b/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc
index bb08a044aa4..388d257001f 100644
--- a/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc
+++ b/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc
@@ -30,31 +30,22 @@ namespace net {
namespace test {
-class TestChannelIDSigner : public ChannelIDSigner {
+class TestChannelIDKey : public ChannelIDKey {
public:
- virtual ~TestChannelIDSigner() { }
+ explicit TestChannelIDKey(EVP_PKEY* ecdsa_key) : ecdsa_key_(ecdsa_key) {}
+ virtual ~TestChannelIDKey() OVERRIDE {}
- // ChannelIDSigner implementation.
-
- virtual bool Sign(const string& hostname,
- StringPiece signed_data,
- string* out_key,
- string* out_signature) OVERRIDE {
- crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key(
- HostnameToKey(hostname));
-
- *out_key = SerializeKey(ecdsa_key.get());
- if (out_key->empty()) {
- return false;
- }
+ // ChannelIDKey implementation.
+ virtual bool Sign(StringPiece signed_data,
+ string* out_signature) const OVERRIDE {
EVP_MD_CTX md_ctx;
EVP_MD_CTX_init(&md_ctx);
crypto::ScopedOpenSSL<EVP_MD_CTX, EvpMdCtxCleanUp>
md_ctx_cleanup(&md_ctx);
if (EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL,
- ecdsa_key.get()) != 1) {
+ ecdsa_key_.get()) != 1) {
return false;
}
@@ -94,10 +85,40 @@ class TestChannelIDSigner : public ChannelIDSigner {
return true;
}
- virtual string GetKeyForHostname(const string& hostname) OVERRIDE {
- crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key(
- HostnameToKey(hostname));
- return SerializeKey(ecdsa_key.get());
+ virtual string SerializeKey() const OVERRIDE {
+ // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256
+ // key, is 0x04 (meaning uncompressed) followed by the x and y field
+ // elements as 32-byte, big-endian numbers.
+ static const int kExpectedKeyLength = 65;
+
+ int len = i2d_PublicKey(ecdsa_key_.get(), NULL);
+ if (len != kExpectedKeyLength) {
+ return "";
+ }
+
+ uint8 buf[kExpectedKeyLength];
+ uint8* derp = buf;
+ i2d_PublicKey(ecdsa_key_.get(), &derp);
+
+ return string(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1);
+ }
+
+ private:
+ crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key_;
+};
+
+class TestChannelIDSource : public ChannelIDSource {
+ public:
+ virtual ~TestChannelIDSource() {}
+
+ // ChannelIDSource implementation.
+
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) OVERRIDE {
+ channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname)));
+ return QUIC_SUCCESS;
}
private:
@@ -143,29 +164,11 @@ class TestChannelIDSigner : public ChannelIDSigner {
return pkey.release();
}
-
- static string SerializeKey(EVP_PKEY* key) {
- // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256
- // key, is 0x04 (meaning uncompressed) followed by the x and y field
- // elements as 32-byte, big-endian numbers.
- static const int kExpectedKeyLength = 65;
-
- int len = i2d_PublicKey(key, NULL);
- if (len != kExpectedKeyLength) {
- return "";
- }
-
- uint8 buf[kExpectedKeyLength];
- uint8* derp = buf;
- i2d_PublicKey(key, &derp);
-
- return string(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1);
- }
};
// static
-ChannelIDSigner* CryptoTestUtils::ChannelIDSignerForTesting() {
- return new TestChannelIDSigner();
+ChannelIDSource* CryptoTestUtils::ChannelIDSourceForTesting() {
+ return new TestChannelIDSource();
}
} // namespace test
diff --git a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc
index eec16b16e8d..b14a1180296 100644
--- a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc
+++ b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc
@@ -29,7 +29,7 @@ void DelayedVerifyStrikeRegisterClient::VerifyNonceIsValidAndUnique(
QuicWallTime now,
ResultCallback* cb) {
if (delay_verifications_) {
- pending_verifications_.push_back(VerifyArgs(nonce.as_string(), now, cb));
+ pending_verifications_.push_back(VerifyArgs(nonce, now, cb));
} else {
LocalStrikeRegisterClient::VerifyNonceIsValidAndUnique(nonce, now, cb);
}
diff --git a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h
index 69de1d077d3..6ae7ba1ee2a 100644
--- a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h
+++ b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h
@@ -43,7 +43,9 @@ class DelayedVerifyStrikeRegisterClient : public LocalStrikeRegisterClient {
VerifyArgs(base::StringPiece in_nonce,
QuicWallTime in_now,
ResultCallback* in_cb)
- : nonce(in_nonce.as_string()), now(in_now), cb(in_cb) {
+ : nonce(in_nonce.as_string()),
+ now(in_now),
+ cb(in_cb) {
}
std::string nonce;
diff --git a/chromium/net/quic/test_tools/mock_clock.h b/chromium/net/quic/test_tools/mock_clock.h
index 5c9631ae84c..d6e490f8961 100644
--- a/chromium/net/quic/test_tools/mock_clock.h
+++ b/chromium/net/quic/test_tools/mock_clock.h
@@ -16,7 +16,6 @@ namespace net {
class MockClock : public QuicClock {
public:
MockClock();
-
virtual ~MockClock();
void AdvanceTime(QuicTime::Delta delta);
@@ -31,6 +30,8 @@ class MockClock : public QuicClock {
private:
QuicTime now_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockClock);
};
} // namespace net
diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream.cc b/chromium/net/quic/test_tools/mock_crypto_client_stream.cc
index bfeb06f0629..a4b3ab0289d 100644
--- a/chromium/net/quic/test_tools/mock_crypto_client_stream.cc
+++ b/chromium/net/quic/test_tools/mock_crypto_client_stream.cc
@@ -3,17 +3,25 @@
// found in the LICENSE file.
#include "net/quic/test_tools/mock_crypto_client_stream.h"
+
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/quic_client_session_base.h"
+#include "net/quic/quic_server_id.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
MockCryptoClientStream::MockCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
QuicCryptoClientConfig* crypto_config,
- HandshakeMode handshake_mode)
- : QuicCryptoClientStream(server_hostname, session, crypto_config),
- handshake_mode_(handshake_mode) {
+ HandshakeMode handshake_mode,
+ const ProofVerifyDetails* proof_verify_details)
+ : QuicCryptoClientStream(server_id, session, verify_context,
+ crypto_config),
+ handshake_mode_(handshake_mode),
+ proof_verify_details_(proof_verify_details) {
}
MockCryptoClientStream::~MockCryptoClientStream() {
@@ -29,6 +37,8 @@ bool MockCryptoClientStream::CryptoConnect() {
case ZERO_RTT: {
encryption_established_ = true;
handshake_confirmed_ = false;
+ session()->connection()->SetDecrypter(QuicDecrypter::Create(kNULL),
+ ENCRYPTION_INITIAL);
session()->OnCryptoHandshakeEvent(
QuicSession::ENCRYPTION_FIRST_ESTABLISHED);
break;
@@ -37,7 +47,14 @@ bool MockCryptoClientStream::CryptoConnect() {
case CONFIRM_HANDSHAKE: {
encryption_established_ = true;
handshake_confirmed_ = true;
+ crypto_negotiated_params_.key_exchange = kC255;
+ crypto_negotiated_params_.aead = kAESG;
+ if (proof_verify_details_) {
+ client_session()->OnProofVerifyDetailsAvailable(*proof_verify_details_);
+ }
SetConfigNegotiated();
+ session()->connection()->SetDecrypter(QuicDecrypter::Create(kNULL),
+ ENCRYPTION_FORWARD_SECURE);
session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
break;
}
@@ -66,7 +83,7 @@ void MockCryptoClientStream::SetConfigNegotiated() {
QuicTagVector cgst;
cgst.push_back(kINAR);
cgst.push_back(kQBIC);
- session()->config()->set_congestion_control(cgst, kQBIC);
+ session()->config()->set_congestion_feedback(cgst, kQBIC);
session()->config()->set_idle_connection_state_lifetime(
QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs),
QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs));
@@ -77,9 +94,13 @@ void MockCryptoClientStream::SetConfigNegotiated() {
session()->config()->ToHandshakeMessage(&msg);
string error_details;
const QuicErrorCode error =
- session()->config()->ProcessClientHello(msg, &error_details);
+ session()->config()->ProcessPeerHello(msg, CLIENT, &error_details);
ASSERT_EQ(QUIC_NO_ERROR, error);
ASSERT_TRUE(session()->config()->negotiated());
}
+QuicClientSessionBase* MockCryptoClientStream::client_session() {
+ return reinterpret_cast<QuicClientSessionBase*>(session());
+}
+
} // namespace net
diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream.h b/chromium/net/quic/test_tools/mock_crypto_client_stream.h
index ada1b6883ba..e940a1e940c 100644
--- a/chromium/net/quic/test_tools/mock_crypto_client_stream.h
+++ b/chromium/net/quic/test_tools/mock_crypto_client_stream.h
@@ -14,6 +14,8 @@
namespace net {
+class QuicServerId;
+
class MockCryptoClientStream : public QuicCryptoClientStream {
public:
// HandshakeMode enumerates the handshake mode MockCryptoClientStream should
@@ -34,10 +36,12 @@ class MockCryptoClientStream : public QuicCryptoClientStream {
};
MockCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
QuicCryptoClientConfig* crypto_config,
- HandshakeMode handshake_mode);
+ HandshakeMode handshake_mode,
+ const ProofVerifyDetails* proof_verify_details_);
virtual ~MockCryptoClientStream();
// CryptoFramerVisitorInterface implementation.
@@ -55,6 +59,11 @@ class MockCryptoClientStream : public QuicCryptoClientStream {
private:
void SetConfigNegotiated();
+ QuicClientSessionBase* client_session();
+
+ const ProofVerifyDetails* proof_verify_details_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCryptoClientStream);
};
} // namespace net
diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc
index e54fb41e2f5..302ed07e9aa 100644
--- a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc
+++ b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc
@@ -5,7 +5,9 @@
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
#include "base/lazy_instance.h"
+#include "net/quic/quic_client_session.h"
#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_server_id.h"
using std::string;
@@ -13,16 +15,18 @@ namespace net {
MockCryptoClientStreamFactory::MockCryptoClientStreamFactory()
: handshake_mode_(MockCryptoClientStream::CONFIRM_HANDSHAKE),
- last_stream_(NULL) {
+ last_stream_(NULL),
+ proof_verify_details_(NULL) {
}
QuicCryptoClientStream*
MockCryptoClientStreamFactory::CreateQuicCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSession* session,
QuicCryptoClientConfig* crypto_config) {
- last_stream_ = new MockCryptoClientStream(server_hostname, session,
- crypto_config, handshake_mode_);
+ last_stream_ = new MockCryptoClientStream(
+ server_id, session, NULL, crypto_config, handshake_mode_,
+ proof_verify_details_);
return last_stream_;
}
diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h
index 9d056cbc72d..721ec258cee 100644
--- a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h
+++ b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h
@@ -13,15 +13,16 @@
namespace net {
+class QuicServerId;
+
class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory {
public:
MockCryptoClientStreamFactory();
-
virtual ~MockCryptoClientStreamFactory() {}
virtual QuicCryptoClientStream* CreateQuicCryptoClientStream(
- const string& server_hostname,
- QuicSession* session,
+ const QuicServerId& server_id,
+ QuicClientSession* session,
QuicCryptoClientConfig* crypto_config) OVERRIDE;
void set_handshake_mode(
@@ -29,6 +30,11 @@ class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory {
handshake_mode_ = handshake_mode;
}
+ void set_proof_verify_details(
+ const ProofVerifyDetails* proof_verify_details) {
+ proof_verify_details_ = proof_verify_details;
+ }
+
MockCryptoClientStream* last_stream() const {
return last_stream_;
}
@@ -36,6 +42,9 @@ class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory {
private:
MockCryptoClientStream::HandshakeMode handshake_mode_;
MockCryptoClientStream* last_stream_;
+ const ProofVerifyDetails* proof_verify_details_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCryptoClientStreamFactory);
};
} // namespace net
diff --git a/chromium/net/quic/test_tools/mock_quic_dispatcher.cc b/chromium/net/quic/test_tools/mock_quic_dispatcher.cc
new file mode 100644
index 00000000000..f3f41a5ae10
--- /dev/null
+++ b/chromium/net/quic/test_tools/mock_quic_dispatcher.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/mock_quic_dispatcher.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+namespace net {
+namespace test {
+
+MockQuicDispatcher::MockQuicDispatcher(
+ const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ QuicConnectionHelperInterface* helper)
+ : QuicDispatcher(config, crypto_config, QuicSupportedVersions(), helper) {
+}
+
+MockQuicDispatcher::~MockQuicDispatcher() {
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/mock_quic_dispatcher.h b/chromium/net/quic/test_tools/mock_quic_dispatcher.h
new file mode 100644
index 00000000000..93ecda4ce6d
--- /dev/null
+++ b/chromium/net/quic/test_tools/mock_quic_dispatcher.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+#define NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/quic_crypto_server_config.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_dispatcher.h"
+#include "net/quic/quic_protocol.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+namespace test {
+
+class MockQuicDispatcher : public QuicDispatcher {
+ public:
+ MockQuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ QuicConnectionHelperInterface* helper);
+
+ virtual ~MockQuicDispatcher();
+
+ MOCK_METHOD3(ProcessPacket, void(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ const QuicEncryptedPacket& packet));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockQuicDispatcher);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
diff --git a/chromium/net/quic/test_tools/mock_random.h b/chromium/net/quic/test_tools/mock_random.h
index 3a3c87f4497..53b24b74eeb 100644
--- a/chromium/net/quic/test_tools/mock_random.h
+++ b/chromium/net/quic/test_tools/mock_random.h
@@ -14,7 +14,7 @@ class MockRandom : public QuicRandom {
public:
// Initializes base_ to 0xDEADBEEF.
MockRandom();
- MockRandom(uint32 base);
+ explicit MockRandom(uint32 base);
// QuicRandom:
// Fills the |data| buffer with a repeating byte, initially 'r'.
@@ -32,6 +32,8 @@ class MockRandom : public QuicRandom {
private:
uint32 base_;
uint8 increment_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockRandom);
};
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_config_peer.cc b/chromium/net/quic/test_tools/quic_config_peer.cc
new file mode 100644
index 00000000000..798209c1855
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_config_peer.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_config_peer.h"
+
+#include "net/quic/quic_config.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicConfigPeer::SetReceivedInitialWindow(QuicConfig* config,
+ size_t initial_window) {
+ config->initial_congestion_window_.SetReceivedValue(initial_window);
+}
+
+// static
+void QuicConfigPeer::SetReceivedLossDetection(QuicConfig* config,
+ QuicTag loss_detection) {
+ config->loss_detection_.SetReceivedValue(loss_detection);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes) {
+ config->initial_flow_control_window_bytes_.SetReceivedValue(window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ QuicConfig* config, uint32 window_bytes) {
+ config->initial_stream_flow_control_window_bytes_.SetReceivedValue(
+ window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
+ QuicConfig* config, uint32 window_bytes) {
+ config->initial_session_flow_control_window_bytes_.SetReceivedValue(
+ window_bytes);
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_config_peer.h b/chromium/net/quic/test_tools/quic_config_peer.h
new file mode 100644
index 00000000000..53abb987635
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_config_peer.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicConfig;
+
+namespace test {
+
+class QuicConfigPeer {
+ public:
+ static void SetReceivedInitialWindow(QuicConfig* config,
+ size_t initial_window);
+
+ static void SetReceivedLossDetection(QuicConfig* config,
+ QuicTag loss_detection);
+
+ // TODO(rjshade): Remove when removing QUIC_VERSION_19.
+ static void SetReceivedInitialFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes);
+
+ static void SetReceivedInitialStreamFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes);
+
+ static void SetReceivedInitialSessionFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicConfigPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
diff --git a/chromium/net/quic/test_tools/quic_connection_peer.cc b/chromium/net/quic/test_tools/quic_connection_peer.cc
index 54410b3bec1..e51fb52480a 100644
--- a/chromium/net/quic/test_tools/quic_connection_peer.cc
+++ b/chromium/net/quic/test_tools/quic_connection_peer.cc
@@ -11,8 +11,8 @@
#include "net/quic/quic_packet_writer.h"
#include "net/quic/quic_received_packet_manager.h"
#include "net/quic/test_tools/quic_framer_peer.h"
+#include "net/quic/test_tools/quic_packet_generator_peer.h"
#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
-#include "net/quic/test_tools/quic_test_writer.h"
namespace net {
namespace test {
@@ -51,7 +51,20 @@ QuicConnectionVisitorInterface* QuicConnectionPeer::GetVisitor(
// static
QuicPacketCreator* QuicConnectionPeer::GetPacketCreator(
QuicConnection* connection) {
- return &connection->packet_creator_;
+ return QuicPacketGeneratorPeer::GetPacketCreator(
+ &connection->packet_generator_);
+}
+
+// static
+QuicPacketGenerator* QuicConnectionPeer::GetPacketGenerator(
+ QuicConnection* connection) {
+ return &connection->packet_generator_;
+}
+
+// static
+QuicSentPacketManager* QuicConnectionPeer::GetSentPacketManager(
+ QuicConnection* connection) {
+ return &connection->sent_packet_manager_;
}
// static
@@ -110,17 +123,6 @@ QuicPacketEntropyHash QuicConnectionPeer::ReceivedEntropyHash(
}
// static
-bool QuicConnectionPeer::IsWriteBlocked(QuicConnection* connection) {
- return connection->write_blocked_;
-}
-
-// static
-void QuicConnectionPeer::SetIsWriteBlocked(QuicConnection* connection,
- bool write_blocked) {
- connection->write_blocked_ = write_blocked;
-}
-
-// static
bool QuicConnectionPeer::IsServer(QuicConnection* connection) {
return connection->is_server_;
}
@@ -147,7 +149,7 @@ void QuicConnectionPeer::SetPeerAddress(QuicConnection* connection,
// static
void QuicConnectionPeer::SwapCrypters(QuicConnection* connection,
QuicFramer* framer) {
- framer->SwapCryptersForTest(&connection->framer_);
+ QuicFramerPeer::SwapCrypters(framer, &connection->framer_);
}
// static
@@ -173,6 +175,17 @@ QuicAlarm* QuicConnectionPeer::GetAckAlarm(QuicConnection* connection) {
}
// static
+QuicAlarm* QuicConnectionPeer::GetPingAlarm(QuicConnection* connection) {
+ return connection->ping_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetResumeWritesAlarm(
+ QuicConnection* connection) {
+ return connection->resume_writes_alarm_.get();
+}
+
+// static
QuicAlarm* QuicConnectionPeer::GetRetransmissionAlarm(
QuicConnection* connection) {
return connection->retransmission_alarm_.get();
@@ -184,12 +197,6 @@ QuicAlarm* QuicConnectionPeer::GetSendAlarm(QuicConnection* connection) {
}
// static
-QuicAlarm* QuicConnectionPeer::GetResumeWritesAlarm(
- QuicConnection* connection) {
- return connection->resume_writes_alarm_.get();
-}
-
-// static
QuicAlarm* QuicConnectionPeer::GetTimeoutAlarm(QuicConnection* connection) {
return connection->timeout_alarm_.get();
}
@@ -201,9 +208,26 @@ QuicPacketWriter* QuicConnectionPeer::GetWriter(QuicConnection* connection) {
// static
void QuicConnectionPeer::SetWriter(QuicConnection* connection,
- QuicTestWriter* writer) {
+ QuicPacketWriter* writer) {
connection->writer_ = writer;
}
+// static
+void QuicConnectionPeer::CloseConnection(QuicConnection* connection) {
+ connection->connected_ = false;
+}
+
+// static
+QuicEncryptedPacket* QuicConnectionPeer::GetConnectionClosePacket(
+ QuicConnection* connection) {
+ return connection->connection_close_packet_.get();
+}
+
+// static
+void QuicConnectionPeer::SetSupportedVersions(QuicConnection* connection,
+ QuicVersionVector versions) {
+ connection->framer_.SetSupportedVersions(versions);
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_connection_peer.h b/chromium/net/quic/test_tools/quic_connection_peer.h
index fd43661dae5..0cc6fbbdda2 100644
--- a/chromium/net/quic/test_tools/quic_connection_peer.h
+++ b/chromium/net/quic/test_tools/quic_connection_peer.h
@@ -18,18 +18,19 @@ class QuicAlarm;
class QuicConnection;
class QuicConnectionHelperInterface;
class QuicConnectionVisitorInterface;
+class QuicEncryptedPacket;
class QuicFecGroup;
class QuicFramer;
class QuicPacketCreator;
+class QuicPacketGenerator;
class QuicPacketWriter;
class QuicReceivedPacketManager;
+class QuicSentPacketManager;
class ReceiveAlgorithmInterface;
class SendAlgorithmInterface;
namespace test {
-class QuicTestWriter;
-
// Peer to make public a number of otherwise private QuicConnection methods.
class QuicConnectionPeer {
public:
@@ -48,6 +49,11 @@ class QuicConnectionPeer {
static QuicPacketCreator* GetPacketCreator(QuicConnection* connection);
+ static QuicPacketGenerator* GetPacketGenerator(QuicConnection* connection);
+
+ static QuicSentPacketManager* GetSentPacketManager(
+ QuicConnection* connection);
+
static QuicReceivedPacketManager* GetReceivedPacketManager(
QuicConnection* connection);
@@ -73,10 +79,6 @@ class QuicConnectionPeer {
QuicConnection* connection,
QuicPacketSequenceNumber sequence_number);
- static bool IsWriteBlocked(QuicConnection* connection);
-
- static void SetIsWriteBlocked(QuicConnection* connection, bool write_blocked);
-
static bool IsServer(QuicConnection* connection);
static void SetIsServer(QuicConnection* connection, bool is_server);
@@ -97,19 +99,27 @@ class QuicConnectionPeer {
static QuicFecGroup* GetFecGroup(QuicConnection* connection, int fec_group);
static QuicAlarm* GetAckAlarm(QuicConnection* connection);
+ static QuicAlarm* GetPingAlarm(QuicConnection* connection);
+ static QuicAlarm* GetResumeWritesAlarm(QuicConnection* connection);
static QuicAlarm* GetRetransmissionAlarm(QuicConnection* connection);
static QuicAlarm* GetSendAlarm(QuicConnection* connection);
- static QuicAlarm* GetResumeWritesAlarm(QuicConnection* connection);
static QuicAlarm* GetTimeoutAlarm(QuicConnection* connection);
static QuicPacketWriter* GetWriter(QuicConnection* connection);
- static void SetWriter(QuicConnection* connection, QuicTestWriter* writer);
+ static void SetWriter(QuicConnection* connection, QuicPacketWriter* writer);
+ static void CloseConnection(QuicConnection* connection);
+ static QuicEncryptedPacket* GetConnectionClosePacket(
+ QuicConnection* connection);
+
+ static void SetSupportedVersions(QuicConnection* connection,
+ QuicVersionVector versions);
private:
DISALLOW_COPY_AND_ASSIGN(QuicConnectionPeer);
};
} // namespace test
+
} // namespace net
-#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_PEER_H_
+#endif // NET_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
diff --git a/chromium/net/quic/test_tools/quic_data_stream_peer.h b/chromium/net/quic/test_tools/quic_data_stream_peer.h
index bfaf826dcdc..914b87d8686 100644
--- a/chromium/net/quic/test_tools/quic_data_stream_peer.h
+++ b/chromium/net/quic/test_tools/quic_data_stream_peer.h
@@ -6,7 +6,6 @@
#define NET_QUIC_TEST_TOOLS_QUIC_DATA_STREAM_PEER_H_
#include "base/basictypes.h"
-#include "net/quic/quic_protocol.h"
namespace net {
diff --git a/chromium/net/quic/test_tools/quic_flow_controller_peer.cc b/chromium/net/quic/test_tools/quic_flow_controller_peer.cc
new file mode 100644
index 00000000000..35882ede4bf
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_flow_controller_peer.cc
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+
+#include <list>
+
+#include "net/quic/quic_flow_controller.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicFlowControllerPeer::SetSendWindowOffset(
+ QuicFlowController* flow_controller,
+ uint64 offset) {
+ flow_controller->send_window_offset_ = offset;
+}
+
+// static
+void QuicFlowControllerPeer::SetReceiveWindowOffset(
+ QuicFlowController* flow_controller,
+ uint64 offset) {
+ flow_controller->receive_window_offset_ = offset;
+}
+
+// static
+void QuicFlowControllerPeer::SetMaxReceiveWindow(
+ QuicFlowController* flow_controller, uint64 window_size) {
+ flow_controller->max_receive_window_ = window_size;
+}
+
+// static
+uint64 QuicFlowControllerPeer::SendWindowOffset(
+ QuicFlowController* flow_controller) {
+ return flow_controller->send_window_offset_;
+}
+
+// static
+uint64 QuicFlowControllerPeer::SendWindowSize(
+ QuicFlowController* flow_controller) {
+ return flow_controller->SendWindowSize();
+}
+
+// static
+uint64 QuicFlowControllerPeer::ReceiveWindowOffset(
+ QuicFlowController* flow_controller) {
+ return flow_controller->receive_window_offset_;
+}
+
+// static
+uint64 QuicFlowControllerPeer::ReceiveWindowSize(
+ QuicFlowController* flow_controller) {
+ return flow_controller->receive_window_offset_ -
+ flow_controller->highest_received_byte_offset_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_flow_controller_peer.h b/chromium/net/quic/test_tools/quic_flow_controller_peer.h
new file mode 100644
index 00000000000..213d40d6fa6
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_flow_controller_peer.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicFlowController;
+
+namespace test {
+
+class QuicFlowControllerPeer {
+ public:
+ static void SetSendWindowOffset(QuicFlowController* flow_controller,
+ uint64 offset);
+
+ static void SetReceiveWindowOffset(QuicFlowController* flow_controller,
+ uint64 offset);
+
+ static void SetMaxReceiveWindow(QuicFlowController* flow_controller,
+ uint64 window_size);
+
+ static uint64 SendWindowOffset(QuicFlowController* flow_controller);
+
+ static uint64 SendWindowSize(QuicFlowController* flow_controller);
+
+ static uint64 ReceiveWindowOffset(QuicFlowController* flow_controller);
+
+ static uint64 ReceiveWindowSize(QuicFlowController* flow_controller);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicFlowControllerPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
diff --git a/chromium/net/quic/test_tools/quic_framer_peer.cc b/chromium/net/quic/test_tools/quic_framer_peer.cc
index e8d43cdc10e..c6464cc70f7 100644
--- a/chromium/net/quic/test_tools/quic_framer_peer.cc
+++ b/chromium/net/quic/test_tools/quic_framer_peer.cc
@@ -20,8 +20,9 @@ QuicPacketSequenceNumber QuicFramerPeer::CalculatePacketSequenceNumberFromWire(
}
// static
-void QuicFramerPeer::SetLastSerializedGuid(QuicFramer* framer, QuicGuid guid) {
- framer->last_serialized_guid_ = guid;
+void QuicFramerPeer::SetLastSerializedConnectionId(
+ QuicFramer* framer, QuicConnectionId connection_id) {
+ framer->last_serialized_connection_id_ = connection_id;
}
void QuicFramerPeer::SetLastSequenceNumber(
@@ -34,5 +35,26 @@ void QuicFramerPeer::SetIsServer(QuicFramer* framer, bool is_server) {
framer->is_server_ = is_server;
}
+void QuicFramerPeer::SwapCrypters(QuicFramer* framer1, QuicFramer* framer2) {
+ for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) {
+ framer1->encrypter_[i].swap(framer2->encrypter_[i]);
+ }
+ framer1->decrypter_.swap(framer2->decrypter_);
+ framer1->alternative_decrypter_.swap(framer2->alternative_decrypter_);
+
+ EncryptionLevel framer2_level = framer2->decrypter_level_;
+ framer2->decrypter_level_ = framer1->decrypter_level_;
+ framer1->decrypter_level_ = framer2_level;
+ framer2_level = framer2->alternative_decrypter_level_;
+ framer2->alternative_decrypter_level_ =
+ framer1->alternative_decrypter_level_;
+ framer1->alternative_decrypter_level_ = framer2_level;
+
+ const bool framer2_latch = framer2->alternative_decrypter_latch_;
+ framer2->alternative_decrypter_latch_ =
+ framer1->alternative_decrypter_latch_;
+ framer1->alternative_decrypter_latch_ = framer2_latch;
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_framer_peer.h b/chromium/net/quic/test_tools/quic_framer_peer.h
index acb45ecb173..9b9c9fa140f 100644
--- a/chromium/net/quic/test_tools/quic_framer_peer.h
+++ b/chromium/net/quic/test_tools/quic_framer_peer.h
@@ -19,12 +19,17 @@ class QuicFramerPeer {
QuicFramer* framer,
QuicSequenceNumberLength sequence_number_length,
QuicPacketSequenceNumber packet_sequence_number);
- static void SetLastSerializedGuid(QuicFramer* framer, QuicGuid guid);
+ static void SetLastSerializedConnectionId(QuicFramer* framer,
+ QuicConnectionId connection_id);
static void SetLastSequenceNumber(
QuicFramer* framer,
QuicPacketSequenceNumber packet_sequence_number);
static void SetIsServer(QuicFramer* framer, bool is_server);
+ // SwapCrypters exchanges the state of the crypters of |framer1| with
+ // |framer2|.
+ static void SwapCrypters(QuicFramer* framer1, QuicFramer* framer2);
+
private:
DISALLOW_COPY_AND_ASSIGN(QuicFramerPeer);
};
diff --git a/chromium/net/quic/test_tools/quic_packet_creator_peer.cc b/chromium/net/quic/test_tools/quic_packet_creator_peer.cc
index 1acdf244f2f..a8014479478 100644
--- a/chromium/net/quic/test_tools/quic_packet_creator_peer.cc
+++ b/chromium/net/quic/test_tools/quic_packet_creator_peer.cc
@@ -33,11 +33,5 @@ QuicSequenceNumberLength QuicPacketCreatorPeer::GetSequenceNumberLength(
return creator->sequence_number_length_;
}
-// static
-void QuicPacketCreatorPeer::SetIsServer(QuicPacketCreator* creator,
- bool is_server) {
- creator->is_server_ = is_server;
-}
-
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_packet_creator_peer.h b/chromium/net/quic/test_tools/quic_packet_creator_peer.h
index 12ae676664b..b4bf8a73ba0 100644
--- a/chromium/net/quic/test_tools/quic_packet_creator_peer.h
+++ b/chromium/net/quic/test_tools/quic_packet_creator_peer.h
@@ -24,8 +24,6 @@ class QuicPacketCreatorPeer {
static QuicSequenceNumberLength GetSequenceNumberLength(
QuicPacketCreator* creator);
- static void SetIsServer(QuicPacketCreator* creator, bool is_server);
-
private:
DISALLOW_COPY_AND_ASSIGN(QuicPacketCreatorPeer);
};
diff --git a/chromium/net/quic/test_tools/quic_packet_generator_peer.cc b/chromium/net/quic/test_tools/quic_packet_generator_peer.cc
new file mode 100644
index 00000000000..ad0693a355f
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_packet_generator_peer.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_packet_generator_peer.h"
+
+#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_packet_generator.h"
+
+namespace net {
+namespace test {
+
+// static
+QuicPacketCreator* QuicPacketGeneratorPeer::GetPacketCreator(
+ QuicPacketGenerator* generator) {
+ return &generator->packet_creator_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_packet_generator_peer.h b/chromium/net/quic/test_tools/quic_packet_generator_peer.h
new file mode 100644
index 00000000000..0762d1f6c58
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_packet_generator_peer.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicPacketCreator;
+class QuicPacketGenerator;
+
+namespace test {
+
+class QuicPacketGeneratorPeer {
+ public:
+ static QuicPacketCreator* GetPacketCreator(QuicPacketGenerator* generator);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicPacketGeneratorPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
diff --git a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc
index d25a209b5f4..2faf84e644e 100644
--- a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc
+++ b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc
@@ -11,12 +11,12 @@ namespace net {
namespace test {
// static
-void QuicReceivedPacketManagerPeer::RecalculateEntropyHash(
+void QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo(
QuicReceivedPacketManager* received_packet_manager,
QuicPacketSequenceNumber peer_least_unacked,
QuicPacketEntropyHash entropy_hash) {
- received_packet_manager->RecalculateEntropyHash(peer_least_unacked,
- entropy_hash);
+ received_packet_manager->entropy_tracker_.SetCumulativeEntropyUpTo(
+ peer_least_unacked, entropy_hash);
}
// static
diff --git a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h
index 4607a0c902d..c3cb2d3bc2b 100644
--- a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h
+++ b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h
@@ -15,7 +15,7 @@ namespace test {
class QuicReceivedPacketManagerPeer {
public:
- static void RecalculateEntropyHash(
+ static void SetCumulativeEntropyUpTo(
QuicReceivedPacketManager* received_packet_manager,
QuicPacketSequenceNumber peer_least_unacked,
QuicPacketEntropyHash entropy_hash);
diff --git a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc
index e2e7c02c1a2..786e63c9b47 100644
--- a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc
+++ b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc
@@ -5,6 +5,7 @@
#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
#include "base/stl_util.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_sent_packet_manager.h"
@@ -13,6 +14,12 @@ namespace net {
namespace test {
// static
+void QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager, size_t max_tail_loss_probes) {
+ sent_packet_manager->max_tail_loss_probes_ = max_tail_loss_probes;
+}
+
+// static
void QuicSentPacketManagerPeer::SetSendAlgorithm(
QuicSentPacketManager* sent_packet_manager,
SendAlgorithmInterface* send_algorithm) {
@@ -20,27 +27,52 @@ void QuicSentPacketManagerPeer::SetSendAlgorithm(
}
// static
+const LossDetectionInterface* QuicSentPacketManagerPeer::GetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->loss_algorithm_.get();
+}
+
+// static
+void QuicSentPacketManagerPeer::SetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager,
+ LossDetectionInterface* loss_detector) {
+ sent_packet_manager->loss_algorithm_.reset(loss_detector);
+}
+
+// static
+RttStats* QuicSentPacketManagerPeer::GetRttStats(
+ QuicSentPacketManager* sent_packet_manager) {
+ return &sent_packet_manager->rtt_stats_;
+}
+
+// static
size_t QuicSentPacketManagerPeer::GetNackCount(
const QuicSentPacketManager* sent_packet_manager,
QuicPacketSequenceNumber sequence_number) {
- return sent_packet_manager->packet_history_map_.find(
- sequence_number)->second->nack_count();
+ return sent_packet_manager->unacked_packets_.
+ GetTransmissionInfo(sequence_number).nack_count;
}
// static
-QuicTime QuicSentPacketManagerPeer::GetSentTime(
- const QuicSentPacketManager* sent_packet_manager,
- QuicPacketSequenceNumber sequence_number) {
- DCHECK(ContainsKey(sent_packet_manager->unacked_packets_, sequence_number));
+size_t QuicSentPacketManagerPeer::GetPendingRetransmissionCount(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->pending_retransmissions_.size();
+}
- return sent_packet_manager->unacked_packets_
- .find(sequence_number)->second.sent_time;
+// static
+bool QuicSentPacketManagerPeer::HasPendingPackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.HasInFlightPackets();
}
// static
-QuicTime::Delta QuicSentPacketManagerPeer::rtt(
- QuicSentPacketManager* sent_packet_manager) {
- return sent_packet_manager->rtt_sample_;
+QuicTime QuicSentPacketManagerPeer::GetSentTime(
+ const QuicSentPacketManager* sent_packet_manager,
+ QuicPacketSequenceNumber sequence_number) {
+ DCHECK(sent_packet_manager->unacked_packets_.IsUnacked(sequence_number));
+
+ return sent_packet_manager->unacked_packets_.GetTransmissionInfo(
+ sequence_number).sent_time;
}
// static
@@ -49,8 +81,8 @@ bool QuicSentPacketManagerPeer::IsRetransmission(
QuicPacketSequenceNumber sequence_number) {
DCHECK(sent_packet_manager->HasRetransmittableFrames(sequence_number));
return sent_packet_manager->HasRetransmittableFrames(sequence_number) &&
- sent_packet_manager->
- unacked_packets_[sequence_number].previous_transmissions != NULL;
+ sent_packet_manager->unacked_packets_.GetTransmissionInfo(
+ sequence_number).all_transmissions->size() > 1;
}
// static
@@ -62,5 +94,37 @@ void QuicSentPacketManagerPeer::MarkForRetransmission(
transmission_type);
}
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->GetRetransmissionDelay();
+}
+
+// static
+bool QuicSentPacketManagerPeer::HasUnackedCryptoPackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.HasPendingCryptoPackets();
+}
+
+// static
+size_t QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ size_t num_unacked_packets = 0;
+ for (QuicUnackedPacketMap::const_iterator it =
+ sent_packet_manager->unacked_packets_.begin();
+ it != sent_packet_manager->unacked_packets_.end(); ++it) {
+ if (it->second.retransmittable_frames != NULL) {
+ ++num_unacked_packets;
+ }
+ }
+ return num_unacked_packets;
+}
+
+// static
+QuicByteCount QuicSentPacketManagerPeer::GetBytesInFlight(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.bytes_in_flight();
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h
index 2ed9b218621..fed80153d77 100644
--- a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h
+++ b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h
@@ -16,18 +16,33 @@ namespace test {
class QuicSentPacketManagerPeer {
public:
+ static void SetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager, size_t max_tail_loss_probes);
+
static void SetSendAlgorithm(QuicSentPacketManager* sent_packet_manager,
SendAlgorithmInterface* send_algorithm);
+ static const LossDetectionInterface* GetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static void SetLossAlgorithm(QuicSentPacketManager* sent_packet_manager,
+ LossDetectionInterface* loss_detector);
+
+ static RttStats* GetRttStats(QuicSentPacketManager* sent_packet_manager);
+
static size_t GetNackCount(
const QuicSentPacketManager* sent_packet_manager,
QuicPacketSequenceNumber sequence_number);
+ static size_t GetPendingRetransmissionCount(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static bool HasPendingPackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
static QuicTime GetSentTime(const QuicSentPacketManager* sent_packet_manager,
QuicPacketSequenceNumber sequence_number);
- static QuicTime::Delta rtt(QuicSentPacketManager* sent_packet_manager);
-
// Returns true if |sequence_number| is a retransmission of a packet.
static bool IsRetransmission(QuicSentPacketManager* sent_packet_manager,
QuicPacketSequenceNumber sequence_number);
@@ -36,6 +51,18 @@ class QuicSentPacketManagerPeer {
QuicPacketSequenceNumber sequence_number,
TransmissionType transmission_type);
+ static QuicTime::Delta GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static bool HasUnackedCryptoPackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static size_t GetNumRetransmittablePackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static QuicByteCount GetBytesInFlight(
+ const QuicSentPacketManager* sent_packet_manager);
+
private:
DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManagerPeer);
};
diff --git a/chromium/net/quic/test_tools/quic_session_peer.cc b/chromium/net/quic/test_tools/quic_session_peer.cc
index c25b42fb216..39663fbb7eb 100644
--- a/chromium/net/quic/test_tools/quic_session_peer.cc
+++ b/chromium/net/quic/test_tools/quic_session_peer.cc
@@ -22,10 +22,28 @@ void QuicSessionPeer::SetMaxOpenStreams(QuicSession* session,
}
// static
-WriteBlockedList<QuicStreamId>* QuicSessionPeer::GetWriteblockedStreams(
+QuicHeadersStream* QuicSessionPeer::GetHeadersStream(QuicSession* session) {
+ return session->headers_stream_.get();
+}
+
+// static
+void QuicSessionPeer::SetHeadersStream(QuicSession* session,
+ QuicHeadersStream* headers_stream) {
+ session->headers_stream_.reset(headers_stream);
+}
+
+// static
+QuicWriteBlockedList* QuicSessionPeer::GetWriteBlockedStreams(
QuicSession* session) {
return &session->write_blocked_streams_;
}
+// static
+QuicDataStream* QuicSessionPeer::GetIncomingDataStream(
+ QuicSession* session,
+ QuicStreamId stream_id) {
+ return session->GetIncomingDataStream(stream_id);
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_session_peer.h b/chromium/net/quic/test_tools/quic_session_peer.h
index fb4529cc0ab..77b376c590b 100644
--- a/chromium/net/quic/test_tools/quic_session_peer.h
+++ b/chromium/net/quic/test_tools/quic_session_peer.h
@@ -6,12 +6,13 @@
#define NET_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
#include "net/quic/quic_protocol.h"
-#include "net/spdy/write_blocked_list.h"
+#include "net/quic/quic_write_blocked_list.h"
namespace net {
+class QuicDataStream;
+class QuicHeadersStream;
class QuicSession;
-class ReliableQuicStream;
namespace test {
@@ -19,8 +20,12 @@ class QuicSessionPeer {
public:
static void SetNextStreamId(QuicSession* session, QuicStreamId id);
static void SetMaxOpenStreams(QuicSession* session, uint32 max_streams);
- static WriteBlockedList<QuicStreamId>* GetWriteblockedStreams(
- QuicSession* session);
+ static QuicHeadersStream* GetHeadersStream(QuicSession* session);
+ static void SetHeadersStream(QuicSession* session,
+ QuicHeadersStream* headers_stream);
+ static QuicWriteBlockedList* GetWriteBlockedStreams(QuicSession* session);
+ static QuicDataStream* GetIncomingDataStream(QuicSession* session,
+ QuicStreamId stream_id);
private:
DISALLOW_COPY_AND_ASSIGN(QuicSessionPeer);
diff --git a/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc
new file mode 100644
index 00000000000..08a5c5b0720
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_stream_sequencer_peer.h"
+
+#include "net/quic/quic_stream_sequencer.h"
+
+using std::map;
+using std::string;
+
+namespace net {
+namespace test {
+
+// static
+map<QuicStreamOffset, string>* QuicStreamSequencerPeer::GetBufferedFrames(
+ QuicStreamSequencer* sequencer) {
+ return &(sequencer->buffered_frames_);
+}
+
+// static
+QuicStreamOffset QuicStreamSequencerPeer::GetCloseOffset(
+ QuicStreamSequencer* sequencer) {
+ return sequencer->close_offset_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h
new file mode 100644
index 00000000000..7b2b28aad46
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicStreamSequencer;
+
+namespace test {
+
+class QuicStreamSequencerPeer {
+ public:
+ static std::map<QuicStreamOffset, std::string>* GetBufferedFrames(
+ QuicStreamSequencer* sequencer);
+
+ static QuicStreamOffset GetCloseOffset(QuicStreamSequencer* sequencer);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicStreamSequencerPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
diff --git a/chromium/net/quic/test_tools/quic_test_packet_maker.cc b/chromium/net/quic/test_tools/quic_test_packet_maker.cc
new file mode 100644
index 00000000000..7f0835f8bdf
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_test_packet_maker.cc
@@ -0,0 +1,249 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_test_packet_maker.h"
+
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_http_utils.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
+namespace net {
+namespace test {
+
+QuicTestPacketMaker::QuicTestPacketMaker(QuicVersion version,
+ QuicConnectionId connection_id)
+ : version_(version),
+ connection_id_(connection_id),
+ spdy_request_framer_(SPDY3),
+ spdy_response_framer_(SPDY3) {
+}
+
+QuicTestPacketMaker::~QuicTestPacketMaker() {
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = include_version;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = num;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicRstStreamFrame rst(stream_id, error_code, 0);
+ return scoped_ptr<QuicEncryptedPacket>(MakePacket(header, QuicFrame(&rst)));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckAndRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback) {
+
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = include_version;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = num;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicAckFrame ack(MakeAckFrame(largest_received, least_unacked));
+ ack.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack));
+ QuicCongestionFeedbackFrame feedback;
+ if (send_feedback) {
+ feedback.type = kTCP;
+ feedback.tcp.receive_window = 256000;
+
+ frames.push_back(QuicFrame(&feedback));
+ }
+
+ QuicStopWaitingFrame stop_waiting;
+ if (version_ > QUIC_VERSION_15) {
+ stop_waiting.least_unacked = least_unacked;
+ frames.push_back(QuicFrame(&stop_waiting));
+ }
+
+ QuicRstStreamFrame rst(stream_id, error_code, 0);
+ frames.push_back(QuicFrame(&rst));
+
+ QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), false);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
+ ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeConnectionClosePacket(
+ QuicPacketSequenceNumber num) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = num;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicConnectionCloseFrame close;
+ close.error_code = QUIC_CRYPTO_VERSION_NOT_SUPPORTED;
+ close.error_details = "Time to panic!";
+ return scoped_ptr<QuicEncryptedPacket>(MakePacket(header, QuicFrame(&close)));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = sequence_number;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicAckFrame ack(MakeAckFrame(largest_received, least_unacked));
+ ack.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+
+ QuicCongestionFeedbackFrame feedback;
+ feedback.type = kTCP;
+ feedback.tcp.receive_window = 256000;
+
+ QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), false);
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack));
+ if (send_feedback) {
+ frames.push_back(QuicFrame(&feedback));
+ }
+
+ QuicStopWaitingFrame stop_waiting;
+ if (version_ > QUIC_VERSION_15) {
+ stop_waiting.least_unacked = least_unacked;
+ frames.push_back(QuicFrame(&stop_waiting));
+ }
+
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
+ ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+}
+
+// Returns a newly created packet to send kData on stream 1.
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeDataPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ QuicStreamOffset offset,
+ base::StringPiece data) {
+ InitializeHeader(sequence_number, should_include_version);
+ QuicStreamFrame frame(stream_id, fin, offset, MakeIOVector(data));
+ return MakePacket(header_, QuicFrame(&frame));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ InitializeHeader(sequence_number, should_include_version);
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_name_value_block(headers);
+ syn_stream.set_fin(fin);
+ syn_stream.set_priority(0);
+ scoped_ptr<SpdySerializedFrame> spdy_frame(
+ spdy_request_framer_.SerializeSynStream(syn_stream));
+ QuicStreamFrame frame(kHeadersStreamId, false, 0,
+ MakeIOVector(base::StringPiece(spdy_frame->data(),
+ spdy_frame->size())));
+ return MakePacket(header_, QuicFrame(&frame));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeResponseHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ InitializeHeader(sequence_number, should_include_version);
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.set_name_value_block(headers);
+ syn_reply.set_fin(fin);
+ scoped_ptr<SpdySerializedFrame> spdy_frame(
+ spdy_response_framer_.SerializeSynReply(syn_reply));
+ QuicStreamFrame frame(kHeadersStreamId, false, 0,
+ MakeIOVector(base::StringPiece(spdy_frame->data(),
+ spdy_frame->size())));
+ return MakePacket(header_, QuicFrame(&frame));
+}
+
+SpdyHeaderBlock QuicTestPacketMaker::GetRequestHeaders(
+ const std::string& method,
+ const std::string& scheme,
+ const std::string& path) {
+ SpdyHeaderBlock headers;
+ headers[":method"] = method;
+ headers[":host"] = "www.google.com";
+ headers[":path"] = path;
+ headers[":scheme"] = scheme;
+ headers[":version"] = "HTTP/1.1";
+ return headers;
+}
+
+SpdyHeaderBlock QuicTestPacketMaker::GetResponseHeaders(
+ const std::string& status) {
+ SpdyHeaderBlock headers;
+ headers[":status"] = status;
+ headers[":version"] = "HTTP/1.1";
+ headers["content-type"] = "text/plain";
+ return headers;
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakePacket(
+ const QuicPacketHeader& header,
+ const QuicFrame& frame) {
+ QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), false);
+ QuicFrames frames;
+ frames.push_back(frame);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
+ ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+}
+
+void QuicTestPacketMaker::InitializeHeader(
+ QuicPacketSequenceNumber sequence_number,
+ bool should_include_version) {
+ header_.public_header.connection_id = connection_id_;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = should_include_version;
+ header_.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header_.packet_sequence_number = sequence_number;
+ header_.fec_group = 0;
+ header_.entropy_flag = false;
+ header_.fec_flag = false;
+}
+
+} // namespace test
+} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_test_packet_maker.h b/chromium/net/quic/test_tools/quic_test_packet_maker.h
new file mode 100644
index 00000000000..e4f3ec03300
--- /dev/null
+++ b/chromium/net/quic/test_tools/quic_test_packet_maker.h
@@ -0,0 +1,91 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Provides a simple interface for QUIC tests to create a variety of packets.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/request_priority.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+
+namespace net {
+namespace test {
+
+class QuicTestPacketMaker {
+ public:
+ QuicTestPacketMaker(QuicVersion version, QuicConnectionId connection_id);
+ ~QuicTestPacketMaker();
+
+ scoped_ptr<QuicEncryptedPacket> MakeRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code);
+ scoped_ptr<QuicEncryptedPacket> MakeAckAndRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback);
+ scoped_ptr<QuicEncryptedPacket> MakeConnectionClosePacket(
+ QuicPacketSequenceNumber num);
+ scoped_ptr<QuicEncryptedPacket> MakeAckPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback);
+ scoped_ptr<QuicEncryptedPacket> MakeDataPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ QuicStreamOffset offset,
+ base::StringPiece data);
+ scoped_ptr<QuicEncryptedPacket> MakeRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers);
+ scoped_ptr<QuicEncryptedPacket> MakeResponseHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers);
+
+ SpdyHeaderBlock GetRequestHeaders(const std::string& method,
+ const std::string& scheme,
+ const std::string& path);
+ SpdyHeaderBlock GetResponseHeaders(const std::string& status);
+
+ private:
+ scoped_ptr<QuicEncryptedPacket> MakePacket(
+ const QuicPacketHeader& header,
+ const QuicFrame& frame);
+
+ void InitializeHeader(QuicPacketSequenceNumber sequence_number,
+ bool should_include_version);
+
+ QuicVersion version_;
+ QuicConnectionId connection_id_;
+ SpdyFramer spdy_request_framer_;
+ SpdyFramer spdy_response_framer_;
+ MockRandom random_generator_;
+ QuicPacketHeader header_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicTestPacketMaker);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_
diff --git a/chromium/net/quic/test_tools/quic_test_utils.cc b/chromium/net/quic/test_tools/quic_test_utils.cc
index d85f92fa07e..b6a0744379a 100644
--- a/chromium/net/quic/test_tools/quic_test_utils.cc
+++ b/chromium/net/quic/test_tools/quic_test_utils.cc
@@ -4,6 +4,7 @@
#include "net/quic/test_tools/quic_test_utils.h"
+#include "base/sha1.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "net/quic/crypto/crypto_framer.h"
@@ -14,6 +15,7 @@
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/spdy/spdy_frame_builder.h"
@@ -21,6 +23,7 @@ using base::StringPiece;
using std::max;
using std::min;
using std::string;
+using testing::AnyNumber;
using testing::_;
namespace net {
@@ -40,6 +43,54 @@ class TestAlarm : public QuicAlarm {
} // namespace
+QuicAckFrame MakeAckFrame(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber least_unacked) {
+ QuicAckFrame ack;
+ ack.received_info.largest_observed = largest_observed;
+ ack.received_info.entropy_hash = 0;
+ ack.sent_info.least_unacked = least_unacked;
+ ack.sent_info.entropy_hash = 0;
+ return ack;
+}
+
+QuicAckFrame MakeAckFrameWithNackRanges(
+ size_t num_nack_ranges, QuicPacketSequenceNumber least_unacked) {
+ QuicAckFrame ack = MakeAckFrame(2 * num_nack_ranges + least_unacked,
+ least_unacked);
+ // Add enough missing packets to get num_nack_ranges nack ranges.
+ for (QuicPacketSequenceNumber i = 1; i < 2 * num_nack_ranges; i += 2) {
+ ack.received_info.missing_packets.insert(least_unacked + i);
+ }
+ return ack;
+}
+
+SerializedPacket BuildUnsizedDataPacket(QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames) {
+ const size_t max_plaintext_size = framer->GetMaxPlaintextSize(kMaxPacketSize);
+ size_t packet_size = GetPacketHeaderSize(header);
+ for (size_t i = 0; i < frames.size(); ++i) {
+ DCHECK_LE(packet_size, max_plaintext_size);
+ bool first_frame = i == 0;
+ bool last_frame = i == frames.size() - 1;
+ const size_t frame_size = framer->GetSerializedFrameLength(
+ frames[i], max_plaintext_size - packet_size, first_frame, last_frame,
+ header.is_in_fec_group,
+ header.public_header.sequence_number_length);
+ DCHECK(frame_size);
+ packet_size += frame_size;
+ }
+ return framer->BuildDataPacket(header, frames, packet_size);
+}
+
+uint64 SimpleRandom::RandUint64() {
+ unsigned char hash[base::kSHA1Length];
+ base::SHA1HashBytes(reinterpret_cast<unsigned char*>(&seed_), sizeof(seed_),
+ hash);
+ memcpy(&seed_, hash, sizeof(seed_));
+ return seed_;
+}
+
MockFramerVisitor::MockFramerVisitor() {
// By default, we want to accept packets.
ON_CALL(*this, OnProtocolVersionMismatch(_))
@@ -49,6 +100,9 @@ MockFramerVisitor::MockFramerVisitor() {
ON_CALL(*this, OnUnauthenticatedHeader(_))
.WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnUnauthenticatedPublicHeader(_))
+ .WillByDefault(testing::Return(true));
+
ON_CALL(*this, OnPacketHeader(_))
.WillByDefault(testing::Return(true));
@@ -61,6 +115,12 @@ MockFramerVisitor::MockFramerVisitor() {
ON_CALL(*this, OnCongestionFeedbackFrame(_))
.WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnStopWaitingFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPingFrame(_))
+ .WillByDefault(testing::Return(true));
+
ON_CALL(*this, OnRstStreamFrame(_))
.WillByDefault(testing::Return(true));
@@ -78,6 +138,11 @@ bool NoOpFramerVisitor::OnProtocolVersionMismatch(QuicVersion version) {
return false;
}
+bool NoOpFramerVisitor::OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) {
+ return true;
+}
+
bool NoOpFramerVisitor::OnUnauthenticatedHeader(
const QuicPacketHeader& header) {
return true;
@@ -100,107 +165,36 @@ bool NoOpFramerVisitor::OnCongestionFeedbackFrame(
return true;
}
-bool NoOpFramerVisitor::OnRstStreamFrame(
- const QuicRstStreamFrame& frame) {
- return true;
-}
-
-bool NoOpFramerVisitor::OnConnectionCloseFrame(
- const QuicConnectionCloseFrame& frame) {
+bool NoOpFramerVisitor::OnStopWaitingFrame(
+ const QuicStopWaitingFrame& frame) {
return true;
}
-bool NoOpFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+bool NoOpFramerVisitor::OnPingFrame(const QuicPingFrame& frame) {
return true;
}
-FramerVisitorCapturingFrames::FramerVisitorCapturingFrames() : frame_count_(0) {
-}
-
-FramerVisitorCapturingFrames::~FramerVisitorCapturingFrames() {
- Reset();
-}
-
-void FramerVisitorCapturingFrames::Reset() {
- STLDeleteElements(&stream_data_);
- stream_frames_.clear();
- frame_count_ = 0;
- ack_.reset();
- feedback_.reset();
- rst_.reset();
- close_.reset();
- goaway_.reset();
- version_negotiation_packet_.reset();
-}
-
-bool FramerVisitorCapturingFrames::OnPacketHeader(
- const QuicPacketHeader& header) {
- header_ = header;
- frame_count_ = 0;
- return true;
-}
-
-bool FramerVisitorCapturingFrames::OnStreamFrame(const QuicStreamFrame& frame) {
- // Make a copy of the frame and store a copy of underlying string, since
- // frame.data may not exist outside this callback.
- stream_data_.push_back(frame.GetDataAsString());
- QuicStreamFrame frame_copy = frame;
- frame_copy.data.Clear();
- frame_copy.data.Append(const_cast<char*>(stream_data_.back()->data()),
- stream_data_.back()->size());
- stream_frames_.push_back(frame_copy);
- ++frame_count_;
- return true;
-}
-
-bool FramerVisitorCapturingFrames::OnAckFrame(const QuicAckFrame& frame) {
- ack_.reset(new QuicAckFrame(frame));
- ++frame_count_;
- return true;
-}
-
-bool FramerVisitorCapturingFrames::OnCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& frame) {
- feedback_.reset(new QuicCongestionFeedbackFrame(frame));
- ++frame_count_;
- return true;
-}
-
-bool FramerVisitorCapturingFrames::OnRstStreamFrame(
+bool NoOpFramerVisitor::OnRstStreamFrame(
const QuicRstStreamFrame& frame) {
- rst_.reset(new QuicRstStreamFrame(frame));
- ++frame_count_;
return true;
}
-bool FramerVisitorCapturingFrames::OnConnectionCloseFrame(
+bool NoOpFramerVisitor::OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) {
- close_.reset(new QuicConnectionCloseFrame(frame));
- ++frame_count_;
return true;
}
-bool FramerVisitorCapturingFrames::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
- goaway_.reset(new QuicGoAwayFrame(frame));
- ++frame_count_;
+bool NoOpFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
return true;
}
-void FramerVisitorCapturingFrames::OnVersionNegotiationPacket(
- const QuicVersionNegotiationPacket& packet) {
- version_negotiation_packet_.reset(new QuicVersionNegotiationPacket(packet));
- frame_count_ = 0;
-}
-
-FramerVisitorCapturingPublicReset::FramerVisitorCapturingPublicReset() {
-}
-
-FramerVisitorCapturingPublicReset::~FramerVisitorCapturingPublicReset() {
+bool NoOpFramerVisitor::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ return true;
}
-void FramerVisitorCapturingPublicReset::OnPublicResetPacket(
- const QuicPublicResetPacket& public_reset) {
- public_reset_packet_ = public_reset;
+bool NoOpFramerVisitor::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ return true;
}
MockConnectionVisitor::MockConnectionVisitor() {
@@ -232,8 +226,8 @@ void MockHelper::AdvanceTime(QuicTime::Delta delta) {
}
MockConnection::MockConnection(bool is_server)
- : QuicConnection(kTestGuid,
- IPEndPoint(Loopback4(), kTestPort),
+ : QuicConnection(kTestConnectionId,
+ IPEndPoint(TestPeerIPAddress(), kTestPort),
new testing::NiceMock<MockHelper>(),
new testing::NiceMock<MockPacketWriter>(),
is_server, QuicSupportedVersions()),
@@ -243,7 +237,7 @@ MockConnection::MockConnection(bool is_server)
MockConnection::MockConnection(IPEndPoint address,
bool is_server)
- : QuicConnection(kTestGuid, address,
+ : QuicConnection(kTestConnectionId, address,
new testing::NiceMock<MockHelper>(),
new testing::NiceMock<MockPacketWriter>(),
is_server, QuicSupportedVersions()),
@@ -251,10 +245,10 @@ MockConnection::MockConnection(IPEndPoint address,
helper_(helper()) {
}
-MockConnection::MockConnection(QuicGuid guid,
+MockConnection::MockConnection(QuicConnectionId connection_id,
bool is_server)
- : QuicConnection(guid,
- IPEndPoint(Loopback4(), kTestPort),
+ : QuicConnection(connection_id,
+ IPEndPoint(TestPeerIPAddress(), kTestPort),
new testing::NiceMock<MockHelper>(),
new testing::NiceMock<MockPacketWriter>(),
is_server, QuicSupportedVersions()),
@@ -262,6 +256,17 @@ MockConnection::MockConnection(QuicGuid guid,
helper_(helper()) {
}
+MockConnection::MockConnection(bool is_server,
+ const QuicVersionVector& supported_versions)
+ : QuicConnection(kTestConnectionId,
+ IPEndPoint(TestPeerIPAddress(), kTestPort),
+ new testing::NiceMock<MockHelper>(),
+ new testing::NiceMock<MockPacketWriter>(),
+ is_server, supported_versions),
+ writer_(QuicConnectionPeer::GetWriter(this)),
+ helper_(helper()) {
+}
+
MockConnection::~MockConnection() {
}
@@ -273,6 +278,12 @@ PacketSavingConnection::PacketSavingConnection(bool is_server)
: MockConnection(is_server) {
}
+PacketSavingConnection::PacketSavingConnection(
+ bool is_server,
+ const QuicVersionVector& supported_versions)
+ : MockConnection(is_server, supported_versions) {
+}
+
PacketSavingConnection::~PacketSavingConnection() {
STLDeleteElements(&packets_);
STLDeleteElements(&encrypted_packets_);
@@ -283,8 +294,8 @@ bool PacketSavingConnection::SendOrQueuePacket(
const SerializedPacket& packet,
TransmissionType transmission_type) {
packets_.push_back(packet.packet);
- QuicEncryptedPacket* encrypted =
- framer_.EncryptPacket(level, packet.sequence_number, *packet.packet);
+ QuicEncryptedPacket* encrypted = QuicConnectionPeer::GetFramer(this)->
+ EncryptPacket(level, packet.sequence_number, *packet.packet);
encrypted_packets_.push_back(encrypted);
return true;
}
@@ -298,11 +309,9 @@ MockSession::MockSession(QuicConnection* connection)
MockSession::~MockSession() {
}
-TestSession::TestSession(QuicConnection* connection,
- const QuicConfig& config)
+TestSession::TestSession(QuicConnection* connection, const QuicConfig& config)
: QuicSession(connection, config),
- crypto_stream_(NULL) {
-}
+ crypto_stream_(NULL) {}
TestSession::~TestSession() {}
@@ -314,6 +323,24 @@ QuicCryptoStream* TestSession::GetCryptoStream() {
return crypto_stream_;
}
+TestClientSession::TestClientSession(QuicConnection* connection,
+ const QuicConfig& config)
+ : QuicClientSessionBase(connection,
+ config),
+ crypto_stream_(NULL) {
+ EXPECT_CALL(*this, OnProofValid(_)).Times(AnyNumber());
+}
+
+TestClientSession::~TestClientSession() {}
+
+void TestClientSession::SetCryptoStream(QuicCryptoStream* stream) {
+ crypto_stream_ = stream;
+}
+
+QuicCryptoStream* TestClientSession::GetCryptoStream() {
+ return crypto_stream_;
+}
+
MockPacketWriter::MockPacketWriter() {
}
@@ -326,6 +353,12 @@ MockSendAlgorithm::MockSendAlgorithm() {
MockSendAlgorithm::~MockSendAlgorithm() {
}
+MockLossAlgorithm::MockLossAlgorithm() {
+}
+
+MockLossAlgorithm::~MockLossAlgorithm() {
+}
+
MockAckNotifierDelegate::MockAckNotifierDelegate() {
}
@@ -373,16 +406,65 @@ string HexDumpWithMarks(const char* data, int length,
} // namespace
+IPAddressNumber TestPeerIPAddress() { return Loopback4(); }
+
QuicVersion QuicVersionMax() { return QuicSupportedVersions().front(); }
QuicVersion QuicVersionMin() { return QuicSupportedVersions().back(); }
IPAddressNumber Loopback4() {
- net::IPAddressNumber addr;
- CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &addr));
+ IPAddressNumber addr;
+ CHECK(ParseIPLiteralToNumber("127.0.0.1", &addr));
+ return addr;
+}
+
+IPAddressNumber Loopback6() {
+ IPAddressNumber addr;
+ CHECK(ParseIPLiteralToNumber("::1", &addr));
return addr;
}
+void GenerateBody(string* body, int length) {
+ body->clear();
+ body->reserve(length);
+ for (int i = 0; i < length; ++i) {
+ body->append(1, static_cast<char>(32 + i % (126 - 32)));
+ }
+}
+
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId connection_id,
+ bool version_flag,
+ bool reset_flag,
+ QuicPacketSequenceNumber sequence_number,
+ const string& data) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id;
+ header.public_header.connection_id_length = PACKET_8BYTE_CONNECTION_ID;
+ header.public_header.version_flag = version_flag;
+ header.public_header.reset_flag = reset_flag;
+ header.public_header.sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = sequence_number;
+ header.entropy_flag = false;
+ header.entropy_hash = 0;
+ header.fec_flag = false;
+ header.is_in_fec_group = NOT_IN_FEC_GROUP;
+ header.fec_group = 0;
+ QuicStreamFrame stream_frame(1, false, 0, MakeIOVector(data));
+ QuicFrame frame(&stream_frame);
+ QuicFrames frames;
+ frames.push_back(frame);
+ QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ EXPECT_TRUE(packet != NULL);
+ QuicEncryptedPacket* encrypted = framer.EncryptPacket(ENCRYPTION_NONE,
+ sequence_number,
+ *packet);
+ EXPECT_TRUE(encrypted != NULL);
+ return encrypted;
+}
+
void CompareCharArraysWithHexError(
const string& description,
const char* actual,
@@ -428,7 +510,7 @@ bool DecodeHexString(const base::StringPiece& hex, std::string* bytes) {
}
static QuicPacket* ConstructPacketFromHandshakeMessage(
- QuicGuid guid,
+ QuicConnectionId connection_id,
const CryptoHandshakeMessage& message,
bool should_include_version) {
CryptoFramer crypto_framer;
@@ -436,7 +518,7 @@ static QuicPacket* ConstructPacketFromHandshakeMessage(
QuicFramer quic_framer(QuicSupportedVersions(), QuicTime::Zero(), false);
QuicPacketHeader header;
- header.public_header.guid = guid;
+ header.public_header.connection_id = connection_id;
header.public_header.reset_flag = false;
header.public_header.version_flag = should_include_version;
header.packet_sequence_number = 1;
@@ -451,13 +533,14 @@ static QuicPacket* ConstructPacketFromHandshakeMessage(
QuicFrame frame(&stream_frame);
QuicFrames frames;
frames.push_back(frame);
- return quic_framer.BuildUnsizedDataPacket(header, frames).packet;
+ return BuildUnsizedDataPacket(&quic_framer, header, frames).packet;
}
-QuicPacket* ConstructHandshakePacket(QuicGuid guid, QuicTag tag) {
+QuicPacket* ConstructHandshakePacket(QuicConnectionId connection_id,
+ QuicTag tag) {
CryptoHandshakeMessage message;
message.set_tag(tag);
- return ConstructPacketFromHandshakeMessage(guid, message, false);
+ return ConstructPacketFromHandshakeMessage(connection_id, message, false);
}
size_t GetPacketLengthForOneStream(
@@ -470,12 +553,12 @@ size_t GetPacketLengthForOneStream(
const size_t stream_length =
NullEncrypter().GetCiphertextSize(*payload_length) +
QuicPacketCreator::StreamFramePacketOverhead(
- version, PACKET_8BYTE_GUID, include_version,
- sequence_number_length, is_in_fec_group);
+ version, PACKET_8BYTE_CONNECTION_ID, include_version,
+ sequence_number_length, 0u, is_in_fec_group);
const size_t ack_length = NullEncrypter().GetCiphertextSize(
QuicFramer::GetMinAckFrameSize(
version, sequence_number_length, PACKET_1BYTE_SEQUENCE_NUMBER)) +
- GetPacketHeaderSize(PACKET_8BYTE_GUID, include_version,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, include_version,
sequence_number_length, is_in_fec_group);
if (stream_length < ack_length) {
*payload_length = 1 + ack_length - stream_length;
@@ -483,42 +566,39 @@ size_t GetPacketLengthForOneStream(
return NullEncrypter().GetCiphertextSize(*payload_length) +
QuicPacketCreator::StreamFramePacketOverhead(
- version, PACKET_8BYTE_GUID, include_version,
- sequence_number_length, is_in_fec_group);
-}
-
-// Size in bytes of the stream frame fields for an arbitrary StreamID and
-// offset and the last frame in a packet.
-size_t GetMinStreamFrameSize(QuicVersion version) {
- return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
+ version, PACKET_8BYTE_CONNECTION_ID, include_version,
+ sequence_number_length, 0u, is_in_fec_group);
}
-TestEntropyCalculator::TestEntropyCalculator() { }
+TestEntropyCalculator::TestEntropyCalculator() {}
-TestEntropyCalculator::~TestEntropyCalculator() { }
+TestEntropyCalculator::~TestEntropyCalculator() {}
QuicPacketEntropyHash TestEntropyCalculator::EntropyHash(
QuicPacketSequenceNumber sequence_number) const {
return 1u;
}
-MockEntropyCalculator::MockEntropyCalculator() { }
+MockEntropyCalculator::MockEntropyCalculator() {}
-MockEntropyCalculator::~MockEntropyCalculator() { }
+MockEntropyCalculator::~MockEntropyCalculator() {}
QuicConfig DefaultQuicConfig() {
QuicConfig config;
config.SetDefaults();
+ config.SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ config.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
return config;
}
-bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) {
- data.AppendToString(&data_);
- return true;
-}
-
-void TestDecompressorVisitor::OnDecompressionError() {
- error_ = true;
+QuicVersionVector SupportedVersions(QuicVersion version) {
+ QuicVersionVector versions;
+ versions.push_back(version);
+ return versions;
}
} // namespace test
diff --git a/chromium/net/quic/test_tools/quic_test_utils.h b/chromium/net/quic/test_tools/quic_test_utils.h
index 06c343189b5..574e7d15c67 100644
--- a/chromium/net/quic/test_tools/quic_test_utils.h
+++ b/chromium/net/quic/test_tools/quic_test_utils.h
@@ -11,12 +11,13 @@
#include <vector>
#include "base/strings/string_piece.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_client_session_base.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_session.h"
-#include "net/quic/quic_spdy_decompressor.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_random.h"
#include "net/spdy/spdy_framer.h"
@@ -26,8 +27,21 @@ namespace net {
namespace test {
-static const QuicGuid kTestGuid = 42;
+static const QuicConnectionId kTestConnectionId = 42;
static const int kTestPort = 123;
+static const uint32 kInitialStreamFlowControlWindowForTest =
+ 32 * 1024; // 32 KB
+static const uint32 kInitialSessionFlowControlWindowForTest =
+ 64 * 1024; // 64 KB
+
+// Data stream IDs start at 5: the crypto stream is 1, headers stream is 3.
+static const QuicStreamId kClientDataStreamId1 = 5;
+static const QuicStreamId kClientDataStreamId2 = 7;
+static const QuicStreamId kClientDataStreamId3 = 9;
+static const QuicStreamId kClientDataStreamId4 = 11;
+
+// Returns the test peer IP address.
+IPAddressNumber TestPeerIPAddress();
// Upper limit on versions we support.
QuicVersion QuicVersionMax();
@@ -38,6 +52,19 @@ QuicVersion QuicVersionMin();
// Returns an address for 127.0.0.1.
IPAddressNumber Loopback4();
+// Returns an address for ::1.
+IPAddressNumber Loopback6();
+
+void GenerateBody(std::string* body, int length);
+
+// Create an encrypted packet for testing.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId connection_id,
+ bool version_flag,
+ bool reset_flag,
+ QuicPacketSequenceNumber sequence_number,
+ const std::string& data);
+
void CompareCharArraysWithHexError(const std::string& description,
const char* actual,
const int actual_len,
@@ -56,13 +83,29 @@ size_t GetPacketLengthForOneStream(
InFecGroup is_in_fec_group,
size_t* payload_length);
-// Size in bytes of the stream frame fields for an arbitrary StreamID and
-// offset and the last frame in a packet.
-size_t GetMinStreamFrameSize(QuicVersion version);
-
// Returns QuicConfig set to default values.
QuicConfig DefaultQuicConfig();
+// Returns a version vector consisting of |version|.
+QuicVersionVector SupportedVersions(QuicVersion version);
+
+// Testing convenience method to construct a QuicAckFrame with all packets
+// from least_unacked to largest_observed acked.
+QuicAckFrame MakeAckFrame(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber least_unacked);
+
+// Testing convenience method to construct a QuicAckFrame with |num_nack_ranges|
+// nack ranges of width 1 packet, starting from |least_unacked|.
+QuicAckFrame MakeAckFrameWithNackRanges(size_t num_nack_ranges,
+ QuicPacketSequenceNumber least_unacked);
+
+// Returns a SerializedPacket whose |packet| member is owned by the caller, and
+// is populated with the fields in |header| and |frames|, or is NULL if the
+// packet could not be created.
+SerializedPacket BuildUnsizedDataPacket(QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames);
+
template<typename SaveType>
class ValueRestore {
public:
@@ -82,10 +125,29 @@ class ValueRestore {
DISALLOW_COPY_AND_ASSIGN(ValueRestore);
};
+// Simple random number generator used to compute random numbers suitable
+// for pseudo-randomly dropping packets in tests. It works by computing
+// the sha1 hash of the current seed, and using the first 64 bits as
+// the next random number, and the next seed.
+class SimpleRandom {
+ public:
+ SimpleRandom() : seed_(0) {}
+
+ // Returns a random number in the range [0, kuint64max].
+ uint64 RandUint64();
+
+ void set_seed(uint64 seed) { seed_ = seed; }
+
+ private:
+ uint64 seed_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleRandom);
+};
+
class MockFramerVisitor : public QuicFramerVisitorInterface {
public:
MockFramerVisitor();
- ~MockFramerVisitor();
+ virtual ~MockFramerVisitor();
MOCK_METHOD1(OnError, void(QuicFramer* framer));
// The constructor sets this up to return false by default.
@@ -97,17 +159,25 @@ class MockFramerVisitor : public QuicFramerVisitorInterface {
MOCK_METHOD0(OnRevivedPacket, void());
// The constructor sets this up to return true by default.
MOCK_METHOD1(OnUnauthenticatedHeader, bool(const QuicPacketHeader& header));
+ // The constructor sets this up to return true by default.
+ MOCK_METHOD1(OnUnauthenticatedPublicHeader, bool(
+ const QuicPacketPublicHeader& header));
+ MOCK_METHOD1(OnDecryptedPacket, void(EncryptionLevel level));
MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header));
MOCK_METHOD1(OnFecProtectedPayload, void(base::StringPiece payload));
MOCK_METHOD1(OnStreamFrame, bool(const QuicStreamFrame& frame));
MOCK_METHOD1(OnAckFrame, bool(const QuicAckFrame& frame));
MOCK_METHOD1(OnCongestionFeedbackFrame,
bool(const QuicCongestionFeedbackFrame& frame));
+ MOCK_METHOD1(OnStopWaitingFrame, bool(const QuicStopWaitingFrame& frame));
+ MOCK_METHOD1(OnPingFrame, bool(const QuicPingFrame& frame));
MOCK_METHOD1(OnFecData, void(const QuicFecData& fec));
MOCK_METHOD1(OnRstStreamFrame, bool(const QuicRstStreamFrame& frame));
MOCK_METHOD1(OnConnectionCloseFrame,
bool(const QuicConnectionCloseFrame& frame));
MOCK_METHOD1(OnGoAwayFrame, bool(const QuicGoAwayFrame& frame));
+ MOCK_METHOD1(OnWindowUpdateFrame, bool(const QuicWindowUpdateFrame& frame));
+ MOCK_METHOD1(OnBlockedFrame, bool(const QuicBlockedFrame& frame));
MOCK_METHOD0(OnPacketComplete, void());
private:
@@ -127,103 +197,49 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface {
virtual void OnRevivedPacket() OVERRIDE {}
virtual bool OnProtocolVersionMismatch(QuicVersion version) OVERRIDE;
virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE;
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {}
virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE {}
virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
virtual bool OnCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual bool OnStopWaitingFrame(
+ const QuicStopWaitingFrame& frame) OVERRIDE;
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE;
virtual void OnFecData(const QuicFecData& fec) OVERRIDE {}
virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
virtual bool OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) OVERRIDE;
virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE;
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE;
virtual void OnPacketComplete() OVERRIDE {}
private:
DISALLOW_COPY_AND_ASSIGN(NoOpFramerVisitor);
};
-class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor {
- public:
- FramerVisitorCapturingPublicReset();
- virtual ~FramerVisitorCapturingPublicReset();
-
- virtual void OnPublicResetPacket(
- const QuicPublicResetPacket& packet) OVERRIDE;
-
- const QuicPublicResetPacket public_reset_packet() {
- return public_reset_packet_;
- }
-
- private:
- QuicPublicResetPacket public_reset_packet_;
-};
-
-class FramerVisitorCapturingFrames : public NoOpFramerVisitor {
- public:
- FramerVisitorCapturingFrames();
- virtual ~FramerVisitorCapturingFrames();
-
- // Reset the visitor to it's initial state.
- void Reset();
-
- // NoOpFramerVisitor
- virtual void OnVersionNegotiationPacket(
- const QuicVersionNegotiationPacket& packet) OVERRIDE;
- virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
- virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
- virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
- virtual bool OnCongestionFeedbackFrame(
- const QuicCongestionFeedbackFrame& frame) OVERRIDE;
- virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
- virtual bool OnConnectionCloseFrame(
- const QuicConnectionCloseFrame& frame) OVERRIDE;
- virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
-
- size_t frame_count() const { return frame_count_; }
- QuicPacketHeader* header() { return &header_; }
- const std::vector<QuicStreamFrame>* stream_frames() const {
- return &stream_frames_;
- }
- const std::vector<string*>& stream_data() const {
- return stream_data_;
- }
- QuicAckFrame* ack() { return ack_.get(); }
- QuicCongestionFeedbackFrame* feedback() { return feedback_.get(); }
- QuicRstStreamFrame* rst() { return rst_.get(); }
- QuicConnectionCloseFrame* close() { return close_.get(); }
- QuicGoAwayFrame* goaway() { return goaway_.get(); }
- QuicVersionNegotiationPacket* version_negotiation_packet() {
- return version_negotiation_packet_.get();
- }
-
- private:
- size_t frame_count_;
- QuicPacketHeader header_;
- std::vector<QuicStreamFrame> stream_frames_;
- std::vector<std::string*> stream_data_;
- scoped_ptr<QuicAckFrame> ack_;
- scoped_ptr<QuicCongestionFeedbackFrame> feedback_;
- scoped_ptr<QuicRstStreamFrame> rst_;
- scoped_ptr<QuicConnectionCloseFrame> close_;
- scoped_ptr<QuicGoAwayFrame> goaway_;
- scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
-
- DISALLOW_COPY_AND_ASSIGN(FramerVisitorCapturingFrames);
-};
-
class MockConnectionVisitor : public QuicConnectionVisitorInterface {
public:
MockConnectionVisitor();
virtual ~MockConnectionVisitor();
- MOCK_METHOD1(OnStreamFrames, bool(const std::vector<QuicStreamFrame>& frame));
+ MOCK_METHOD1(OnStreamFrames, void(const std::vector<QuicStreamFrame>& frame));
+ MOCK_METHOD1(OnWindowUpdateFrames,
+ void(const std::vector<QuicWindowUpdateFrame>& frame));
+ MOCK_METHOD1(OnBlockedFrames,
+ void(const std::vector<QuicBlockedFrame>& frame));
MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame));
MOCK_METHOD1(OnGoAway, void(const QuicGoAwayFrame& frame));
MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer));
- MOCK_METHOD0(OnCanWrite, bool());
+ MOCK_METHOD0(OnWriteBlocked, void());
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_CONST_METHOD0(WillingAndAbleToWrite, bool());
MOCK_CONST_METHOD0(HasPendingHandshake, bool());
+ MOCK_CONST_METHOD0(HasOpenDataStreams, bool());
MOCK_METHOD1(OnSuccessfulVersionNegotiation,
void(const QuicVersion& version));
MOCK_METHOD0(OnConfigNegotiated, void());
@@ -236,30 +252,31 @@ class MockHelper : public QuicConnectionHelperInterface {
public:
MockHelper();
virtual ~MockHelper();
-
- MOCK_METHOD1(SetConnection, void(QuicConnection* connection));
- const QuicClock* GetClock() const;
- QuicRandom* GetRandomGenerator();
+ virtual const QuicClock* GetClock() const OVERRIDE;
+ virtual QuicRandom* GetRandomGenerator() OVERRIDE;
+ virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) OVERRIDE;
void AdvanceTime(QuicTime::Delta delta);
- virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate);
private:
MockClock clock_;
MockRandom random_generator_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockHelper);
};
class MockConnection : public QuicConnection {
public:
- // Uses a MockHelper, GUID of 42, and 127.0.0.1:123.
+ // Uses a MockHelper, ConnectionId of 42, and 127.0.0.1:123.
explicit MockConnection(bool is_server);
- // Uses a MockHelper, GUID of 42.
- MockConnection(IPEndPoint address,
- bool is_server);
+ // Uses a MockHelper, ConnectionId of 42.
+ MockConnection(IPEndPoint address, bool is_server);
// Uses a MockHelper, and 127.0.0.1:123
- MockConnection(QuicGuid guid,
- bool is_server);
+ MockConnection(QuicConnectionId connection_id, bool is_server);
+
+ // Uses a Mock helper, ConnectionId of 42, and 127.0.0.1:123.
+ MockConnection(bool is_server, const QuicVersionVector& supported_versions);
virtual ~MockConnection();
@@ -273,13 +290,18 @@ class MockConnection : public QuicConnection {
MOCK_METHOD1(SendConnectionClose, void(QuicErrorCode error));
MOCK_METHOD2(SendConnectionCloseWithDetails, void(QuicErrorCode error,
const string& details));
- MOCK_METHOD2(SendRstStream, void(QuicStreamId id,
- QuicRstStreamErrorCode error));
+ MOCK_METHOD2(SendConnectionClosePacket, void(QuicErrorCode error,
+ const string& details));
+ MOCK_METHOD3(SendRstStream, void(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
MOCK_METHOD3(SendGoAway, void(QuicErrorCode error,
QuicStreamId last_good_stream_id,
const string& reason));
- MOCK_METHOD0(OnCanWrite, bool());
- MOCK_CONST_METHOD0(HasPendingHandshake, bool());
+ MOCK_METHOD1(SendBlocked, void(QuicStreamId id));
+ MOCK_METHOD2(SendWindowUpdate, void(QuicStreamId id,
+ QuicStreamOffset byte_offset));
+ MOCK_METHOD0(OnCanWrite, void());
void ProcessUdpPacketInternal(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
@@ -301,6 +323,10 @@ class MockConnection : public QuicConnection {
class PacketSavingConnection : public MockConnection {
public:
explicit PacketSavingConnection(bool is_server);
+
+ PacketSavingConnection(bool is_server,
+ const QuicVersionVector& supported_versions);
+
virtual ~PacketSavingConnection();
virtual bool SendOrQueuePacket(EncryptionLevel level,
@@ -318,25 +344,31 @@ class MockSession : public QuicSession {
public:
explicit MockSession(QuicConnection* connection);
virtual ~MockSession();
-
- MOCK_METHOD4(OnPacket, bool(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const std::vector<QuicStreamFrame>& frame));
MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer));
MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id));
MOCK_METHOD0(GetCryptoStream, QuicCryptoStream*());
MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*());
MOCK_METHOD6(WritevData,
QuicConsumedData(QuicStreamId id,
- const struct iovec* iov,
- int count,
+ const IOVector& data,
QuicStreamOffset offset,
bool fin,
+ FecProtection fec_protection,
QuicAckNotifier::DelegateInterface*));
- MOCK_METHOD0(IsHandshakeComplete, bool());
+ MOCK_METHOD2(OnStreamHeaders, void(QuicStreamId stream_id,
+ base::StringPiece headers_data));
+ MOCK_METHOD2(OnStreamHeadersPriority, void(QuicStreamId stream_id,
+ QuicPriority priority));
+ MOCK_METHOD3(OnStreamHeadersComplete, void(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len));
+ MOCK_METHOD3(SendRstStream, void(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
MOCK_METHOD0(IsCryptoHandshakeConfirmed, bool());
+ using QuicSession::ActivateStream;
+
private:
DISALLOW_COPY_AND_ASSIGN(MockSession);
};
@@ -351,25 +383,55 @@ class TestSession : public QuicSession {
void SetCryptoStream(QuicCryptoStream* stream);
- virtual QuicCryptoStream* GetCryptoStream();
+ virtual QuicCryptoStream* GetCryptoStream() OVERRIDE;
private:
QuicCryptoStream* crypto_stream_;
+
DISALLOW_COPY_AND_ASSIGN(TestSession);
};
+class TestClientSession : public QuicClientSessionBase {
+ public:
+ TestClientSession(QuicConnection* connection, const QuicConfig& config);
+ virtual ~TestClientSession();
+
+ // QuicClientSessionBase
+ MOCK_METHOD1(OnProofValid,
+ void(const QuicCryptoClientConfig::CachedState& cached));
+ MOCK_METHOD1(OnProofVerifyDetailsAvailable,
+ void(const ProofVerifyDetails& verify_details));
+
+ // TestClientSession
+ MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id));
+ MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*());
+
+ void SetCryptoStream(QuicCryptoStream* stream);
+
+ virtual QuicCryptoStream* GetCryptoStream() OVERRIDE;
+
+ private:
+ QuicCryptoStream* crypto_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestClientSession);
+};
+
class MockPacketWriter : public QuicPacketWriter {
public:
MockPacketWriter();
virtual ~MockPacketWriter();
- MOCK_METHOD5(WritePacket,
+ MOCK_METHOD4(WritePacket,
WriteResult(const char* buffer,
size_t buf_len,
const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer));
+ const IPEndPoint& peer_address));
MOCK_CONST_METHOD0(IsWriteBlockedDataBuffered, bool());
+ MOCK_CONST_METHOD0(IsWriteBlocked, bool());
+ MOCK_METHOD0(SetWritable, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockPacketWriter);
};
class MockSendAlgorithm : public SendAlgorithmInterface {
@@ -379,24 +441,23 @@ class MockSendAlgorithm : public SendAlgorithmInterface {
MOCK_METHOD2(SetFromConfig, void(const QuicConfig& config, bool is_server));
MOCK_METHOD1(SetMaxPacketSize, void(QuicByteCount max_packet_size));
- MOCK_METHOD3(OnIncomingQuicCongestionFeedbackFrame,
+ MOCK_METHOD2(OnIncomingQuicCongestionFeedbackFrame,
void(const QuicCongestionFeedbackFrame&,
- QuicTime feedback_receive_time,
- const SentPacketsMap&));
- MOCK_METHOD3(OnPacketAcked,
- void(QuicPacketSequenceNumber, QuicByteCount, QuicTime::Delta));
- MOCK_METHOD2(OnPacketLost, void(QuicPacketSequenceNumber, QuicTime));
+ QuicTime feedback_receive_time));
+ MOCK_METHOD4(OnCongestionEvent, void(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionMap& acked_packets,
+ const CongestionMap& lost_packets));
MOCK_METHOD5(OnPacketSent,
- bool(QuicTime sent_time, QuicPacketSequenceNumber, QuicByteCount,
- TransmissionType, HasRetransmittableData));
- MOCK_METHOD0(OnRetransmissionTimeout, void());
- MOCK_METHOD2(OnPacketAbandoned, void(QuicPacketSequenceNumber sequence_number,
- QuicByteCount abandoned_bytes));
- MOCK_METHOD4(TimeUntilSend, QuicTime::Delta(QuicTime now, TransmissionType,
- HasRetransmittableData,
- IsHandshake));
+ bool(QuicTime, QuicByteCount, QuicPacketSequenceNumber,
+ QuicByteCount, HasRetransmittableData));
+ MOCK_METHOD1(OnRetransmissionTimeout, void(bool));
+ MOCK_CONST_METHOD3(TimeUntilSend,
+ QuicTime::Delta(QuicTime now,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData));
MOCK_CONST_METHOD0(BandwidthEstimate, QuicBandwidth(void));
- MOCK_CONST_METHOD0(SmoothedRtt, QuicTime::Delta(void));
+ MOCK_METHOD1(OnRttUpdated, void(QuicPacketSequenceNumber));
MOCK_CONST_METHOD0(RetransmissionDelay, QuicTime::Delta(void));
MOCK_CONST_METHOD0(GetCongestionWindow, QuicByteCount());
@@ -404,6 +465,23 @@ class MockSendAlgorithm : public SendAlgorithmInterface {
DISALLOW_COPY_AND_ASSIGN(MockSendAlgorithm);
};
+class MockLossAlgorithm : public LossDetectionInterface {
+ public:
+ MockLossAlgorithm();
+ virtual ~MockLossAlgorithm();
+
+ MOCK_CONST_METHOD0(GetLossDetectionType, LossDetectionType());
+ MOCK_METHOD4(DetectLostPackets,
+ SequenceNumberSet(const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats));
+ MOCK_CONST_METHOD0(GetLossTimeout, QuicTime());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockLossAlgorithm);
+};
+
class TestEntropyCalculator :
public QuicReceivedEntropyHashCalculatorInterface {
public:
@@ -412,6 +490,9 @@ class TestEntropyCalculator :
virtual QuicPacketEntropyHash EntropyHash(
QuicPacketSequenceNumber sequence_number) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestEntropyCalculator);
};
class MockEntropyCalculator : public TestEntropyCalculator {
@@ -422,28 +503,27 @@ class MockEntropyCalculator : public TestEntropyCalculator {
MOCK_CONST_METHOD1(
EntropyHash,
QuicPacketEntropyHash(QuicPacketSequenceNumber sequence_number));
-};
-
-class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor {
- public:
- virtual ~TestDecompressorVisitor() {}
- virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE;
- virtual void OnDecompressionError() OVERRIDE;
-
- string data() { return data_; }
- bool error() { return error_; }
private:
- string data_;
- bool error_;
+ DISALLOW_COPY_AND_ASSIGN(MockEntropyCalculator);
};
class MockAckNotifierDelegate : public QuicAckNotifier::DelegateInterface {
public:
MockAckNotifierDelegate();
+
+ MOCK_METHOD5(OnAckNotification, void(int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed));
+
+ protected:
+ // Object is ref counted.
virtual ~MockAckNotifierDelegate();
- MOCK_METHOD0(OnAckNotification, void());
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAckNotifierDelegate);
};
} // namespace test
diff --git a/chromium/net/quic/test_tools/quic_test_writer.cc b/chromium/net/quic/test_tools/quic_test_writer.cc
deleted file mode 100644
index 6755b2cfa8c..00000000000
--- a/chromium/net/quic/test_tools/quic_test_writer.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/test_tools/quic_test_writer.h"
-
-namespace net {
-namespace test {
-
-QuicTestWriter::QuicTestWriter() {}
-
-QuicTestWriter::~QuicTestWriter() {}
-
-QuicPacketWriter* QuicTestWriter::writer() {
- return writer_.get();
-}
-
-void QuicTestWriter::set_writer(QuicPacketWriter* writer) {
- writer_.reset(writer);
-}
-
-} // namespace test
-} // namespace net
diff --git a/chromium/net/quic/test_tools/quic_test_writer.h b/chromium/net/quic/test_tools/quic_test_writer.h
deleted file mode 100644
index ee33b7abe2a..00000000000
--- a/chromium/net/quic/test_tools/quic_test_writer.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_WRITER_H_
-#define NET_QUIC_TEST_TOOLS_QUIC_TEST_WRITER_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "net/quic/quic_packet_writer.h"
-
-namespace net {
-namespace test {
-
-// Allows setting a writer for a QuicConnection, to allow
-// fine-grained control of writes.
-class QuicTestWriter : public QuicPacketWriter {
- public:
- QuicTestWriter();
- virtual ~QuicTestWriter();
- // Takes ownership of |writer|.
- void set_writer(QuicPacketWriter* writer);
-
- protected:
- QuicPacketWriter* writer();
-
- private:
- scoped_ptr<QuicPacketWriter> writer_;
-};
-
-} // namespace test
-} // namespace net
-
-#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_WRITER_H_
diff --git a/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc b/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc
index 5119d035faa..914609ee1d5 100644
--- a/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc
+++ b/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc
@@ -4,6 +4,8 @@
#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include <list>
+
#include "net/quic/reliable_quic_stream.h"
namespace net {
@@ -22,5 +24,38 @@ void ReliableQuicStreamPeer::SetStreamBytesWritten(
stream->stream_bytes_written_ = stream_bytes_written;
}
+// static
+void ReliableQuicStreamPeer::CloseReadSide(ReliableQuicStream* stream) {
+ stream->CloseReadSide();
+}
+
+// static
+bool ReliableQuicStreamPeer::FinSent(ReliableQuicStream* stream) {
+ return stream->fin_sent_;
+}
+
+// static
+bool ReliableQuicStreamPeer::RstSent(ReliableQuicStream* stream) {
+ return stream->rst_sent_;
+}
+
+// static
+uint32 ReliableQuicStreamPeer::SizeOfQueuedData(ReliableQuicStream* stream) {
+ uint32 total = 0;
+ std::list<ReliableQuicStream::PendingData>::iterator it =
+ stream->queued_data_.begin();
+ while (it != stream->queued_data_.end()) {
+ total += it->data.size();
+ ++it;
+ }
+ return total;
+}
+
+// static
+void ReliableQuicStreamPeer::SetFecPolicy(ReliableQuicStream* stream,
+ FecPolicy fec_policy) {
+ stream->set_fec_policy(fec_policy);
+}
+
} // namespace test
} // namespace net
diff --git a/chromium/net/quic/test_tools/reliable_quic_stream_peer.h b/chromium/net/quic/test_tools/reliable_quic_stream_peer.h
index da229da44e0..1f5467d00ee 100644
--- a/chromium/net/quic/test_tools/reliable_quic_stream_peer.h
+++ b/chromium/net/quic/test_tools/reliable_quic_stream_peer.h
@@ -19,6 +19,14 @@ class ReliableQuicStreamPeer {
static void SetWriteSideClosed(bool value, ReliableQuicStream* stream);
static void SetStreamBytesWritten(QuicStreamOffset stream_bytes_written,
ReliableQuicStream* stream);
+ static void CloseReadSide(ReliableQuicStream* stream);
+
+ static bool FinSent(ReliableQuicStream* stream);
+ static bool RstSent(ReliableQuicStream* stream);
+
+ static uint32 SizeOfQueuedData(ReliableQuicStream* stream);
+
+ static void SetFecPolicy(ReliableQuicStream* stream, FecPolicy fec_policy);
private:
DISALLOW_COPY_AND_ASSIGN(ReliableQuicStreamPeer);
diff --git a/chromium/net/quic/test_tools/simple_quic_framer.cc b/chromium/net/quic/test_tools/simple_quic_framer.cc
index 7d6439d2dcb..a08d5555663 100644
--- a/chromium/net/quic/test_tools/simple_quic_framer.cc
+++ b/chromium/net/quic/test_tools/simple_quic_framer.cc
@@ -22,7 +22,7 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface {
: error_(QUIC_NO_ERROR) {
}
- virtual ~SimpleFramerVisitor() {
+ virtual ~SimpleFramerVisitor() OVERRIDE {
STLDeleteElements(&stream_data_);
}
@@ -36,21 +36,30 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface {
virtual void OnPacket() OVERRIDE {}
virtual void OnPublicResetPacket(
- const QuicPublicResetPacket& packet) OVERRIDE {}
+ const QuicPublicResetPacket& packet) OVERRIDE {
+ public_reset_packet_.reset(new QuicPublicResetPacket(packet));
+ }
virtual void OnVersionNegotiationPacket(
- const QuicVersionNegotiationPacket& packet) OVERRIDE {}
+ const QuicVersionNegotiationPacket& packet) OVERRIDE {
+ version_negotiation_packet_.reset(
+ new QuicVersionNegotiationPacket(packet));
+ }
virtual void OnRevivedPacket() OVERRIDE {}
- virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE {
- has_header_ = true;
- header_ = header;
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE {
return true;
}
-
virtual bool OnUnauthenticatedHeader(
const QuicPacketHeader& header) OVERRIDE {
return true;
}
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {}
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE {
+ has_header_ = true;
+ header_ = header;
+ return true;
+ }
virtual void OnFecProtectedPayload(StringPiece payload) OVERRIDE {}
@@ -77,6 +86,16 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface {
return true;
}
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE {
+ stop_waiting_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE {
+ ping_frames_.push_back(frame);
+ return true;
+ }
+
virtual void OnFecData(const QuicFecData& fec) OVERRIDE {
fec_data_ = fec;
fec_redundancy_ = fec_data_.redundancy.as_string();
@@ -99,6 +118,17 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface {
return true;
}
+ virtual bool OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) OVERRIDE {
+ window_update_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+ blocked_frames_.push_back(frame);
+ return true;
+ }
+
virtual void OnPacketComplete() OVERRIDE {}
const QuicPacketHeader& header() const { return header_; }
@@ -118,22 +148,40 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface {
const vector<QuicStreamFrame>& stream_frames() const {
return stream_frames_;
}
+ const vector<QuicStopWaitingFrame>& stop_waiting_frames() const {
+ return stop_waiting_frames_;
+ }
+ const vector<QuicPingFrame>& ping_frames() const {
+ return ping_frames_;
+ }
const QuicFecData& fec_data() const {
return fec_data_;
}
+ const QuicVersionNegotiationPacket* version_negotiation_packet() const {
+ return version_negotiation_packet_.get();
+ }
+ const QuicPublicResetPacket* public_reset_packet() const {
+ return public_reset_packet_.get();
+ }
private:
QuicErrorCode error_;
bool has_header_;
QuicPacketHeader header_;
QuicFecData fec_data_;
+ scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
+ scoped_ptr<QuicPublicResetPacket> public_reset_packet_;
string fec_redundancy_;
vector<QuicAckFrame> ack_frames_;
vector<QuicCongestionFeedbackFrame> feedback_frames_;
+ vector<QuicStopWaitingFrame> stop_waiting_frames_;
+ vector<QuicPingFrame> ping_frames_;
vector<QuicStreamFrame> stream_frames_;
vector<QuicRstStreamFrame> rst_stream_frames_;
vector<QuicGoAwayFrame> goaway_frames_;
vector<QuicConnectionCloseFrame> connection_close_frames_;
+ vector<QuicWindowUpdateFrame> window_update_frames_;
+ vector<QuicBlockedFrame> blocked_frames_;
vector<string*> stream_data_;
DISALLOW_COPY_AND_ASSIGN(SimpleFramerVisitor);
@@ -143,6 +191,10 @@ SimpleQuicFramer::SimpleQuicFramer()
: framer_(QuicSupportedVersions(), QuicTime::Zero(), true) {
}
+SimpleQuicFramer::SimpleQuicFramer(const QuicVersionVector& supported_versions)
+ : framer_(supported_versions, QuicTime::Zero(), true) {
+}
+
SimpleQuicFramer::~SimpleQuicFramer() {
}
@@ -158,6 +210,11 @@ bool SimpleQuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
return framer_.ProcessPacket(packet);
}
+void SimpleQuicFramer::Reset() {
+ visitor_.reset(new SimpleFramerVisitor);
+}
+
+
const QuicPacketHeader& SimpleQuicFramer::header() const {
return visitor_->header();
}
@@ -166,16 +223,27 @@ const QuicFecData& SimpleQuicFramer::fec_data() const {
return visitor_->fec_data();
}
+const QuicVersionNegotiationPacket*
+SimpleQuicFramer::version_negotiation_packet() const {
+ return visitor_->version_negotiation_packet();
+}
+
+const QuicPublicResetPacket* SimpleQuicFramer::public_reset_packet() const {
+ return visitor_->public_reset_packet();
+}
+
QuicFramer* SimpleQuicFramer::framer() {
return &framer_;
}
size_t SimpleQuicFramer::num_frames() const {
return ack_frames().size() +
- stream_frames().size() +
feedback_frames().size() +
- rst_stream_frames().size() +
goaway_frames().size() +
+ rst_stream_frames().size() +
+ stop_waiting_frames().size() +
+ stream_frames().size() +
+ ping_frames().size() +
connection_close_frames().size();
}
@@ -183,6 +251,15 @@ const vector<QuicAckFrame>& SimpleQuicFramer::ack_frames() const {
return visitor_->ack_frames();
}
+const vector<QuicStopWaitingFrame>&
+SimpleQuicFramer::stop_waiting_frames() const {
+ return visitor_->stop_waiting_frames();
+}
+
+const vector<QuicPingFrame>& SimpleQuicFramer::ping_frames() const {
+ return visitor_->ping_frames();
+}
+
const vector<QuicStreamFrame>& SimpleQuicFramer::stream_frames() const {
return visitor_->stream_frames();
}
diff --git a/chromium/net/quic/test_tools/simple_quic_framer.h b/chromium/net/quic/test_tools/simple_quic_framer.h
index 4416019dba3..7a31014d41e 100644
--- a/chromium/net/quic/test_tools/simple_quic_framer.h
+++ b/chromium/net/quic/test_tools/simple_quic_framer.h
@@ -7,6 +7,7 @@
#include <vector>
+#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_protocol.h"
@@ -29,22 +30,33 @@ class SimpleFramerVisitor;
class SimpleQuicFramer {
public:
SimpleQuicFramer();
+ explicit SimpleQuicFramer(const QuicVersionVector& supported_versions);
~SimpleQuicFramer();
bool ProcessPacket(const QuicEncryptedPacket& packet);
bool ProcessPacket(const QuicPacket& packet);
+ void Reset();
const QuicPacketHeader& header() const;
size_t num_frames() const;
const std::vector<QuicAckFrame>& ack_frames() const;
const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const;
const std::vector<QuicCongestionFeedbackFrame>& feedback_frames() const;
+ const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const;
+ const std::vector<QuicPingFrame>& ping_frames() const;
const std::vector<QuicGoAwayFrame>& goaway_frames() const;
const std::vector<QuicRstStreamFrame>& rst_stream_frames() const;
const std::vector<QuicStreamFrame>& stream_frames() const;
const QuicFecData& fec_data() const;
+ const QuicVersionNegotiationPacket* version_negotiation_packet() const;
+ const QuicPublicResetPacket* public_reset_packet() const;
+
QuicFramer* framer();
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ framer_.SetSupportedVersions(versions);
+ }
+
private:
QuicFramer framer_;
scoped_ptr<SimpleFramerVisitor> visitor_;