summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/third_party/quiche')
-rw-r--r--chromium/net/third_party/quiche/BUILD.gn39
-rw-r--r--chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_bug_tracker_impl.h20
-rw-r--r--chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_logging_impl.h2
-rw-r--r--chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_thread_local_impl.h27
-rw-r--r--chromium/net/third_party/quiche/src/CONTRIBUTING.md13
-rw-r--r--chromium/net/third_party/quiche/src/README.md30
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/api/quiche_bug_tracker.h2
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils.cc51
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils.h40
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils_test.cc86
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/api/quiche_test.h12
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/api/quiche_thread_local.h27
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_bug_tracker_impl.h15
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_containers_impl.h21
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_estimate_memory_usage_impl.h20
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_file_utils_impl.cc182
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_file_utils_impl.h26
-rw-r--r--chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_thread_local_impl.h24
-rw-r--r--chromium/net/third_party/quiche/src/common/print_elements.h35
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.cc330
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.h47
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/callback_visitor_test.cc89
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/data_source.cc23
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/data_source.h53
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/data_source_test.cc40
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/http2_adapter.h106
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.cc4
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.h46
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/http2_session.h5
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/http2_util.h7
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/http2_visitor_interface.h63
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/mock_http2_visitor.h46
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.cc9
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.h6
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.cc174
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.h81
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter_test.cc1438
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.cc169
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.h47
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.cc63
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.h37
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider_test.cc117
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.cc24
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.h11
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session_test.cc67
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test.cc205
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.cc454
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.h99
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.cc158
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.h15
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util_test.cc109
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.cc77
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.h31
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter_test.cc841
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.cc722
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.h193
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session_test.cc842
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/oghttp2_util.h4
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.cc58
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.h20
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.cc76
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.h20
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/test_utils.cc526
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/test_utils.h115
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/window_manager.h4
-rw-r--r--chromium/net/third_party/quiche/src/http2/adapter/window_manager_test.cc10
-rw-r--r--chromium/net/third_party/quiche/src/http2/core/http2_priority_write_scheduler.h1
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.cc5
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h7
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h4
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc5
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.h3
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc1
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.cc13
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.h3
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc1
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc1
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc10
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc1
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc1
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc1
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc1
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc11
-rw-r--r--chromium/net/third_party/quiche/src/http2/http2_constants.cc1
-rw-r--r--chromium/net/third_party/quiche/src/http2/http2_structures.cc1
-rw-r--r--chromium/net/third_party/quiche/src/http2/http2_structures_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/platform/api/http2_containers.h17
-rw-r--r--chromium/net/third_party/quiche/src/http2/platform/api/http2_estimate_memory_usage.h21
-rw-r--r--chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h23
-rw-r--r--chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/test_tools/http2_random.cc1
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc16
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.h6
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer_test.cc8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h1
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.cc34
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc10
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc6
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender_test.cc93
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.cc14
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc28
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc3
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h7
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc26
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc26
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h43
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.cc7
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.h8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc14
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h12
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc43
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h3
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.cc17
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.cc10
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.cc25
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h16
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc6
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h3
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc6
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.cc11
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.h8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.h2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_streams_blocked_frame.h2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc49
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc298
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h82
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc641
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc25
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h13
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc42
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_frames.h43
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_frames_test.cc26
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc6
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc39
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h9
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc38
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc19
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc55
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc155
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h82
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc249
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc433
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h116
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc329
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc11
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.cc88
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h32
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc3
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc60
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_alarm.h19
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc43
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.h53
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc26
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h10
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection.cc896
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection.h145
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_context.cc36
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_context.h118
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_context_test.cc173
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc565
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_constants.h8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc9
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc5
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc5
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc11
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc11
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.cc8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue_test.cc5
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc194
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h42
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc70
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h5
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h82
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_framer.cc12
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_framer.h9
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc32
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.cc26
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.h8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector_test.cc20
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_interval_set.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_lru_cache.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.cc14
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h5
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc15
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h6
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc57
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_path_validator.h3
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc23
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h3
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc122
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_server_id.cc6
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_server_id.h6
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_server_id_test.cc21
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_session.cc155
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_session.h71
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc83
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h5
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator_test.cc38
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream.cc84
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream.h46
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc15
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc18
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc29
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc3
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h11
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc10
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_types.cc6
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_types.h23
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc11
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h5
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_utils.cc35
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_utils.h12
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc8
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_versions.cc16
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_versions.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc19
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/stream_delegate_interface.h19
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc35
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc7
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc13
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h20
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc224
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h61
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc17
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.cc7
-rw-r--r--chromium/net/third_party/quiche/src/quic/masque/masque_client_bin.cc2
-rw-r--r--chromium/net/third_party/quiche/src/quic/masque/masque_client_session.cc124
-rw-r--r--chromium/net/third_party/quiche/src/quic/masque/masque_client_session.h35
-rw-r--r--chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.cc71
-rw-r--r--chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.h19
-rw-r--r--chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.cc16
-rw-r--r--chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.h7
-rw-r--r--chromium/net/third_party/quiche/src/quic/masque/masque_server_bin.cc2
-rw-r--r--chromium/net/third_party/quiche/src/quic/masque/masque_server_session.cc170
-rw-r--r--chromium/net/third_party/quiche/src/quic/masque/masque_server_session.h55
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h20
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_containers_test.cc63
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_estimate_memory_usage.h21
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.cc23
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.h26
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc3
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_map_util.h24
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h17
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_test.cc15
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller.cc22
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller.h16
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller_test.cc9
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc1
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc6
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.cc15
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger_test.cc16
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc4
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h2
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc8
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc8
-rw-r--r--chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc8
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc6
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h5
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc5
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h2
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.cc33
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.h13
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc9
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_offline_decoder.cc26
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc15
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h3
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.cc1
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc17
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc7
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h1
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc12
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h10
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc14
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h85
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.cc3
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc43
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h3
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc2
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/test_ticket_crypter.cc3
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/test_ticket_crypter.h3
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc17
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h5
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h7
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc63
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc34
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc4
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h1
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc5
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc144
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc4
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h7
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc14
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter.cc7
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter.h3
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter_test.cc10
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc5
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h8
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc5
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc6
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h5
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc30
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc5
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h3
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.cc7
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h3
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc5
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h3
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.cc7
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h3
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc25
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h14
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/http2_header_block_hpack_listener.h47
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/metadata_extension.cc195
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/metadata_extension.h116
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/metadata_extension_test.cc226
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc1
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc15
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h5
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc98
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc7
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h14
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_header_block_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_header_storage.h2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_intrusive_list.h7
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc1
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h21
-rw-r--r--chromium/net/third_party/quiche/src/spdy/platform/api/spdy_containers.h18
-rw-r--r--chromium/net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h21
-rw-r--r--chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h21
355 files changed, 13102 insertions, 5415 deletions
diff --git a/chromium/net/third_party/quiche/BUILD.gn b/chromium/net/third_party/quiche/BUILD.gn
index 41d2dfff1d9..eb544f93d1c 100644
--- a/chromium/net/third_party/quiche/BUILD.gn
+++ b/chromium/net/third_party/quiche/BUILD.gn
@@ -31,17 +31,22 @@ source_set("quiche") {
sources = [
"overrides/quiche_platform_impl/quic_mutex_impl.cc",
"overrides/quiche_platform_impl/quic_mutex_impl.h",
+ "overrides/quiche_platform_impl/quiche_bug_tracker_impl.h",
"overrides/quiche_platform_impl/quiche_export_impl.h",
"overrides/quiche_platform_impl/quiche_logging_impl.h",
+ "overrides/quiche_platform_impl/quiche_thread_local_impl.h",
"overrides/quiche_platform_impl/quiche_time_utils_impl.cc",
"overrides/quiche_platform_impl/quiche_time_utils_impl.h",
+ "src/common/platform/api/quiche_bug_tracker.h",
"src/common/platform/api/quiche_export.h",
"src/common/platform/api/quiche_flag_utils.h",
"src/common/platform/api/quiche_flags.h",
"src/common/platform/api/quiche_logging.h",
"src/common/platform/api/quiche_prefetch.h",
+ "src/common/platform/api/quiche_thread_local.h",
"src/common/platform/api/quiche_time_utils.h",
"src/common/platform/default/quiche_platform_impl/quiche_prefetch_impl.h",
+ "src/common/print_elements.h",
"src/common/quiche_circular_deque.h",
"src/common/quiche_data_reader.cc",
"src/common/quiche_data_reader.h",
@@ -140,13 +145,10 @@ source_set("quiche") {
"src/http2/http2_structures.cc",
"src/http2/http2_structures.h",
"src/http2/platform/api/http2_bug_tracker.h",
- "src/http2/platform/api/http2_containers.h",
- "src/http2/platform/api/http2_estimate_memory_usage.h",
"src/http2/platform/api/http2_flag_utils.h",
"src/http2/platform/api/http2_flags.h",
"src/http2/platform/api/http2_logging.h",
"src/http2/platform/api/http2_macros.h",
- "src/http2/platform/api/http2_string_utils.h",
"src/quic/core/congestion_control/bandwidth_sampler.cc",
"src/quic/core/congestion_control/bandwidth_sampler.h",
"src/quic/core/congestion_control/bbr2_drain.cc",
@@ -433,6 +435,8 @@ source_set("quiche") {
"src/quic/core/quic_config.h",
"src/quic/core/quic_connection.cc",
"src/quic/core/quic_connection.h",
+ "src/quic/core/quic_connection_context.cc",
+ "src/quic/core/quic_connection_context.h",
"src/quic/core/quic_connection_id.cc",
"src/quic/core/quic_connection_id.h",
"src/quic/core/quic_connection_id_manager.cc",
@@ -463,6 +467,7 @@ source_set("quiche") {
"src/quic/core/quic_datagram_queue.h",
"src/quic/core/quic_error_codes.cc",
"src/quic/core/quic_error_codes.h",
+ "src/quic/core/quic_flags_list.h",
"src/quic/core/quic_flow_controller.cc",
"src/quic/core/quic_flow_controller.h",
"src/quic/core/quic_framer.cc",
@@ -552,11 +557,8 @@ source_set("quiche") {
"src/quic/platform/api/quic_client_stats.h",
"src/quic/platform/api/quic_containers.h",
"src/quic/platform/api/quic_error_code_wrappers.h",
- "src/quic/platform/api/quic_estimate_memory_usage.h",
"src/quic/platform/api/quic_export.h",
"src/quic/platform/api/quic_exported_stats.h",
- "src/quic/platform/api/quic_file_utils.cc",
- "src/quic/platform/api/quic_file_utils.h",
"src/quic/platform/api/quic_flag_utils.h",
"src/quic/platform/api/quic_flags.h",
"src/quic/platform/api/quic_hostname_utils.cc",
@@ -566,7 +568,6 @@ source_set("quiche") {
"src/quic/platform/api/quic_ip_address.h",
"src/quic/platform/api/quic_ip_address_family.h",
"src/quic/platform/api/quic_logging.h",
- "src/quic/platform/api/quic_map_util.h",
"src/quic/platform/api/quic_mem_slice.h",
"src/quic/platform/api/quic_mem_slice_span.h",
"src/quic/platform/api/quic_mem_slice_storage.h",
@@ -633,8 +634,6 @@ source_set("quiche") {
"src/spdy/core/spdy_simple_arena.cc",
"src/spdy/core/spdy_simple_arena.h",
"src/spdy/core/zero_copy_output_buffer.h",
- "src/spdy/platform/api/spdy_containers.h",
- "src/spdy/platform/api/spdy_estimate_memory_usage.h",
]
deps = [ "//net:net_deps" ]
@@ -859,6 +858,7 @@ source_set("quiche_test_tools_core") {
testonly = true
sources = [
"src/common/platform/api/quiche_test.h",
+ "src/common/platform/api/quiche_test_helpers.h",
"src/common/test_tools/quiche_test_utils.cc",
"src/common/test_tools/quiche_test_utils.h",
]
@@ -869,6 +869,17 @@ source_set("quiche_test_tools_core") {
]
}
+source_set("quiche_file_utils") {
+ sources = [
+ "src/common/platform/api/quiche_file_utils.cc",
+ "src/common/platform/api/quiche_file_utils.h",
+ "src/common/platform/default/quiche_platform_impl/quiche_file_utils_impl.cc",
+ "src/common/platform/default/quiche_platform_impl/quiche_file_utils_impl.h",
+ ]
+ configs += [ ":quiche_config" ]
+ deps = [ "//base" ]
+}
+
source_set("quic_test_tools_core") {
testonly = true
sources = [
@@ -1005,6 +1016,7 @@ source_set("quic_test_tools_core") {
]
deps = [
+ ":quiche_file_utils",
"//base",
"//crypto:test_support",
"//net",
@@ -1100,6 +1112,7 @@ source_set("simple_quic_tools_core") {
"src/quic/tools/simple_ticket_crypter.h",
]
deps = [
+ ":quiche_file_utils",
"//base",
"//base/third_party/dynamic_annotations",
"//net",
@@ -1136,6 +1149,7 @@ if (!is_ios) {
executable("quic_packet_printer") {
sources = [ "src/quic/tools/quic_packet_printer_bin.cc" ]
deps = [
+ ":quiche_file_utils",
"//base",
"//build/win:default_exe_manifest",
"//net",
@@ -1185,6 +1199,8 @@ if (!is_ios) {
source_set("quiche_tests") {
testonly = true
sources = [
+ # TODO(bnc): Include in tests after test data files are added to QUICHE.
+ # "src/common/platform/api/quiche_file_utils_test.cc",
"src/common/platform/api/quiche_time_utils_test.cc",
"src/common/quiche_circular_deque_test.cc",
"src/common/quiche_data_writer_test.cc",
@@ -1312,6 +1328,7 @@ source_set("quiche_tests") {
"src/quic/core/frames/quic_frames_test.cc",
"src/quic/core/http/http_decoder_test.cc",
"src/quic/core/http/http_encoder_test.cc",
+ "src/quic/core/http/http_frames_test.cc",
"src/quic/core/http/quic_client_promised_info_test.cc",
"src/quic/core/http/quic_client_push_promise_index_test.cc",
"src/quic/core/http/quic_header_list_test.cc",
@@ -1351,6 +1368,7 @@ source_set("quiche_tests") {
"src/quic/core/quic_chaos_protector_test.cc",
"src/quic/core/quic_coalesced_packet_test.cc",
"src/quic/core/quic_config_test.cc",
+ "src/quic/core/quic_connection_context_test.cc",
"src/quic/core/quic_connection_id_manager_test.cc",
"src/quic/core/quic_connection_id_test.cc",
"src/quic/core/quic_connection_test.cc",
@@ -1400,9 +1418,9 @@ source_set("quiche_tests") {
"src/quic/core/quic_versions_test.cc",
"src/quic/core/quic_write_blocked_list_test.cc",
"src/quic/core/tls_chlo_extractor_test.cc",
+ "src/quic/core/tls_client_handshaker_test.cc",
"src/quic/core/uber_quic_stream_id_manager_test.cc",
"src/quic/core/uber_received_packet_manager_test.cc",
- "src/quic/platform/api/quic_containers_test.cc",
"src/quic/platform/api/quic_hostname_utils_test.cc",
"src/quic/platform/api/quic_ip_address_test.cc",
"src/quic/platform/api/quic_mem_slice_span_test.cc",
@@ -1455,6 +1473,7 @@ source_set("quiche_tests") {
]
deps = [
+ ":quiche_file_utils",
"//net",
"//net:quic_test_tools",
"//net:quiche_test_tools",
diff --git a/chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_bug_tracker_impl.h b/chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_bug_tracker_impl.h
new file mode 100644
index 00000000000..5ae25a09c77
--- /dev/null
+++ b/chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_bug_tracker_impl.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef NET_THIRD_PARTY_QUICHE_OVERRIDES_QUICHE_PLATFORM_IMPL_QUICHE_BUG_TRACKER_IMPL_H_
+#define NET_THIRD_PARTY_QUICHE_OVERRIDES_QUICHE_PLATFORM_IMPL_QUICHE_BUG_TRACKER_IMPL_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+#define QUICHE_BUG_IMPL(bug_id) QUIC_LOG(DFATAL)
+#define QUICHE_BUG_IF_IMPL(bug_id, condition) QUIC_LOG_IF(DFATAL, condition)
+#define QUICHE_PEER_BUG_IMPL(bug_id) QUIC_LOG(ERROR)
+#define QUICHE_PEER_BUG_IF_IMPL(bug_id, condition) QUIC_LOG_IF(ERROR, condition)
+
+#define QUICHE_BUG_V2_IMPL(bug_id) QUIC_LOG(DFATAL)
+#define QUICHE_BUG_IF_V2_IMPL(bug_id, condition) QUIC_LOG_IF(DFATAL, condition)
+#define QUICHE_PEER_BUG_V2_IMPL(bug_id) QUIC_LOG(ERROR)
+#define QUICHE_PEER_BUG_IF_V2_IMPL(bug_id, condition) \
+ QUIC_LOG_IF(ERROR, condition)
+
+#endif // NET_THIRD_PARTY_QUICHE_OVERRIDES_QUICHE_PLATFORM_IMPL_QUICHE_BUG_TRACKER_IMPL_H_
diff --git a/chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_logging_impl.h b/chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_logging_impl.h
index 8ff5e27f8ee..326ed930701 100644
--- a/chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_logging_impl.h
+++ b/chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_logging_impl.h
@@ -5,6 +5,8 @@
#ifndef NET_THIRD_PARTY_QUICHE_OVERRIDES_QUICHE_PLATFORM_IMPL_QUICHE_LOGGING_IMPL_H_
#define NET_THIRD_PARTY_QUICHE_OVERRIDES_QUICHE_PLATFORM_IMPL_QUICHE_LOGGING_IMPL_H_
+#include <vector>
+
#include "base/check_op.h"
#include "base/logging.h"
#include "base/notreached.h"
diff --git a/chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_thread_local_impl.h b/chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_thread_local_impl.h
new file mode 100644
index 00000000000..8be2972e5b6
--- /dev/null
+++ b/chromium/net/third_party/quiche/overrides/quiche_platform_impl/quiche_thread_local_impl.h
@@ -0,0 +1,27 @@
+// Copyright 2021 The Chromium 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_THIRD_PARTY_QUICHE_OVERRIDES_QUICHE_PLATFORM_IMPL_QUICHE_THREAD_LOCAL_IMPL_H_
+#define NET_THIRD_PARTY_QUICHE_OVERRIDES_QUICHE_PLATFORM_IMPL_QUICHE_THREAD_LOCAL_IMPL_H_
+
+#include "base/no_destructor.h"
+#include "base/threading/thread_local.h"
+
+#define DEFINE_QUICHE_THREAD_LOCAL_POINTER_IMPL(name, type) \
+ struct QuicheThreadLocalPointer_##name { \
+ static ::base::ThreadLocalPointer<type>* Instance() { \
+ static ::base::NoDestructor<::base::ThreadLocalPointer<type>> instance; \
+ return instance.get(); \
+ } \
+ static type* Get() { return Instance()->Get(); } \
+ static void Set(type* ptr) { Instance()->Set(ptr); } \
+ }
+
+#define GET_QUICHE_THREAD_LOCAL_POINTER_IMPL(name) \
+ QuicheThreadLocalPointer_##name::Get()
+
+#define SET_QUICHE_THREAD_LOCAL_POINTER_IMPL(name, value) \
+ QuicheThreadLocalPointer_##name::Set(value)
+
+#endif // NET_THIRD_PARTY_QUICHE_OVERRIDES_QUICHE_PLATFORM_IMPL_QUICHE_THREAD_LOCAL_IMPL_H_
diff --git a/chromium/net/third_party/quiche/src/CONTRIBUTING.md b/chromium/net/third_party/quiche/src/CONTRIBUTING.md
index 07824e04d4f..d8c6e48b7be 100644
--- a/chromium/net/third_party/quiche/src/CONTRIBUTING.md
+++ b/chromium/net/third_party/quiche/src/CONTRIBUTING.md
@@ -20,12 +20,13 @@ again.
The QUICHE repository is currently not set up to accept pull requests directly.
If you would like to make a contribution, please follow these steps:
-1. Sign the Contributor License Agreement (see above).
-2. Create a Gerrit pull request at <https://quiche-review.googlesource.com>.
-3. Email a link to your pull request to <quiche-contribution@google.com>.
-4. An engineer will review your pull request and merge it internally.
+1. Sign the Contributor License Agreement (see above).
+2. Create a Gerrit pull request at <https://quiche-review.googlesource.com>, or
+ a GitHub pull request at <https://github.com/google/quiche/pulls>.
+3. Email a link to your pull request to <quiche-contribution@google.com>.
+4. An engineer will review your pull request and merge it internally.
## Community Guidelines
-This project follows [Google's Open Source Community
-Guidelines](https://opensource.google.com/conduct/).
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/).
diff --git a/chromium/net/third_party/quiche/src/README.md b/chromium/net/third_party/quiche/src/README.md
index 0c441923f54..60f24da2b09 100644
--- a/chromium/net/third_party/quiche/src/README.md
+++ b/chromium/net/third_party/quiche/src/README.md
@@ -1,8 +1,28 @@
# QUICHE
-QUICHE (QUIC, Http/2, Etc) is Google's implementation of QUIC and related
-protocols. It powers Chromium as well as Google's QUIC servers and some other
-projects. QUICHE is only supported on little-endian platforms.
+QUICHE stands for QUIC, Http/2, Etc. It is Google's production-ready
+implementation of QUIC, HTTP/2, HTTP/3, and related protocols and tools. It
+powers Google's servers, Chromium, Envoy, and other projects. It is actively
+developed and maintained.
-Code can be viewed in CodeSearch in Quiche and is imported into
-[Chromium](https://source.chromium.org/chromium/chromium/src/+/master:net/third_party/quiche/src).
+There are two public QUICHE repositories. Either one may be used by embedders,
+as they are automatically kept in sync:
+
+* https://quiche.googlesource.com/quiche
+* https://github.com/google/quiche
+
+To embed QUICHE in your project, platform APIs need to be implemented and build
+files need to be created. Note that it is on the QUICHE team's roadmap to
+include default implementation for all platform APIs and to open-source build
+files. In the meanwhile, take a look at open source embedders like Chromium and
+Envoy to get started:
+
+* Platform implementations in Chromium:
+ + [quic/platform](https://source.chromium.org/chromium/chromium/src/+/main:net/net/quic/platform/impl/)
+ + [http2/platform](https://source.chromium.org/chromium/chromium/src/+/main:net/net/http2/platform/impl/)
+ + [quiche/common/platform](https://source.chromium.org/chromium/chromium/src/+/main:net/quiche/net/quiche/common/platform/impl/)
+* [Build file in Chromium](https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/BUILD.gn)
+* [Platform implementations in Envoy](https://github.com/envoyproxy/envoy/tree/master/source/common/quic/platform)
+* [Build file in Envoy](https://github.com/envoyproxy/envoy/blob/main/bazel/external/quiche.BUILD)
+
+QUICHE is only supported on little-endian platforms.
diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_bug_tracker.h b/chromium/net/third_party/quiche/src/common/platform/api/quiche_bug_tracker.h
index 0e8e456209a..27ac0d20948 100644
--- a/chromium/net/third_party/quiche/src/common/platform/api/quiche_bug_tracker.h
+++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_bug_tracker.h
@@ -5,7 +5,7 @@
#ifndef QUICHE_COMMON_PLATFORM_API_QUICHE_BUG_TRACKER_H_
#define QUICHE_COMMON_PLATFORM_API_QUICHE_BUG_TRACKER_H_
-#include "net/quiche/common/platform/impl/quiche_bug_tracker_impl.h"
+#include "quiche_platform_impl/quiche_bug_tracker_impl.h"
#define QUICHE_BUG QUICHE_BUG_IMPL
#define QUICHE_BUG_IF QUICHE_BUG_IF_IMPL
diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils.cc b/chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils.cc
new file mode 100644
index 00000000000..fc0c189382e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils.cc
@@ -0,0 +1,51 @@
+#include "common/platform/api/quiche_file_utils.h"
+
+#include "quiche_platform_impl/quiche_file_utils_impl.h"
+
+namespace quiche {
+
+std::string JoinPath(absl::string_view a, absl::string_view b) {
+ return JoinPathImpl(a, b);
+}
+
+absl::optional<std::string> ReadFileContents(absl::string_view file) {
+ return ReadFileContentsImpl(file);
+}
+
+bool EnumerateDirectory(absl::string_view path,
+ std::vector<std::string>& directories,
+ std::vector<std::string>& files) {
+ return EnumerateDirectoryImpl(path, directories, files);
+}
+
+bool EnumerateDirectoryRecursivelyInner(absl::string_view path,
+ int recursion_limit,
+ std::vector<std::string>& files) {
+ if (recursion_limit < 0) {
+ return false;
+ }
+
+ std::vector<std::string> local_files;
+ std::vector<std::string> directories;
+ if (!EnumerateDirectory(path, directories, local_files)) {
+ return false;
+ }
+ for (const std::string& directory : directories) {
+ if (!EnumerateDirectoryRecursivelyInner(JoinPath(path, directory),
+ recursion_limit - 1, files)) {
+ return false;
+ }
+ }
+ for (const std::string& file : local_files) {
+ files.push_back(JoinPath(path, file));
+ }
+ return true;
+}
+
+bool EnumerateDirectoryRecursively(absl::string_view path,
+ std::vector<std::string>& files) {
+ constexpr int kRecursionLimit = 20;
+ return EnumerateDirectoryRecursivelyInner(path, kRecursionLimit, files);
+}
+
+} // namespace quiche
diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils.h b/chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils.h
new file mode 100644
index 00000000000..47723d19e92
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils.h
@@ -0,0 +1,40 @@
+// Copyright 2021 The Chromium 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 header contains basic filesystem functions for use in unit tests and CLI
+// tools. Note that those are not 100% suitable for production use, as in, they
+// might be prone to race conditions and not always handle non-ASCII filenames
+// correctly.
+#ifndef QUICHE_COMMON_PLATFORM_API_QUICHE_FILE_UTILS_H_
+#define QUICHE_COMMON_PLATFORM_API_QUICHE_FILE_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+
+namespace quiche {
+
+// Join two paths in a platform-specific way. Returns |a| if |b| is empty, and
+// vice versa.
+std::string JoinPath(absl::string_view a, absl::string_view b);
+
+// Reads the entire file into the memory.
+absl::optional<std::string> ReadFileContents(absl::string_view file);
+
+// Lists all files and directories in the directory specified by |path|. Returns
+// true on success, false on failure.
+bool EnumerateDirectory(absl::string_view path,
+ std::vector<std::string>& directories,
+ std::vector<std::string>& files);
+
+// Recursively enumerates all of the files in the directory and all of the
+// internal subdirectories. Has a fairly small recursion limit.
+bool EnumerateDirectoryRecursively(absl::string_view path,
+ std::vector<std::string>& files);
+
+} // namespace quiche
+
+#endif // QUICHE_COMMON_PLATFORM_API_QUICHE_FILE_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils_test.cc b/chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils_test.cc
new file mode 100644
index 00000000000..ddf1a4000b1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_file_utils_test.cc
@@ -0,0 +1,86 @@
+#include "common/platform/api/quiche_file_utils.h"
+
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/strip.h"
+#include "absl/types/optional.h"
+#include "common/platform/api/quiche_test.h"
+
+namespace quiche {
+namespace test {
+namespace {
+
+using testing::UnorderedElementsAre;
+using testing::UnorderedElementsAreArray;
+
+TEST(QuicheFileUtilsTest, ReadFileContents) {
+ std::string path = absl::StrCat(QuicheGetCommonSourcePath(),
+ "/platform/api/testdir/testfile");
+ absl::optional<std::string> contents = ReadFileContents(path);
+ ASSERT_TRUE(contents.has_value());
+ EXPECT_EQ(*contents, "This is a test file.");
+}
+
+TEST(QuicheFileUtilsTest, ReadFileContentsFileNotFound) {
+ std::string path =
+ absl::StrCat(QuicheGetCommonSourcePath(),
+ "/platform/api/testdir/file-that-does-not-exist");
+ absl::optional<std::string> contents = ReadFileContents(path);
+ EXPECT_FALSE(contents.has_value());
+}
+
+TEST(QuicheFileUtilsTest, EnumerateDirectory) {
+ std::string path =
+ absl::StrCat(QuicheGetCommonSourcePath(), "/platform/api/testdir");
+ std::vector<std::string> dirs;
+ std::vector<std::string> files;
+ bool success = EnumerateDirectory(path, dirs, files);
+ EXPECT_TRUE(success);
+ EXPECT_THAT(files, UnorderedElementsAre("testfile", "README.md"));
+ EXPECT_THAT(dirs, UnorderedElementsAre("a"));
+}
+
+TEST(QuicheFileUtilsTest, EnumerateDirectoryNoSuchDirectory) {
+ std::string path = absl::StrCat(QuicheGetCommonSourcePath(),
+ "/platform/api/testdir/no-such-directory");
+ std::vector<std::string> dirs;
+ std::vector<std::string> files;
+ bool success = EnumerateDirectory(path, dirs, files);
+ EXPECT_FALSE(success);
+}
+
+TEST(QuicheFileUtilsTest, EnumerateDirectoryNotADirectory) {
+ std::string path = absl::StrCat(QuicheGetCommonSourcePath(),
+ "/platform/api/testdir/testfile");
+ std::vector<std::string> dirs;
+ std::vector<std::string> files;
+ bool success = EnumerateDirectory(path, dirs, files);
+ EXPECT_FALSE(success);
+}
+
+TEST(QuicheFileUtilsTest, EnumerateDirectoryRecursively) {
+ std::vector<std::string> expected_paths = {"a/b/c/d/e", "a/subdir/testfile",
+ "a/z", "testfile", "README.md"};
+
+ std::string root_path =
+ absl::StrCat(QuicheGetCommonSourcePath(), "/platform/api/testdir");
+ for (std::string& path : expected_paths) {
+ // For Windows, use Windows path separators.
+ if (JoinPath("a", "b") == "a\\b") {
+ absl::c_replace(path, '/', '\\');
+ }
+
+ path = JoinPath(root_path, path);
+ }
+
+ std::vector<std::string> files;
+ bool success = EnumerateDirectoryRecursively(root_path, files);
+ EXPECT_TRUE(success);
+ EXPECT_THAT(files, UnorderedElementsAreArray(expected_paths));
+}
+
+} // namespace
+} // namespace test
+} // namespace quiche
diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_test.h b/chromium/net/third_party/quiche/src/common/platform/api/quiche_test.h
index 1af25df1ea5..efec6384db3 100644
--- a/chromium/net/third_party/quiche/src/common/platform/api/quiche_test.h
+++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_test.h
@@ -12,6 +12,18 @@ using QuicheTest = quiche::test::QuicheTest;
template <class T>
using QuicheTestWithParam = quiche::test::QuicheTestWithParamImpl<T>;
+namespace quiche {
+namespace test {
+
+// Returns the path to quiche/common directory where the test data could be
+// located.
+inline std::string QuicheGetCommonSourcePath() {
+ return QuicheGetCommonSourcePathImpl();
+}
+
+} // namespace test
+} // namespace quiche
+
#define EXPECT_QUICHE_DEBUG_DEATH(condition, message) \
EXPECT_QUICHE_DEBUG_DEATH_IMPL(condition, message)
diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_thread_local.h b/chromium/net/third_party/quiche/src/common/platform/api/quiche_thread_local.h
new file mode 100644
index 00000000000..c2d58e1dd5e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_thread_local.h
@@ -0,0 +1,27 @@
+// Copyright 2021 The Chromium 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 QUICHE_COMMON_PLATFORM_API_QUICHE_THREAD_LOCAL_H_
+#define QUICHE_COMMON_PLATFORM_API_QUICHE_THREAD_LOCAL_H_
+
+#include "quiche_platform_impl/quiche_thread_local_impl.h"
+
+// Define a thread local |type*| with |name|. Conceptually, this is a
+//
+// static thread_local type* name = nullptr;
+//
+// It is wrapped in a macro because the thread_local keyword is banned from
+// Chromium.
+#define DEFINE_QUICHE_THREAD_LOCAL_POINTER(name, type) \
+ DEFINE_QUICHE_THREAD_LOCAL_POINTER_IMPL(name, type)
+
+// Get the value of |name| for the current thread.
+#define GET_QUICHE_THREAD_LOCAL_POINTER(name) \
+ GET_QUICHE_THREAD_LOCAL_POINTER_IMPL(name)
+
+// Set the |value| of |name| for the current thread.
+#define SET_QUICHE_THREAD_LOCAL_POINTER(name, value) \
+ SET_QUICHE_THREAD_LOCAL_POINTER_IMPL(name, value)
+
+#endif // QUICHE_COMMON_PLATFORM_API_QUICHE_THREAD_LOCAL_H_
diff --git a/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_bug_tracker_impl.h b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_bug_tracker_impl.h
new file mode 100644
index 00000000000..de164aac15e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_bug_tracker_impl.h
@@ -0,0 +1,15 @@
+// Copyright 2021 The Chromium 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 QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_BUG_TRACKER_IMPL_H_
+#define QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_BUG_TRACKER_IMPL_H_
+
+#include "common/platform/api/quiche_logging.h"
+
+#define QUICHE_BUG_IMPL(b) QUICHE_LOG(DFATAL)
+#define QUICHE_BUG_IF_IMPL(b, condition) QUICHE_LOG_IF(DFATAL, condition)
+#define QUICHE_PEER_BUG_IMPL(b) QUICHE_LOG(DFATAL)
+#define QUICHE_PEER_BUG_IF_IMPL(b, condition) QUICHE_LOG_IF(DFATAL, condition)
+
+#endif // QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_BUG_TRACKER_IMPL_H_
diff --git a/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_containers_impl.h b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_containers_impl.h
deleted file mode 100644
index 2e18dd7a2f8..00000000000
--- a/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_containers_impl.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2021 The Chromium 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 QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_CONTAINERS_IMPL_H_
-#define QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_CONTAINERS_IMPL_H_
-
-#include <deque>
-
-namespace quiche {
-
-// Represents a double-ended queue which may be backed by a list or a flat
-// circular buffer.
-//
-// DOES NOT GUARANTEE POINTER OR ITERATOR STABILITY!
-template <typename T>
-using QuicheDequeImpl = std::deque<T>;
-
-} // namespace quiche
-
-#endif // QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_CONTAINERS_IMPL_H_
diff --git a/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_estimate_memory_usage_impl.h b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_estimate_memory_usage_impl.h
deleted file mode 100644
index 59087a725eb..00000000000
--- a/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_estimate_memory_usage_impl.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 The Chromium 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 QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_ESTIMATE_MEMORY_USAGE_IMPL_H_
-#define QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_ESTIMATE_MEMORY_USAGE_IMPL_H_
-
-#include <cstddef>
-
-namespace quiche {
-
-// No-op implementation.
-template <class T>
-size_t QuicheEstimateMemoryUsageImpl(const T& /*object*/) {
- return 0;
-}
-
-} // namespace quiche
-
-#endif // QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_ESTIMATE_MEMORY_USAGE_IMPL_H_
diff --git a/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_file_utils_impl.cc b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_file_utils_impl.cc
new file mode 100644
index 00000000000..65965b222d5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_file_utils_impl.cc
@@ -0,0 +1,182 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche_platform_impl/quiche_file_utils_impl.h"
+
+#if defined(_WIN32)
+#include <windows.h>
+#else
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif // defined(_WIN32)
+
+#include <fstream>
+#include <ios>
+#include <iostream>
+
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/strings/strip.h"
+#include "absl/types/optional.h"
+
+namespace quiche {
+
+#if defined(_WIN32)
+std::string JoinPathImpl(absl::string_view a, absl::string_view b) {
+ if (a.empty()) {
+ return std::string(b);
+ }
+ if (b.empty()) {
+ return std::string(a);
+ }
+ // Win32 actually provides two different APIs for combining paths; one of them
+ // has issues that could potentially lead to buffer overflow, and another is
+ // not supported in Windows 7, which is why we're doing it manually.
+ a = absl::StripSuffix(a, "/");
+ a = absl::StripSuffix(a, "\\");
+ return absl::StrCat(a, "\\", b);
+}
+#else
+std::string JoinPathImpl(absl::string_view a, absl::string_view b) {
+ if (a.empty()) {
+ return std::string(b);
+ }
+ if (b.empty()) {
+ return std::string(a);
+ }
+ return absl::StrCat(absl::StripSuffix(a, "/"), "/", b);
+}
+#endif // defined(_WIN32)
+
+absl::optional<std::string> ReadFileContentsImpl(absl::string_view file) {
+ std::ifstream input_file(std::string{file}, std::ios::binary);
+ if (!input_file || !input_file.is_open()) {
+ return absl::nullopt;
+ }
+
+ input_file.seekg(0, std::ios_base::end);
+ auto file_size = input_file.tellg();
+ if (!input_file) {
+ return absl::nullopt;
+ }
+ input_file.seekg(0, std::ios_base::beg);
+
+ std::string output;
+ output.resize(file_size);
+ input_file.read(&output[0], file_size);
+ if (!input_file) {
+ return absl::nullopt;
+ }
+
+ return output;
+}
+
+#if defined(_WIN32)
+
+class ScopedDir {
+ public:
+ ScopedDir(HANDLE dir) : dir_(dir) {}
+ ~ScopedDir() {
+ if (dir_ != INVALID_HANDLE_VALUE) {
+ // The API documentation explicitly says that CloseHandle() should not be
+ // used on directory search handles.
+ FindClose(dir_);
+ dir_ = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ HANDLE get() { return dir_; }
+
+ private:
+ HANDLE dir_;
+};
+
+bool EnumerateDirectoryImpl(absl::string_view path,
+ std::vector<std::string>& directories,
+ std::vector<std::string>& files) {
+ std::string path_owned(path);
+
+ // Explicitly check that the directory we are trying to search is in fact a
+ // directory.
+ DWORD attributes = GetFileAttributesA(path_owned.c_str());
+ if (attributes == INVALID_FILE_ATTRIBUTES) {
+ return false;
+ }
+ if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ return false;
+ }
+
+ std::string search_path = JoinPathImpl(path, "*");
+ WIN32_FIND_DATAA file_data;
+ ScopedDir dir(FindFirstFileA(search_path.c_str(), &file_data));
+ if (dir.get() == INVALID_HANDLE_VALUE) {
+ return GetLastError() == ERROR_FILE_NOT_FOUND;
+ }
+ do {
+ std::string filename(file_data.cFileName);
+ if (filename == "." || filename == "..") {
+ continue;
+ }
+ if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ directories.push_back(std::move(filename));
+ } else {
+ files.push_back(std::move(filename));
+ }
+ } while (FindNextFileA(dir.get(), &file_data));
+ return GetLastError() == ERROR_NO_MORE_FILES;
+}
+
+#else // defined(_WIN32)
+
+class ScopedDir {
+ public:
+ ScopedDir(DIR* dir) : dir_(dir) {}
+ ~ScopedDir() {
+ if (dir_ != nullptr) {
+ closedir(dir_);
+ dir_ = nullptr;
+ }
+ }
+
+ DIR* get() { return dir_; }
+
+ private:
+ DIR* dir_;
+};
+
+bool EnumerateDirectoryImpl(absl::string_view path,
+ std::vector<std::string>& directories,
+ std::vector<std::string>& files) {
+ std::string path_owned(path);
+ ScopedDir dir(opendir(path_owned.c_str()));
+ if (dir.get() == nullptr) {
+ return false;
+ }
+
+ dirent* entry;
+ while ((entry = readdir(dir.get()))) {
+ const std::string filename(entry->d_name);
+ if (filename == "." || filename == "..") {
+ continue;
+ }
+
+ const std::string entry_path = JoinPathImpl(path, filename);
+ struct stat stat_entry;
+ if (stat(entry_path.c_str(), &stat_entry) != 0) {
+ return false;
+ }
+ if (S_ISREG(stat_entry.st_mode)) {
+ files.push_back(std::move(filename));
+ } else if (S_ISDIR(stat_entry.st_mode)) {
+ directories.push_back(std::move(filename));
+ }
+ }
+ return true;
+}
+
+#endif // defined(_WIN32)
+
+} // namespace quiche
diff --git a/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_file_utils_impl.h b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_file_utils_impl.h
new file mode 100644
index 00000000000..ad5ff1a9084
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_file_utils_impl.h
@@ -0,0 +1,26 @@
+// Copyright 2021 The Chromium 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 QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_FILE_UTILS_IMPL_H_
+#define QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_FILE_UTILS_IMPL_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+
+namespace quiche {
+
+std::string JoinPathImpl(absl::string_view a, absl::string_view b);
+
+absl::optional<std::string> ReadFileContentsImpl(absl::string_view file);
+
+bool EnumerateDirectoryImpl(absl::string_view path,
+ std::vector<std::string>& directories,
+ std::vector<std::string>& files);
+
+} // namespace quiche
+
+#endif // QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_FILE_UTILS_IMPL_H_
diff --git a/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_thread_local_impl.h b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_thread_local_impl.h
new file mode 100644
index 00000000000..5ebea4c5bfb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_thread_local_impl.h
@@ -0,0 +1,24 @@
+// Copyright 2021 The Chromium 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 QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_THREAD_LOCAL_IMPL_H_
+#define QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_THREAD_LOCAL_IMPL_H_
+
+#define DEFINE_QUICHE_THREAD_LOCAL_POINTER_IMPL(name, type) \
+ struct QuicheThreadLocalPointer_##name { \
+ static type** Instance() { \
+ static thread_local type* instance = nullptr; \
+ return &instance; \
+ } \
+ static type* Get() { return *Instance(); } \
+ static void Set(type* ptr) { *Instance() = ptr; } \
+ }
+
+#define GET_QUICHE_THREAD_LOCAL_POINTER_IMPL(name) \
+ QuicheThreadLocalPointer_##name::Get()
+
+#define SET_QUICHE_THREAD_LOCAL_POINTER_IMPL(name, value) \
+ QuicheThreadLocalPointer_##name::Set(value)
+
+#endif // QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_THREAD_LOCAL_IMPL_H_
diff --git a/chromium/net/third_party/quiche/src/common/print_elements.h b/chromium/net/third_party/quiche/src/common/print_elements.h
new file mode 100644
index 00000000000..f241a4b9509
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/common/print_elements.h
@@ -0,0 +1,35 @@
+// Copyright 2021 The Chromium 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 QUICHE_COMMON_PRINT_ELEMENTS_H_
+#define QUICHE_COMMON_PRINT_ELEMENTS_H_
+
+#include <ostream>
+#include <sstream>
+#include <string>
+
+#include "common/platform/api/quiche_export.h"
+
+namespace quiche {
+
+// Print elements of any iterable container that has cbegin() and cend() methods
+// and the elements have operator<<(ostream) override.
+template <typename T>
+QUICHE_EXPORT_PRIVATE inline std::string PrintElements(const T& container) {
+ std::stringstream debug_string;
+ debug_string << "{";
+ auto it = container.cbegin();
+ debug_string << *it;
+ ++it;
+ while (it != container.cend()) {
+ debug_string << ", " << *it;
+ ++it;
+ }
+ debug_string << "}";
+ return debug_string.str();
+}
+
+} // namespace quiche
+
+#endif // QUICHE_COMMON_PRINT_ELEMENTS_H_
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.cc b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.cc
index eaf4b6169d2..4381749d430 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.cc
@@ -36,14 +36,63 @@ struct nghttp2_session_callbacks {
namespace http2 {
namespace adapter {
+CallbackVisitor::CallbackVisitor(Perspective perspective,
+ const nghttp2_session_callbacks& callbacks,
+ void* user_data)
+ : perspective_(perspective),
+ callbacks_(MakeCallbacksPtr(nullptr)),
+ user_data_(user_data) {
+ nghttp2_session_callbacks* c;
+ nghttp2_session_callbacks_new(&c);
+ *c = callbacks;
+ callbacks_ = MakeCallbacksPtr(c);
+}
+
+ssize_t CallbackVisitor::OnReadyToSend(absl::string_view serialized) {
+ if (!callbacks_->send_callback) {
+ return kSendError;
+ }
+ ssize_t result = callbacks_->send_callback(
+ nullptr, ToUint8Ptr(serialized.data()), serialized.size(), 0, user_data_);
+ QUICHE_VLOG(1) << "CallbackVisitor::OnReadyToSend returning " << result;
+ if (result > 0) {
+ return result;
+ } else if (result == NGHTTP2_ERR_WOULDBLOCK) {
+ return kSendBlocked;
+ } else {
+ return kSendError;
+ }
+}
+
void CallbackVisitor::OnConnectionError() {
- QUICHE_LOG(FATAL) << "Not implemented";
+ QUICHE_LOG(ERROR) << "OnConnectionError not implemented";
}
void CallbackVisitor::OnFrameHeader(Http2StreamId stream_id,
size_t length,
uint8_t type,
uint8_t flags) {
+ QUICHE_VLOG(1) << "CallbackVisitor::OnFrameHeader(stream_id: " << stream_id
+ << ", len: " << length << ", type: " << int(type)
+ << ", flags: " << int(flags) << ")";
+ if (static_cast<FrameType>(type) == FrameType::CONTINUATION) {
+ // Treat CONTINUATION as HEADERS
+ QUICHE_DCHECK_EQ(current_frame_.hd.stream_id, stream_id);
+ current_frame_.hd.length += length;
+ current_frame_.hd.flags |= flags;
+ QUICHE_DLOG_IF(ERROR, length == 0) << "Empty CONTINUATION!";
+ // Still need to deliver the CONTINUATION to the begin frame callback.
+ nghttp2_frame_hd hd;
+ memset(&hd, 0, sizeof(hd));
+ hd.stream_id = stream_id;
+ hd.length = length;
+ hd.type = type;
+ hd.flags = flags;
+ if (callbacks_->on_begin_frame_callback) {
+ callbacks_->on_begin_frame_callback(nullptr, &hd, user_data_);
+ }
+ return;
+ }
// The general strategy is to clear |current_frame_| at the start of a new
// frame, accumulate frame information from the various callback events, then
// invoke the on_frame_recv_callback() with the accumulated frame data.
@@ -52,7 +101,10 @@ void CallbackVisitor::OnFrameHeader(Http2StreamId stream_id,
current_frame_.hd.length = length;
current_frame_.hd.type = type;
current_frame_.hd.flags = flags;
- callbacks_->on_begin_frame_callback(nullptr, &current_frame_.hd, user_data_);
+ if (callbacks_->on_begin_frame_callback) {
+ callbacks_->on_begin_frame_callback(nullptr, &current_frame_.hd,
+ user_data_);
+ }
}
void CallbackVisitor::OnSettingsStart() {}
@@ -64,106 +116,147 @@ void CallbackVisitor::OnSetting(Http2Setting setting) {
void CallbackVisitor::OnSettingsEnd() {
current_frame_.settings.niv = settings_.size();
current_frame_.settings.iv = settings_.data();
- callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ QUICHE_VLOG(1) << "OnSettingsEnd, received settings of size "
+ << current_frame_.settings.niv;
+ if (callbacks_->on_frame_recv_callback) {
+ callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ }
settings_.clear();
}
void CallbackVisitor::OnSettingsAck() {
// ACK is part of the flags, which were set in OnFrameHeader().
- callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ if (callbacks_->on_frame_recv_callback) {
+ callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ }
}
-void CallbackVisitor::OnBeginHeadersForStream(Http2StreamId stream_id) {
- auto it = stream_map_.find(stream_id);
- if (it == stream_map_.end()) {
- auto p = stream_map_.insert({stream_id, absl::make_unique<StreamInfo>()});
- it = p.first;
- }
+bool CallbackVisitor::OnBeginHeadersForStream(Http2StreamId stream_id) {
+ auto it = GetStreamInfo(stream_id);
if (it->second->received_headers) {
// At least one headers frame has already been received.
+ QUICHE_VLOG(1)
+ << "Headers already received for stream " << stream_id
+ << ", these are trailers or headers following a 100 response";
current_frame_.headers.cat = NGHTTP2_HCAT_HEADERS;
} else {
switch (perspective_) {
case Perspective::kClient:
+ QUICHE_VLOG(1) << "First headers at the client for stream " << stream_id
+ << "; these are response headers";
current_frame_.headers.cat = NGHTTP2_HCAT_RESPONSE;
break;
case Perspective::kServer:
+ QUICHE_VLOG(1) << "First headers at the server for stream " << stream_id
+ << "; these are request headers";
current_frame_.headers.cat = NGHTTP2_HCAT_REQUEST;
break;
}
}
- callbacks_->on_begin_headers_callback(nullptr, &current_frame_, user_data_);
it->second->received_headers = true;
+ if (callbacks_->on_begin_headers_callback) {
+ const int result = callbacks_->on_begin_headers_callback(
+ nullptr, &current_frame_, user_data_);
+ return result == 0;
+ }
+ return true;
}
-void CallbackVisitor::OnHeaderForStream(Http2StreamId stream_id,
- absl::string_view name,
- absl::string_view value) {
- callbacks_->on_header_callback(
- nullptr, &current_frame_, ToUint8Ptr(name.data()), name.size(),
- ToUint8Ptr(value.data()), value.size(), NGHTTP2_NV_FLAG_NONE, user_data_);
+Http2VisitorInterface::OnHeaderResult CallbackVisitor::OnHeaderForStream(
+ Http2StreamId /*stream_id*/, absl::string_view name,
+ absl::string_view value) {
+ if (callbacks_->on_header_callback) {
+ const int result = callbacks_->on_header_callback(
+ nullptr, &current_frame_, ToUint8Ptr(name.data()), name.size(),
+ ToUint8Ptr(value.data()), value.size(), NGHTTP2_NV_FLAG_NONE,
+ user_data_);
+ if (result == 0) {
+ return HEADER_OK;
+ } else if (result == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
+ return HEADER_RST_STREAM;
+ } else {
+ // Assume NGHTTP2_ERR_CALLBACK_FAILURE.
+ return HEADER_CONNECTION_ERROR;
+ }
+ }
+ return HEADER_OK;
}
-void CallbackVisitor::OnEndHeadersForStream(Http2StreamId stream_id) {
- callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+void CallbackVisitor::OnEndHeadersForStream(Http2StreamId /*stream_id*/) {
+ if (callbacks_->on_frame_recv_callback) {
+ const int result = callbacks_->on_frame_recv_callback(
+ nullptr, &current_frame_, user_data_);
+ QUICHE_DCHECK_EQ(0, result);
+ }
}
-void CallbackVisitor::OnBeginDataForStream(Http2StreamId stream_id,
+void CallbackVisitor::OnBeginDataForStream(Http2StreamId /*stream_id*/,
size_t payload_length) {
// TODO(b/181586191): Interpret padding, subtract padding from
// |remaining_data_|.
remaining_data_ = payload_length;
- if (remaining_data_ == 0) {
+ if (remaining_data_ == 0 && callbacks_->on_frame_recv_callback) {
callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
}
}
void CallbackVisitor::OnDataForStream(Http2StreamId stream_id,
absl::string_view data) {
- callbacks_->on_data_chunk_recv_callback(nullptr, current_frame_.hd.flags,
- stream_id, ToUint8Ptr(data.data()),
- data.size(), user_data_);
+ if (callbacks_->on_data_chunk_recv_callback) {
+ callbacks_->on_data_chunk_recv_callback(nullptr, current_frame_.hd.flags,
+ stream_id, ToUint8Ptr(data.data()),
+ data.size(), user_data_);
+ }
remaining_data_ -= data.size();
- if (remaining_data_ == 0) {
+ if (remaining_data_ == 0 && callbacks_->on_frame_recv_callback) {
callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
}
}
-void CallbackVisitor::OnEndStream(Http2StreamId stream_id) {}
+void CallbackVisitor::OnEndStream(Http2StreamId /*stream_id*/) {}
-void CallbackVisitor::OnRstStream(Http2StreamId stream_id,
+void CallbackVisitor::OnRstStream(Http2StreamId /*stream_id*/,
Http2ErrorCode error_code) {
current_frame_.rst_stream.error_code = static_cast<uint32_t>(error_code);
- callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ if (callbacks_->on_frame_recv_callback) {
+ callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ }
}
void CallbackVisitor::OnCloseStream(Http2StreamId stream_id,
Http2ErrorCode error_code) {
- callbacks_->on_stream_close_callback(
- nullptr, stream_id, static_cast<uint32_t>(error_code), user_data_);
+ if (callbacks_->on_stream_close_callback) {
+ QUICHE_VLOG(1) << "OnCloseStream(stream_id: " << stream_id
+ << ", error_code: " << int(error_code) << ")";
+ callbacks_->on_stream_close_callback(
+ nullptr, stream_id, static_cast<uint32_t>(error_code), user_data_);
+ }
}
-void CallbackVisitor::OnPriorityForStream(Http2StreamId stream_id,
+void CallbackVisitor::OnPriorityForStream(Http2StreamId /*stream_id*/,
Http2StreamId parent_stream_id,
- int weight,
- bool exclusive) {
+ int weight, bool exclusive) {
current_frame_.priority.pri_spec.stream_id = parent_stream_id;
current_frame_.priority.pri_spec.weight = weight;
current_frame_.priority.pri_spec.exclusive = exclusive;
- callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ if (callbacks_->on_frame_recv_callback) {
+ callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ }
}
-void CallbackVisitor::OnPing(Http2PingId ping_id, bool is_ack) {
+void CallbackVisitor::OnPing(Http2PingId ping_id, bool /*is_ack*/) {
uint64_t network_order_opaque_data =
quiche::QuicheEndian::HostToNet64(ping_id);
std::memcpy(current_frame_.ping.opaque_data, &network_order_opaque_data,
sizeof(network_order_opaque_data));
- callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ if (callbacks_->on_frame_recv_callback) {
+ callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ }
}
-void CallbackVisitor::OnPushPromiseForStream(Http2StreamId stream_id,
- Http2StreamId promised_stream_id) {
- QUICHE_LOG(FATAL) << "Not implemented";
+void CallbackVisitor::OnPushPromiseForStream(
+ Http2StreamId /*stream_id*/, Http2StreamId /*promised_stream_id*/) {
+ QUICHE_LOG(DFATAL) << "Not implemented";
}
void CallbackVisitor::OnGoAway(Http2StreamId last_accepted_stream_id,
@@ -173,42 +266,173 @@ void CallbackVisitor::OnGoAway(Http2StreamId last_accepted_stream_id,
current_frame_.goaway.error_code = static_cast<uint32_t>(error_code);
current_frame_.goaway.opaque_data = ToUint8Ptr(opaque_data.data());
current_frame_.goaway.opaque_data_len = opaque_data.size();
- callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ if (callbacks_->on_frame_recv_callback) {
+ callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ }
}
-void CallbackVisitor::OnWindowUpdate(Http2StreamId stream_id,
+void CallbackVisitor::OnWindowUpdate(Http2StreamId /*stream_id*/,
int window_increment) {
current_frame_.window_update.window_size_increment = window_increment;
- callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ if (callbacks_->on_frame_recv_callback) {
+ callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ }
+}
+
+void CallbackVisitor::PopulateFrame(nghttp2_frame& frame, uint8_t frame_type,
+ Http2StreamId stream_id, size_t length,
+ uint8_t flags, uint32_t error_code,
+ bool sent_headers) {
+ frame.hd.type = frame_type;
+ frame.hd.stream_id = stream_id;
+ frame.hd.length = length;
+ frame.hd.flags = flags;
+ const FrameType frame_type_enum = static_cast<FrameType>(frame_type);
+ if (frame_type_enum == FrameType::HEADERS) {
+ if (sent_headers) {
+ frame.headers.cat = NGHTTP2_HCAT_HEADERS;
+ } else {
+ switch (perspective_) {
+ case Perspective::kClient:
+ QUICHE_VLOG(1) << "First headers sent by the client for stream "
+ << stream_id << "; these are request headers";
+ frame.headers.cat = NGHTTP2_HCAT_REQUEST;
+ break;
+ case Perspective::kServer:
+ QUICHE_VLOG(1) << "First headers sent by the server for stream "
+ << stream_id << "; these are response headers";
+ frame.headers.cat = NGHTTP2_HCAT_RESPONSE;
+ break;
+ }
+ }
+ } else if (frame_type_enum == FrameType::RST_STREAM) {
+ frame.rst_stream.error_code = error_code;
+ } else if (frame_type_enum == FrameType::GOAWAY) {
+ frame.goaway.error_code = error_code;
+ }
}
-void CallbackVisitor::OnReadyToSendDataForStream(Http2StreamId stream_id,
- char* destination_buffer,
- size_t length,
- ssize_t* written,
- bool* end_stream) {
- QUICHE_LOG(FATAL) << "Not implemented";
+int CallbackVisitor::OnBeforeFrameSent(uint8_t frame_type,
+ Http2StreamId stream_id, size_t length,
+ uint8_t flags) {
+ if (callbacks_->before_frame_send_callback) {
+ QUICHE_VLOG(1) << "OnBeforeFrameSent(type=" << int(frame_type)
+ << ", stream_id=" << stream_id << ", length=" << length
+ << ", flags=" << int(flags) << ")";
+ nghttp2_frame frame;
+ auto it = GetStreamInfo(stream_id);
+ // The implementation of the before_frame_send_callback doesn't look at the
+ // error code, so for now it's populated with 0.
+ PopulateFrame(frame, frame_type, stream_id, length, flags, /*error_code=*/0,
+ it->second->before_sent_headers);
+ it->second->before_sent_headers = true;
+ return callbacks_->before_frame_send_callback(nullptr, &frame, user_data_);
+ }
+ return 0;
+}
+
+int CallbackVisitor::OnFrameSent(uint8_t frame_type, Http2StreamId stream_id,
+ size_t length, uint8_t flags,
+ uint32_t error_code) {
+ if (callbacks_->on_frame_send_callback) {
+ QUICHE_VLOG(1) << "OnFrameSent(type=" << int(frame_type)
+ << ", stream_id=" << stream_id << ", length=" << length
+ << ", flags=" << int(flags) << ", error_code=" << error_code
+ << ")";
+ nghttp2_frame frame;
+ auto it = GetStreamInfo(stream_id);
+ PopulateFrame(frame, frame_type, stream_id, length, flags, error_code,
+ it->second->sent_headers);
+ it->second->sent_headers = true;
+ return callbacks_->on_frame_send_callback(nullptr, &frame, user_data_);
+ }
+ return 0;
+}
+
+void CallbackVisitor::OnReadyToSendDataForStream(Http2StreamId /*stream_id*/,
+ char* /*destination_buffer*/,
+ size_t /*length*/,
+ ssize_t* /*written*/,
+ bool* /*end_stream*/) {
+ QUICHE_LOG(DFATAL) << "Not implemented";
+}
+
+bool CallbackVisitor::OnInvalidFrame(Http2StreamId stream_id, int error_code) {
+ QUICHE_VLOG(1) << "OnInvalidFrame(" << stream_id << ", " << error_code << ")";
+ QUICHE_DCHECK_EQ(stream_id, current_frame_.hd.stream_id);
+ if (callbacks_->on_invalid_frame_recv_callback) {
+ return 0 == callbacks_->on_invalid_frame_recv_callback(
+ nullptr, &current_frame_, error_code, user_data_);
+ }
+ return true;
}
void CallbackVisitor::OnReadyToSendMetadataForStream(Http2StreamId stream_id,
char* buffer,
size_t length,
ssize_t* written) {
- QUICHE_LOG(FATAL) << "Not implemented";
+ if (callbacks_->pack_extension_callback) {
+ nghttp2_frame frame;
+ frame.hd.type = kMetadataFrameType;
+ frame.hd.stream_id = stream_id;
+ frame.hd.flags = 0;
+ frame.hd.length = 0;
+ *written = callbacks_->pack_extension_callback(nullptr, ToUint8Ptr(buffer),
+ length, &frame, user_data_);
+ }
+ QUICHE_VLOG(1) << "OnReadyToSendMetadataForStream(stream_id=" << stream_id
+ << ", length=" << length << ", written=" << *written << ")";
}
void CallbackVisitor::OnBeginMetadataForStream(Http2StreamId stream_id,
size_t payload_length) {
- QUICHE_LOG(FATAL) << "Not implemented";
+ QUICHE_VLOG(1) << "OnBeginMetadataForStream(stream_id=" << stream_id
+ << ", payload_length=" << payload_length << ")";
}
void CallbackVisitor::OnMetadataForStream(Http2StreamId stream_id,
absl::string_view metadata) {
- QUICHE_LOG(FATAL) << "Not implemented";
+ QUICHE_VLOG(1) << "OnMetadataForStream(stream_id=" << stream_id
+ << ", len=" << metadata.size() << ")";
+ if (callbacks_->on_extension_chunk_recv_callback) {
+ int result = callbacks_->on_extension_chunk_recv_callback(
+ nullptr, &current_frame_.hd, ToUint8Ptr(metadata.data()),
+ metadata.size(), user_data_);
+ QUICHE_DCHECK_EQ(0, result);
+ }
+}
+
+bool CallbackVisitor::OnMetadataEndForStream(Http2StreamId stream_id) {
+ QUICHE_LOG_IF(DFATAL, current_frame_.hd.flags != kMetadataEndFlag);
+ QUICHE_VLOG(1) << "OnMetadataEndForStream(stream_id=" << stream_id << ")";
+ if (callbacks_->unpack_extension_callback) {
+ void* payload;
+ int result = callbacks_->unpack_extension_callback(
+ nullptr, &payload, &current_frame_.hd, user_data_);
+ if (callbacks_->on_frame_recv_callback) {
+ current_frame_.ext.payload = payload;
+ callbacks_->on_frame_recv_callback(nullptr, &current_frame_, user_data_);
+ }
+ return (result == 0);
+ }
+ return true;
}
-void CallbackVisitor::OnMetadataEndForStream(Http2StreamId stream_id) {
- QUICHE_LOG(FATAL) << "Not implemented";
+void CallbackVisitor::OnErrorDebug(absl::string_view message) {
+ if (callbacks_->error_callback2) {
+ callbacks_->error_callback2(nullptr, -1, message.data(), message.size(),
+ user_data_);
+ }
+}
+
+CallbackVisitor::StreamInfoMap::iterator CallbackVisitor::GetStreamInfo(
+ Http2StreamId stream_id) {
+ auto it = stream_map_.find(stream_id);
+ if (it == stream_map_.end()) {
+ auto p = stream_map_.insert({stream_id, absl::make_unique<StreamInfo>()});
+ it = p.first;
+ }
+ return it;
}
} // namespace adapter
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.h b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.h
index 94746bf8edb..b5afc83a103 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.h
@@ -8,21 +8,20 @@
#include "http2/adapter/http2_visitor_interface.h"
#include "http2/adapter/nghttp2_util.h"
#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+#include "common/platform/api/quiche_export.h"
namespace http2 {
namespace adapter {
// This visitor implementation accepts a set of nghttp2 callbacks and a "user
// data" pointer, and invokes the callbacks according to HTTP/2 events received.
-class CallbackVisitor : public Http2VisitorInterface {
+class QUICHE_EXPORT_PRIVATE CallbackVisitor : public Http2VisitorInterface {
public:
explicit CallbackVisitor(Perspective perspective,
- nghttp2_session_callbacks_unique_ptr callbacks,
- void* user_data)
- : perspective_(perspective),
- callbacks_(std::move(callbacks)),
- user_data_(user_data) {}
+ const nghttp2_session_callbacks& callbacks,
+ void* user_data);
+ ssize_t OnReadyToSend(absl::string_view serialized) override;
void OnConnectionError() override;
void OnFrameHeader(Http2StreamId stream_id,
size_t length,
@@ -32,10 +31,10 @@ class CallbackVisitor : public Http2VisitorInterface {
void OnSetting(Http2Setting setting) override;
void OnSettingsEnd() override;
void OnSettingsAck() override;
- void OnBeginHeadersForStream(Http2StreamId stream_id) override;
- void OnHeaderForStream(Http2StreamId stream_id,
- absl::string_view name,
- absl::string_view value) override;
+ bool OnBeginHeadersForStream(Http2StreamId stream_id) override;
+ OnHeaderResult OnHeaderForStream(Http2StreamId stream_id,
+ absl::string_view name,
+ absl::string_view value) override;
void OnEndHeadersForStream(Http2StreamId stream_id) override;
void OnBeginDataForStream(Http2StreamId stream_id,
size_t payload_length) override;
@@ -56,11 +55,16 @@ class CallbackVisitor : public Http2VisitorInterface {
Http2ErrorCode error_code,
absl::string_view opaque_data) override;
void OnWindowUpdate(Http2StreamId stream_id, int window_increment) override;
+ int OnBeforeFrameSent(uint8_t frame_type, Http2StreamId stream_id,
+ size_t length, uint8_t flags) override;
+ int OnFrameSent(uint8_t frame_type, Http2StreamId stream_id, size_t length,
+ uint8_t flags, uint32_t error_code) override;
void OnReadyToSendDataForStream(Http2StreamId stream_id,
char* destination_buffer,
size_t length,
ssize_t* written,
bool* end_stream) override;
+ bool OnInvalidFrame(Http2StreamId stream_id, int error_code) override;
void OnReadyToSendMetadataForStream(Http2StreamId stream_id,
char* buffer,
size_t length,
@@ -69,9 +73,25 @@ class CallbackVisitor : public Http2VisitorInterface {
size_t payload_length) override;
void OnMetadataForStream(Http2StreamId stream_id,
absl::string_view metadata) override;
- void OnMetadataEndForStream(Http2StreamId stream_id) override;
+ bool OnMetadataEndForStream(Http2StreamId stream_id) override;
+ void OnErrorDebug(absl::string_view message) override;
private:
+ struct QUICHE_EXPORT_PRIVATE StreamInfo {
+ bool before_sent_headers = false;
+ bool sent_headers = false;
+ bool received_headers = false;
+ };
+
+ using StreamInfoMap =
+ absl::flat_hash_map<Http2StreamId, std::unique_ptr<StreamInfo>>;
+
+ void PopulateFrame(nghttp2_frame& frame, uint8_t frame_type,
+ Http2StreamId stream_id, size_t length, uint8_t flags,
+ uint32_t error_code, bool sent_headers);
+ // Creates the StreamInfoMap entry if it doesn't exist.
+ StreamInfoMap::iterator GetStreamInfo(Http2StreamId stream_id);
+
Perspective perspective_;
nghttp2_session_callbacks_unique_ptr callbacks_;
void* user_data_;
@@ -80,10 +100,7 @@ class CallbackVisitor : public Http2VisitorInterface {
std::vector<nghttp2_settings_entry> settings_;
size_t remaining_data_ = 0;
- struct StreamInfo {
- bool received_headers = false;
- };
- absl::flat_hash_map<Http2StreamId, std::unique_ptr<StreamInfo>> stream_map_;
+ StreamInfoMap stream_map_;
};
} // namespace adapter
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor_test.cc
index 4dfcb5fcddb..2cb31ed24f4 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor_test.cc
@@ -1,6 +1,7 @@
#include "http2/adapter/callback_visitor.h"
#include "http2/adapter/mock_nghttp2_callbacks.h"
+#include "http2/adapter/nghttp2_test_utils.h"
#include "http2/adapter/test_utils.h"
#include "common/platform/api/quiche_test.h"
@@ -21,13 +22,14 @@ enum FrameType {
PING,
GOAWAY,
WINDOW_UPDATE,
+ CONTINUATION,
};
// Tests connection-level events.
TEST(ClientCallbackVisitorUnitTest, ConnectionFrames) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kClient,
- MockNghttp2Callbacks::GetCallbacks(), &callbacks);
+ *MockNghttp2Callbacks::GetCallbacks(), &callbacks);
testing::InSequence seq;
@@ -73,7 +75,7 @@ TEST(ClientCallbackVisitorUnitTest, ConnectionFrames) {
TEST(ClientCallbackVisitorUnitTest, StreamFrames) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kClient,
- MockNghttp2Callbacks::GetCallbacks(), &callbacks);
+ *MockNghttp2Callbacks::GetCallbacks(), &callbacks);
testing::InSequence seq;
@@ -153,12 +155,69 @@ TEST(ClientCallbackVisitorUnitTest, StreamFrames) {
EXPECT_CALL(callbacks, OnStreamClose(5, NGHTTP2_REFUSED_STREAM));
visitor.OnCloseStream(5, Http2ErrorCode::REFUSED_STREAM);
+
+ // Metadata events
+ constexpr size_t kMetadataBufferSize = 256;
+ char metadata_dest[kMetadataBufferSize];
+ ssize_t written = 0;
+
+ const absl::string_view kExampleFrame = "This is supposed to be metadata.";
+ EXPECT_CALL(
+ callbacks,
+ OnPackExtension(_, kMetadataBufferSize,
+ Field(&nghttp2_frame::hd,
+ HasFrameHeaderRef(7, kMetadataFrameType, _))))
+ .WillOnce(testing::Invoke(
+ [kExampleFrame](uint8_t* buf, size_t /*len*/,
+ const nghttp2_frame* /*frame*/) -> ssize_t {
+ std::memcpy(buf, kExampleFrame.data(), kExampleFrame.size());
+ return kExampleFrame.size();
+ }));
+ visitor.OnReadyToSendMetadataForStream(7, metadata_dest, kMetadataBufferSize,
+ &written);
+ ASSERT_EQ(written, kExampleFrame.size());
+ EXPECT_EQ(absl::string_view(metadata_dest, written), kExampleFrame);
+}
+
+TEST(ClientCallbackVisitorUnitTest, HeadersWithContinuation) {
+ testing::StrictMock<MockNghttp2Callbacks> callbacks;
+ CallbackVisitor visitor(Perspective::kClient,
+ *MockNghttp2Callbacks::GetCallbacks(), &callbacks);
+
+ testing::InSequence seq;
+
+ // HEADERS on stream 1
+ EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, 0x0)));
+ visitor.OnFrameHeader(1, 23, HEADERS, 0x0);
+
+ EXPECT_CALL(callbacks,
+ OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE)));
+ visitor.OnBeginHeadersForStream(1);
+
+ EXPECT_CALL(callbacks, OnHeader(_, ":status", "200", _));
+ visitor.OnHeaderForStream(1, ":status", "200");
+
+ EXPECT_CALL(callbacks, OnHeader(_, "server", "my-fake-server", _));
+ visitor.OnHeaderForStream(1, "server", "my-fake-server");
+
+ EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, CONTINUATION, 0x4)));
+ visitor.OnFrameHeader(1, 23, CONTINUATION, 0x4);
+
+ EXPECT_CALL(callbacks,
+ OnHeader(_, "date", "Tue, 6 Apr 2021 12:54:01 GMT", _));
+ visitor.OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT");
+
+ EXPECT_CALL(callbacks, OnHeader(_, "trailer", "x-server-status", _));
+ visitor.OnHeaderForStream(1, "trailer", "x-server-status");
+
+ EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE)));
+ visitor.OnEndHeadersForStream(1);
}
TEST(ServerCallbackVisitorUnitTest, ConnectionFrames) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kServer,
- MockNghttp2Callbacks::GetCallbacks(), &callbacks);
+ *MockNghttp2Callbacks::GetCallbacks(), &callbacks);
testing::InSequence seq;
@@ -196,7 +255,7 @@ TEST(ServerCallbackVisitorUnitTest, ConnectionFrames) {
TEST(ServerCallbackVisitorUnitTest, StreamFrames) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kServer,
- MockNghttp2Callbacks::GetCallbacks(), &callbacks);
+ *MockNghttp2Callbacks::GetCallbacks(), &callbacks);
testing::InSequence seq;
@@ -252,6 +311,28 @@ TEST(ServerCallbackVisitorUnitTest, StreamFrames) {
EXPECT_CALL(callbacks, OnStreamClose(3, NGHTTP2_INTERNAL_ERROR));
visitor.OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR);
+
+ // Metadata events
+ constexpr size_t kMetadataBufferSize = 256;
+ char metadata_dest[kMetadataBufferSize];
+ ssize_t written = 0;
+
+ const absl::string_view kExampleFrame = "This is supposed to be metadata.";
+ EXPECT_CALL(
+ callbacks,
+ OnPackExtension(_, kMetadataBufferSize,
+ Field(&nghttp2_frame::hd,
+ HasFrameHeaderRef(5, kMetadataFrameType, _))))
+ .WillOnce(testing::Invoke(
+ [kExampleFrame](uint8_t* buf, size_t /*len*/,
+ const nghttp2_frame* /*frame*/) -> ssize_t {
+ std::memcpy(buf, kExampleFrame.data(), kExampleFrame.size());
+ return kExampleFrame.size();
+ }));
+ visitor.OnReadyToSendMetadataForStream(5, metadata_dest, kMetadataBufferSize,
+ &written);
+ ASSERT_EQ(written, kExampleFrame.size());
+ EXPECT_EQ(absl::string_view(metadata_dest, written), kExampleFrame);
}
} // namespace
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/data_source.cc b/chromium/net/third_party/quiche/src/http2/adapter/data_source.cc
deleted file mode 100644
index 13f89d28581..00000000000
--- a/chromium/net/third_party/quiche/src/http2/adapter/data_source.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "http2/adapter/data_source.h"
-
-namespace http2 {
-namespace adapter {
-
-StringDataSource::StringDataSource(std::string data)
- : data_(std::move(data)), remaining_(data_) {
- state_ = remaining_.empty() ? DONE : READY;
-}
-
-absl::string_view StringDataSource::NextData() const {
- return remaining_;
-}
-
-void StringDataSource::Consume(size_t bytes) {
- remaining_.remove_prefix(std::min(bytes, remaining_.size()));
- if (remaining_.empty()) {
- state_ = DONE;
- }
-}
-
-} // namespace adapter
-} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/data_source.h b/chromium/net/third_party/quiche/src/http2/adapter/data_source.h
index e170104a597..d86714148c3 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/data_source.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/data_source.h
@@ -2,49 +2,46 @@
#define QUICHE_HTTP2_ADAPTER_DATA_SOURCE_H_
#include <string>
+#include <utility>
#include "absl/strings/string_view.h"
+#include "common/platform/api/quiche_export.h"
namespace http2 {
namespace adapter {
-// Represents a HTTP message body.
-class DataSource {
+// Represents a source of DATA frames for transmission to the peer.
+class QUICHE_EXPORT_PRIVATE DataFrameSource {
public:
- virtual ~DataSource() {}
+ virtual ~DataFrameSource() {}
- enum State {
- // The source is not done, but cannot currently provide more data.
- NOT_READY,
- // The source can provide more data.
- READY,
- // The source is done.
- DONE,
- };
+ enum : ssize_t { kBlocked = 0, kError = -1 };
- State state() const { return state_; }
+ // Returns the number of bytes to send in the next DATA frame, and whether
+ // this frame indicates the end of the data. Returns {kBlocked, false} if
+ // blocked, {kError, false} on error.
+ virtual std::pair<ssize_t, bool> SelectPayloadLength(size_t max_length) = 0;
- // The next range of data provided by this data source.
- virtual absl::string_view NextData() const = 0;
+ // This method is called with a frame header and a payload length to send. The
+ // source should send or buffer the entire frame and return true, or return
+ // false without sending or buffering anything.
+ virtual bool Send(absl::string_view frame_header, size_t payload_length) = 0;
- // Indicates that |bytes| bytes have been consumed by the caller.
- virtual void Consume(size_t bytes) = 0;
-
- protected:
- State state_ = NOT_READY;
+ // If true, the end of this data source indicates the end of the stream.
+ // Otherwise, this data will be followed by trailers.
+ virtual bool send_fin() const = 0;
};
-// A simple implementation constructible from a string_view or std::string.
-class StringDataSource : public DataSource {
+// Represents a source of metadata frames for transmission to the peer.
+class QUICHE_EXPORT_PRIVATE MetadataSource {
public:
- explicit StringDataSource(std::string data);
-
- absl::string_view NextData() const override;
- void Consume(size_t bytes) override;
+ virtual ~MetadataSource() {}
- private:
- const std::string data_;
- absl::string_view remaining_;
+ // This method is called with a destination buffer and length. It should
+ // return the number of payload bytes copied to |dest|, or a negative integer
+ // to indicate an error, as well as a boolean indicating whether the metadata
+ // has been completely copied.
+ virtual std::pair<ssize_t, bool> Pack(uint8_t* dest, size_t dest_len) = 0;
};
} // namespace adapter
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/data_source_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/data_source_test.cc
deleted file mode 100644
index c290124b430..00000000000
--- a/chromium/net/third_party/quiche/src/http2/adapter/data_source_test.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "http2/adapter/data_source.h"
-
-#include "common/platform/api/quiche_test.h"
-
-namespace http2 {
-namespace adapter {
-namespace test {
-namespace {
-
-TEST(StringDataSourceTest, EmptyString) {
- StringDataSource source("");
-
- EXPECT_EQ(source.state(), DataSource::DONE);
- EXPECT_THAT(source.NextData(), testing::IsEmpty());
-}
-
-TEST(StringDataSourceTest, PartialConsume) {
- StringDataSource source("I'm a HTTP message body. Really!");
-
- EXPECT_EQ(source.state(), DataSource::READY);
- EXPECT_THAT(source.NextData(), testing::Not(testing::IsEmpty()));
- source.Consume(6);
- EXPECT_EQ(source.state(), DataSource::READY);
- EXPECT_THAT(source.NextData(), testing::StartsWith("HTTP"));
-
- source.Consume(0);
- EXPECT_EQ(source.state(), DataSource::READY);
- EXPECT_THAT(source.NextData(), testing::StartsWith("HTTP"));
-
- // Consumes more than the remaining bytes available.
- source.Consume(50);
- EXPECT_THAT(source.NextData(), testing::IsEmpty())
- << "next data: " << source.NextData();
- EXPECT_EQ(source.state(), DataSource::DONE);
-}
-
-} // namespace
-} // namespace test
-} // namespace adapter
-} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_adapter.h b/chromium/net/third_party/quiche/src/http2/adapter/http2_adapter.h
index ef2ae0a5baf..789be98ae3b 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/http2_adapter.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_adapter.h
@@ -4,9 +4,11 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "absl/types/span.h"
+#include "http2/adapter/data_source.h"
#include "http2/adapter/http2_protocol.h"
#include "http2/adapter/http2_session.h"
#include "http2/adapter/http2_visitor_interface.h"
+#include "common/platform/api/quiche_export.h"
namespace http2 {
namespace adapter {
@@ -17,11 +19,13 @@ namespace adapter {
// invokes corresponding callbacks on its passed-in Http2VisitorInterface.
// Http2Adapter is a base class shared between client-side and server-side
// implementations.
-class Http2Adapter {
+class QUICHE_EXPORT_PRIVATE Http2Adapter {
public:
Http2Adapter(const Http2Adapter&) = delete;
Http2Adapter& operator=(const Http2Adapter&) = delete;
+ virtual bool IsServerSession() const = 0;
+
// Processes the incoming |bytes| as HTTP/2 and invokes callbacks on the
// |visitor_| as appropriate.
virtual ssize_t ProcessBytes(absl::string_view bytes) = 0;
@@ -36,17 +40,15 @@ class Http2Adapter {
int weight,
bool exclusive) = 0;
- // Submits a PING on the connection. Note that nghttp2 automatically submits
- // PING acks upon receiving non-ack PINGs from the peer, so callers only use
- // this method to originate PINGs. See nghttp2_option_set_no_auto_ping_ack().
+ // Submits a PING on the connection.
virtual void SubmitPing(Http2PingId ping_id) = 0;
+ // Starts a graceful shutdown. A no-op for clients.
+ virtual void SubmitShutdownNotice() = 0;
+
// Submits a GOAWAY on the connection. Note that |last_accepted_stream_id|
- // refers to stream IDs initiated by the peer. For client-side, this last
- // stream ID must be even (or 0); for server-side, this last stream ID must be
- // odd (or 0). To submit a GOAWAY with |last_accepted_stream_id| with the
- // maximum stream ID, signaling imminent connection termination, call
- // SubmitShutdownNotice() instead (though this is only possible server-side).
+ // refers to stream IDs initiated by the peer. For a server sending this
+ // frame, this last stream ID must be odd (or 0).
virtual void SubmitGoAway(Http2StreamId last_accepted_stream_id,
Http2ErrorCode error_code,
absl::string_view opaque_data) = 0;
@@ -56,27 +58,85 @@ class Http2Adapter {
virtual void SubmitWindowUpdate(Http2StreamId stream_id,
int window_increment) = 0;
- // Submits a METADATA frame for the given stream (a |stream_id| of 0 indicates
- // connection-level METADATA). If |fin|, the frame will also have the
- // END_METADATA flag set.
- virtual void SubmitMetadata(Http2StreamId stream_id, bool fin) = 0;
+ // Submits a RST_STREAM for the given |stream_id| and |error_code|.
+ virtual void SubmitRst(Http2StreamId stream_id,
+ Http2ErrorCode error_code) = 0;
+
+ // Submits a sequence of METADATA frames for the given stream. A |stream_id|
+ // of 0 indicates connection-level METADATA.
+ virtual void SubmitMetadata(Http2StreamId stream_id,
+ std::unique_ptr<MetadataSource> source) = 0;
+
+ // Invokes the visitor's OnReadyToSend() method for serialized frame data.
+ // Returns 0 on success.
+ virtual int Send() = 0;
+
+ // Returns the connection-level flow control window advertised by the peer.
+ virtual int GetSendWindowSize() const = 0;
- // Returns serialized bytes for writing to the wire.
- // Writes should be submitted to Http2Adapter first, so that Http2Adapter
- // has data to serialize and return in this method.
- virtual std::string GetBytesToWrite(absl::optional<size_t> max_bytes) = 0;
+ // Returns the stream-level flow control window advertised by the peer.
+ virtual int GetStreamSendWindowSize(Http2StreamId stream_id) const = 0;
- // Returns the connection-level flow control window for the peer.
- virtual int GetPeerConnectionWindow() const = 0;
+ // Returns the current upper bound on the flow control receive window for this
+ // stream. This value does not account for data received from the peer.
+ virtual int GetStreamReceiveWindowLimit(Http2StreamId stream_id) const = 0;
+
+ // Returns the amount of data a peer could send on a given stream. This is
+ // the outstanding stream receive window.
+ virtual int GetStreamReceiveWindowSize(Http2StreamId stream_id) const = 0;
+
+ // Returns the total amount of data a peer could send on the connection. This
+ // is the outstanding connection receive window.
+ virtual int GetReceiveWindowSize() const = 0;
+
+ // Returns the size of the HPACK encoder's dynamic table, including the
+ // per-entry overhead from the specification.
+ virtual int GetHpackEncoderDynamicTableSize() const = 0;
+
+ // Returns the size of the HPACK decoder's dynamic table, including the
+ // per-entry overhead from the specification.
+ virtual int GetHpackDecoderDynamicTableSize() const = 0;
+
+ // Gets the highest stream ID value seen in a frame received by this endpoint.
+ // This method is only guaranteed to work for server endpoints.
+ virtual Http2StreamId GetHighestReceivedStreamId() const = 0;
// Marks the given amount of data as consumed for the given stream, which
- // enables the nghttp2 layer to trigger WINDOW_UPDATEs as appropriate.
+ // enables the implementation layer to send WINDOW_UPDATEs as appropriate.
virtual void MarkDataConsumedForStream(Http2StreamId stream_id,
size_t num_bytes) = 0;
- // Submits a RST_STREAM for the given stream.
- virtual void SubmitRst(Http2StreamId stream_id,
- Http2ErrorCode error_code) = 0;
+ // Returns the assigned stream ID if the operation succeeds. Otherwise,
+ // returns a negative integer indicating an error code. |data_source| may be
+ // nullptr if the request does not have a body.
+ virtual int32_t SubmitRequest(absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source,
+ void* user_data) = 0;
+
+ // Returns 0 on success. |data_source| may be nullptr if the response does not
+ // have a body.
+ virtual int SubmitResponse(Http2StreamId stream_id,
+ absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source) = 0;
+
+ // Queues trailers to be sent after any outstanding data on the stream with ID
+ // |stream_id|. Returns 0 on success.
+ virtual int SubmitTrailer(Http2StreamId stream_id,
+ absl::Span<const Header> trailers) = 0;
+
+ // Sets a user data pointer for the given stream. Can be called after
+ // SubmitRequest/SubmitResponse, or after receiving any frame for a given
+ // stream.
+ virtual void SetStreamUserData(Http2StreamId stream_id, void* user_data) = 0;
+
+ // Returns nullptr if the stream does not exist, or if stream user data has
+ // not been set.
+ virtual void* GetStreamUserData(Http2StreamId stream_id) = 0;
+
+ // Resumes a stream that was previously blocked (for example, due to
+ // DataFrameSource::SelectPayloadLength() returning kBlocked). Returns true if
+ // the stream was successfully resumed.
+ virtual bool ResumeStream(Http2StreamId stream_id) = 0;
protected:
// Subclasses should expose a public factory method for constructing and
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.cc b/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.cc
index 8d59aeedda0..dc0a57acac5 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.cc
@@ -12,6 +12,10 @@ const char kHttp2AuthorityPseudoHeader[] = ":authority";
const char kHttp2PathPseudoHeader[] = ":path";
const char kHttp2StatusPseudoHeader[] = ":status";
+const uint8_t kMetadataFrameType = 0x4d;
+const uint8_t kMetadataEndFlag = 0x04;
+const uint16_t kMetadataExtensionId = 0x4d44;
+
std::pair<absl::string_view, bool> GetStringView(const HeaderRep& rep) {
if (absl::holds_alternative<absl::string_view>(rep)) {
return std::make_pair(absl::get<absl::string_view>(rep), true);
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.h b/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.h
index 1e1dd39a21a..9f72fbe9d05 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.h
@@ -5,10 +5,10 @@
#include <string>
#include <utility>
-#include "base/integral_types.h"
#include "absl/base/attributes.h"
#include "absl/strings/string_view.h"
#include "absl/types/variant.h"
+#include "common/platform/api/quiche_export.h"
namespace http2 {
namespace adapter {
@@ -34,7 +34,7 @@ std::pair<absl::string_view, bool> GetStringView(const HeaderRep& rep);
using Header = std::pair<HeaderRep, HeaderRep>;
// Represents an HTTP/2 SETTINGS key-value parameter.
-struct Http2Setting {
+struct QUICHE_EXPORT_PRIVATE Http2Setting {
Http2SettingsId id;
uint32_t value;
};
@@ -50,17 +50,40 @@ const Http2StreamId kConnectionStreamId = 0;
// 7540 Section 6.5.2 (SETTINGS_MAX_FRAME_SIZE).
const int kDefaultFramePayloadSizeLimit = 16 * 1024;
-// The default value for the initial stream flow control window size, according
-// to RFC 7540 Section 6.9.2.
-const int kDefaultInitialStreamWindowSize = 64 * 1024 - 1;
+// The default value for the initial stream and connection flow control window
+// size, according to RFC 7540 Section 6.9.2.
+const int kInitialFlowControlWindowSize = 64 * 1024 - 1;
// The pseudo-header fields as specified in RFC 7540 Section 8.1.2.3 (request)
// and Section 8.1.2.4 (response).
-ABSL_CONST_INIT extern const char kHttp2MethodPseudoHeader[];
-ABSL_CONST_INIT extern const char kHttp2SchemePseudoHeader[];
-ABSL_CONST_INIT extern const char kHttp2AuthorityPseudoHeader[];
-ABSL_CONST_INIT extern const char kHttp2PathPseudoHeader[];
-ABSL_CONST_INIT extern const char kHttp2StatusPseudoHeader[];
+ABSL_CONST_INIT QUICHE_EXPORT_PRIVATE extern const char
+ kHttp2MethodPseudoHeader[];
+ABSL_CONST_INIT QUICHE_EXPORT_PRIVATE extern const char
+ kHttp2SchemePseudoHeader[];
+ABSL_CONST_INIT QUICHE_EXPORT_PRIVATE extern const char
+ kHttp2AuthorityPseudoHeader[];
+ABSL_CONST_INIT QUICHE_EXPORT_PRIVATE extern const char
+ kHttp2PathPseudoHeader[];
+ABSL_CONST_INIT QUICHE_EXPORT_PRIVATE extern const char
+ kHttp2StatusPseudoHeader[];
+
+ABSL_CONST_INIT QUICHE_EXPORT_PRIVATE extern const uint8_t kMetadataFrameType;
+ABSL_CONST_INIT QUICHE_EXPORT_PRIVATE extern const uint8_t kMetadataEndFlag;
+ABSL_CONST_INIT QUICHE_EXPORT_PRIVATE extern const uint16_t
+ kMetadataExtensionId;
+
+enum class FrameType : uint8_t {
+ DATA = 0x0,
+ HEADERS,
+ PRIORITY,
+ RST_STREAM,
+ SETTINGS,
+ PUSH_PROMISE,
+ PING,
+ GOAWAY,
+ WINDOW_UPDATE,
+ CONTINUATION,
+};
// HTTP/2 error codes as specified in RFC 7540 Section 7.
enum class Http2ErrorCode {
@@ -94,7 +117,8 @@ enum Http2KnownSettingsId : Http2SettingsId {
INITIAL_WINDOW_SIZE = 0x4,
MAX_FRAME_SIZE = 0x5,
MAX_HEADER_LIST_SIZE = 0x6,
- MAX_SETTING = MAX_HEADER_LIST_SIZE
+ ENABLE_CONNECT_PROTOCOL = 0x8, // See RFC 8441
+ MAX_SETTING = ENABLE_CONNECT_PROTOCOL
};
// Returns a human-readable string representation of the given SETTINGS |id| for
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_session.h b/chromium/net/third_party/quiche/src/http2/adapter/http2_session.h
index 0a6321c0ccc..3b57d8f1aba 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/http2_session.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_session.h
@@ -5,14 +5,15 @@
#include "absl/strings/string_view.h"
#include "http2/adapter/http2_protocol.h"
+#include "common/platform/api/quiche_export.h"
namespace http2 {
namespace adapter {
-struct Http2SessionCallbacks {};
+struct QUICHE_EXPORT_PRIVATE Http2SessionCallbacks {};
// A class to represent the state of a single HTTP/2 connection.
-class Http2Session {
+class QUICHE_EXPORT_PRIVATE Http2Session {
public:
Http2Session() = default;
virtual ~Http2Session() {}
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_util.h b/chromium/net/third_party/quiche/src/http2/adapter/http2_util.h
index 3ace28b2ce9..88e9a49e6e4 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/http2_util.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_util.h
@@ -2,13 +2,16 @@
#define QUICHE_HTTP2_ADAPTER_HTTP2_UTIL_H_
#include "http2/adapter/http2_protocol.h"
+#include "common/platform/api/quiche_export.h"
#include "spdy/core/spdy_protocol.h"
namespace http2 {
namespace adapter {
-spdy::SpdyErrorCode TranslateErrorCode(Http2ErrorCode code);
-Http2ErrorCode TranslateErrorCode(spdy::SpdyErrorCode code);
+QUICHE_EXPORT_PRIVATE spdy::SpdyErrorCode TranslateErrorCode(
+ Http2ErrorCode code);
+QUICHE_EXPORT_PRIVATE Http2ErrorCode
+TranslateErrorCode(spdy::SpdyErrorCode code);
} // namespace adapter
} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_visitor_interface.h b/chromium/net/third_party/quiche/src/http2/adapter/http2_visitor_interface.h
index 12729e75651..292021a23f4 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/http2_visitor_interface.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_visitor_interface.h
@@ -5,6 +5,7 @@
#include "absl/strings/string_view.h"
#include "http2/adapter/http2_protocol.h"
+#include "common/platform/api/quiche_export.h"
namespace http2 {
namespace adapter {
@@ -44,20 +45,24 @@ namespace adapter {
// - OnCloseStream()
//
// More details are at RFC 7540 (go/http2spec).
-class Http2VisitorInterface {
+class QUICHE_EXPORT_PRIVATE Http2VisitorInterface {
public:
Http2VisitorInterface(const Http2VisitorInterface&) = delete;
Http2VisitorInterface& operator=(const Http2VisitorInterface&) = delete;
virtual ~Http2VisitorInterface() = default;
+ static const ssize_t kSendBlocked = 0;
+ static const ssize_t kSendError = -1;
+ // Called when there are serialized frames to send. Should return how many
+ // bytes were actually sent. May return kSendBlocked or kSendError.
+ virtual ssize_t OnReadyToSend(absl::string_view serialized) = 0;
+
// Called when a connection-level processing error has been encountered.
virtual void OnConnectionError() = 0;
// Called when the header for a frame is received.
- virtual void OnFrameHeader(Http2StreamId stream_id,
- size_t length,
- uint8_t type,
- uint8_t flags) {}
+ virtual void OnFrameHeader(Http2StreamId /*stream_id*/, size_t /*length*/,
+ uint8_t /*type*/, uint8_t /*flags*/) {}
// Called when a non-ack SETTINGS frame is received.
virtual void OnSettingsStart() = 0;
@@ -72,15 +77,28 @@ class Http2VisitorInterface {
virtual void OnSettingsAck() = 0;
// Called when the connection receives the header block for a HEADERS frame on
- // a stream but has not yet parsed individual headers.
- virtual void OnBeginHeadersForStream(Http2StreamId stream_id) = 0;
+ // a stream but has not yet parsed individual headers. Returns false if a
+ // fatal error has occurred.
+ virtual bool OnBeginHeadersForStream(Http2StreamId stream_id) = 0;
// Called when the connection receives the header |key| and |value| for a
// stream. The HTTP/2 pseudo-headers defined in RFC 7540 Sections 8.1.2.3 and
// 8.1.2.4 are also conveyed in this callback. This method is called after
- // OnBeginHeadersForStream().
- virtual void OnHeaderForStream(Http2StreamId stream_id, absl::string_view key,
- absl::string_view value) = 0;
+ // OnBeginHeadersForStream(). May return HEADER_RST_STREAM to indicate the
+ // header block should be rejected. This will cause the library to queue a
+ // RST_STREAM frame, which will have a default error code of INTERNAL_ERROR.
+ // The visitor implementation may choose to queue a RST_STREAM with a
+ // different error code instead, which should be done before returning
+ // HEADER_RST_STREAM. Returning HEADER_CONNECTION_ERROR will lead to a
+ // non-recoverable error on the connection.
+ enum OnHeaderResult {
+ HEADER_OK,
+ HEADER_CONNECTION_ERROR,
+ HEADER_RST_STREAM,
+ };
+ virtual OnHeaderResult OnHeaderForStream(Http2StreamId stream_id,
+ absl::string_view key,
+ absl::string_view value) = 0;
// Called when the connection has received the complete header block for a
// logical HEADERS frame on a stream (which may contain CONTINUATION frames,
@@ -133,6 +151,18 @@ class Http2VisitorInterface {
virtual void OnWindowUpdate(Http2StreamId stream_id,
int window_increment) = 0;
+ // Called immediately before a frame of the given type is sent. Should return
+ // 0 on success.
+ virtual int OnBeforeFrameSent(uint8_t frame_type, Http2StreamId stream_id,
+ size_t length, uint8_t flags) = 0;
+
+ // Called immediately after a frame of the given type is sent. Should return 0
+ // on success. |error_code| is only populated for RST_STREAM and GOAWAY frame
+ // types.
+ virtual int OnFrameSent(uint8_t frame_type, Http2StreamId stream_id,
+ size_t length, uint8_t flags,
+ uint32_t error_code) = 0;
+
// Called when the connection is ready to send data for a stream. The
// implementation should write at most |length| bytes of the data payload to
// the |destination_buffer| and set |end_stream| to true IFF there will be no
@@ -144,6 +174,12 @@ class Http2VisitorInterface {
ssize_t* written,
bool* end_stream) = 0;
+ // Called when the connection receives an invalid frame. |error_code| is a
+ // negative integer error code generated by the library. A return value of
+ // false will result in the connection entering an error state, with no
+ // further frame processing possible.
+ virtual bool OnInvalidFrame(Http2StreamId stream_id, int error_code) = 0;
+
// Called when the connection is ready to write metadata for |stream_id| to
// the wire. The implementation should write at most |length| bytes of the
// serialized metadata payload to the |buffer| and set |written| to the number
@@ -155,6 +191,7 @@ class Http2VisitorInterface {
// Called when the connection receives the beginning of a METADATA frame
// (which may itself be the middle of a logical metadata block). The metadata
// payload will be provided via subsequent calls to OnMetadataForStream().
+ // TODO(birenroy): Consider removing this unnecessary method.
virtual void OnBeginMetadataForStream(Http2StreamId stream_id,
size_t payload_length) = 0;
@@ -165,7 +202,11 @@ class Http2VisitorInterface {
// Called when the connection has finished receiving a logical metadata block
// for a stream. Note that there may be multiple metadata blocks for a stream.
- virtual void OnMetadataEndForStream(Http2StreamId stream_id) = 0;
+ // Returns false if there was an error unpacking the metadata payload.
+ virtual bool OnMetadataEndForStream(Http2StreamId stream_id) = 0;
+
+ // Invoked with an error message from the application.
+ virtual void OnErrorDebug(absl::string_view message) = 0;
protected:
Http2VisitorInterface() = default;
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/mock_http2_visitor.h b/chromium/net/third_party/quiche/src/http2/adapter/mock_http2_visitor.h
index 171d40f443f..3daa8053962 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/mock_http2_visitor.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/mock_http2_visitor.h
@@ -2,6 +2,7 @@
#define QUICHE_HTTP2_ADAPTER_MOCK_HTTP2_VISITOR_INTERFACE_H_
#include "http2/adapter/http2_visitor_interface.h"
+#include "common/platform/api/quiche_export.h"
#include "common/platform/api/quiche_test.h"
namespace http2 {
@@ -9,10 +10,20 @@ namespace adapter {
namespace test {
// A mock visitor class, for use in tests.
-class MockHttp2Visitor : public Http2VisitorInterface {
+class QUICHE_NO_EXPORT MockHttp2Visitor : public Http2VisitorInterface {
public:
- MockHttp2Visitor() = default;
-
+ MockHttp2Visitor() {
+ ON_CALL(*this, OnBeginHeadersForStream)
+ .WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnHeaderForStream).WillByDefault(testing::Return(HEADER_OK));
+ ON_CALL(*this, OnInvalidFrame).WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnMetadataEndForStream).WillByDefault(testing::Return(true));
+ }
+
+ MOCK_METHOD(ssize_t,
+ OnReadyToSend,
+ (absl::string_view serialized),
+ (override));
MOCK_METHOD(void, OnConnectionError, (), (override));
MOCK_METHOD(
void,
@@ -23,15 +34,11 @@ class MockHttp2Visitor : public Http2VisitorInterface {
MOCK_METHOD(void, OnSetting, (Http2Setting setting), (override));
MOCK_METHOD(void, OnSettingsEnd, (), (override));
MOCK_METHOD(void, OnSettingsAck, (), (override));
- MOCK_METHOD(void,
- OnBeginHeadersForStream,
- (Http2StreamId stream_id),
+ MOCK_METHOD(bool, OnBeginHeadersForStream, (Http2StreamId stream_id),
(override));
- MOCK_METHOD(void,
- OnHeaderForStream,
- (Http2StreamId stream_id,
- absl::string_view key,
+ MOCK_METHOD(OnHeaderResult, OnHeaderForStream,
+ (Http2StreamId stream_id, absl::string_view key,
absl::string_view value),
(override));
@@ -89,6 +96,19 @@ class MockHttp2Visitor : public Http2VisitorInterface {
(Http2StreamId stream_id, int window_increment),
(override));
+ MOCK_METHOD(int, OnBeforeFrameSent,
+ (uint8_t frame_type, Http2StreamId stream_id, size_t length,
+ uint8_t flags),
+ (override));
+
+ MOCK_METHOD(int, OnFrameSent,
+ (uint8_t frame_type, Http2StreamId stream_id, size_t length,
+ uint8_t flags, uint32_t error_code),
+ (override));
+
+ MOCK_METHOD(bool, OnInvalidFrame, (Http2StreamId stream_id, int error_code),
+ (override));
+
MOCK_METHOD(void,
OnReadyToSendDataForStream,
(Http2StreamId stream_id,
@@ -114,10 +134,10 @@ class MockHttp2Visitor : public Http2VisitorInterface {
(Http2StreamId stream_id, absl::string_view metadata),
(override));
- MOCK_METHOD(void,
- OnMetadataEndForStream,
- (Http2StreamId stream_id),
+ MOCK_METHOD(bool, OnMetadataEndForStream, (Http2StreamId stream_id),
(override));
+
+ MOCK_METHOD(void, OnErrorDebug, (absl::string_view message), (override));
};
} // namespace test
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.cc b/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.cc
index 4699a371add..4243347e758 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.cc
@@ -109,12 +109,19 @@ nghttp2_session_callbacks_unique_ptr MockNghttp2Callbacks::GetCallbacks() {
nghttp2_session_callbacks_set_error_callback2(
callbacks,
- [](nghttp2_session* session, int lib_error_code, const char* msg,
+ [](nghttp2_session* /*session*/, int lib_error_code, const char* msg,
size_t len, void* user_data) -> int {
return static_cast<MockNghttp2Callbacks*>(user_data)->OnErrorCallback2(
lib_error_code, msg, len);
});
+ nghttp2_session_callbacks_set_pack_extension_callback(
+ callbacks,
+ [](nghttp2_session*, uint8_t* buf, size_t len, const nghttp2_frame* frame,
+ void* user_data) -> ssize_t {
+ return static_cast<MockNghttp2Callbacks*>(user_data)->OnPackExtension(
+ buf, len, frame);
+ });
return MakeCallbacksPtr(callbacks);
}
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.h b/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.h
index e2794575c8d..08d15be7d7d 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.h
@@ -4,6 +4,7 @@
#include "absl/strings/string_view.h"
#include "http2/adapter/nghttp2_util.h"
#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+#include "common/platform/api/quiche_export.h"
#include "common/platform/api/quiche_test.h"
namespace http2 {
@@ -12,7 +13,7 @@ namespace test {
// This class provides a set of mock nghttp2 callbacks for use in unit test
// expectations.
-class MockNghttp2Callbacks {
+class QUICHE_NO_EXPORT MockNghttp2Callbacks {
public:
MockNghttp2Callbacks() = default;
@@ -71,6 +72,9 @@ class MockNghttp2Callbacks {
OnErrorCallback2,
(int lib_error_code, const char* msg, size_t len),
());
+
+ MOCK_METHOD(ssize_t, OnPackExtension,
+ (uint8_t * buf, size_t len, const nghttp2_frame* frame), ());
};
} // namespace test
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.cc
index 878c040ef72..9b444a3243d 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.cc
@@ -4,6 +4,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "http2/adapter/nghttp2_callbacks.h"
+#include "http2/adapter/nghttp2_data_provider.h"
#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
#include "common/platform/api/quiche_logging.h"
#include "common/quiche_endian.h"
@@ -13,20 +14,26 @@ namespace adapter {
/* static */
std::unique_ptr<NgHttp2Adapter> NgHttp2Adapter::CreateClientAdapter(
- Http2VisitorInterface& visitor) {
- auto adapter = new NgHttp2Adapter(visitor, Perspective::kClient);
+ Http2VisitorInterface& visitor, const nghttp2_option* options) {
+ auto adapter = new NgHttp2Adapter(visitor, Perspective::kClient, options);
adapter->Initialize();
return absl::WrapUnique(adapter);
}
/* static */
std::unique_ptr<NgHttp2Adapter> NgHttp2Adapter::CreateServerAdapter(
- Http2VisitorInterface& visitor) {
- auto adapter = new NgHttp2Adapter(visitor, Perspective::kServer);
+ Http2VisitorInterface& visitor, const nghttp2_option* options) {
+ auto adapter = new NgHttp2Adapter(visitor, Perspective::kServer, options);
adapter->Initialize();
return absl::WrapUnique(adapter);
}
+bool NgHttp2Adapter::IsServerSession() const {
+ int result = nghttp2_session_check_server_session(session_->raw_ptr());
+ QUICHE_DCHECK_EQ(perspective_ == Perspective::kServer, result > 0);
+ return result > 0;
+}
+
ssize_t NgHttp2Adapter::ProcessBytes(absl::string_view bytes) {
const ssize_t processed_bytes = session_->ProcessBytes(bytes);
if (processed_bytes < 0) {
@@ -64,6 +71,10 @@ void NgHttp2Adapter::SubmitPing(Http2PingId ping_id) {
nghttp2_submit_ping(session_->raw_ptr(), NGHTTP2_FLAG_NONE, opaque_data);
}
+void NgHttp2Adapter::SubmitShutdownNotice() {
+ nghttp2_submit_shutdown_notice(session_->raw_ptr());
+}
+
void NgHttp2Adapter::SubmitGoAway(Http2StreamId last_accepted_stream_id,
Http2ErrorCode error_code,
absl::string_view opaque_data) {
@@ -79,32 +90,55 @@ void NgHttp2Adapter::SubmitWindowUpdate(Http2StreamId stream_id,
stream_id, window_increment);
}
-void NgHttp2Adapter::SubmitMetadata(Http2StreamId stream_id,
- bool end_metadata) {
+void NgHttp2Adapter::SubmitMetadata(
+ Http2StreamId /*stream_id*/, std::unique_ptr<MetadataSource> /*source*/) {
QUICHE_LOG(DFATAL) << "Not implemented";
}
-std::string NgHttp2Adapter::GetBytesToWrite(absl::optional<size_t> max_bytes) {
- ssize_t num_bytes = 0;
- std::string result;
- do {
- const uint8_t* data = nullptr;
- num_bytes = nghttp2_session_mem_send(session_->raw_ptr(), &data);
- if (num_bytes > 0) {
- absl::StrAppend(
- &result,
- absl::string_view(reinterpret_cast<const char*>(data), num_bytes));
- } else if (num_bytes < 0) {
- visitor_.OnConnectionError();
- }
- } while (num_bytes > 0);
+int NgHttp2Adapter::Send() {
+ const int result = nghttp2_session_send(session_->raw_ptr());
+ if (result != 0) {
+ QUICHE_VLOG(1) << "nghttp2_session_send returned " << result;
+ visitor_.OnConnectionError();
+ }
return result;
}
-int NgHttp2Adapter::GetPeerConnectionWindow() const {
+int NgHttp2Adapter::GetSendWindowSize() const {
return session_->GetRemoteWindowSize();
}
+int NgHttp2Adapter::GetStreamSendWindowSize(Http2StreamId stream_id) const {
+ return nghttp2_session_get_stream_remote_window_size(session_->raw_ptr(),
+ stream_id);
+}
+
+int NgHttp2Adapter::GetStreamReceiveWindowLimit(Http2StreamId stream_id) const {
+ return nghttp2_session_get_stream_effective_local_window_size(
+ session_->raw_ptr(), stream_id);
+}
+
+int NgHttp2Adapter::GetStreamReceiveWindowSize(Http2StreamId stream_id) const {
+ return nghttp2_session_get_stream_local_window_size(session_->raw_ptr(),
+ stream_id);
+}
+
+int NgHttp2Adapter::GetReceiveWindowSize() const {
+ return nghttp2_session_get_local_window_size(session_->raw_ptr());
+}
+
+int NgHttp2Adapter::GetHpackEncoderDynamicTableSize() const {
+ return nghttp2_session_get_hd_deflate_dynamic_table_size(session_->raw_ptr());
+}
+
+int NgHttp2Adapter::GetHpackDecoderDynamicTableSize() const {
+ return nghttp2_session_get_hd_inflate_dynamic_table_size(session_->raw_ptr());
+}
+
+Http2StreamId NgHttp2Adapter::GetHighestReceivedStreamId() const {
+ return nghttp2_session_get_last_proc_stream_id(session_->raw_ptr());
+}
+
void NgHttp2Adapter::MarkDataConsumedForStream(Http2StreamId stream_id,
size_t num_bytes) {
int rc = session_->Consume(stream_id, num_bytes);
@@ -125,24 +159,96 @@ void NgHttp2Adapter::SubmitRst(Http2StreamId stream_id,
}
}
+int32_t NgHttp2Adapter::SubmitRequest(
+ absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source, void* stream_user_data) {
+ auto nvs = GetNghttp2Nvs(headers);
+ std::unique_ptr<nghttp2_data_provider> provider =
+ MakeDataProvider(data_source.get());
+
+ int32_t stream_id =
+ nghttp2_submit_request(session_->raw_ptr(), nullptr, nvs.data(),
+ nvs.size(), provider.get(), stream_user_data);
+ // TODO(birenroy): clean up data source on stream close
+ sources_.emplace(stream_id, std::move(data_source));
+ QUICHE_VLOG(1) << "Submitted request with " << nvs.size()
+ << " request headers and user data " << stream_user_data
+ << "; resulted in stream " << stream_id;
+ return stream_id;
+}
+
+int NgHttp2Adapter::SubmitResponse(
+ Http2StreamId stream_id, absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source) {
+ auto nvs = GetNghttp2Nvs(headers);
+ std::unique_ptr<nghttp2_data_provider> provider =
+ MakeDataProvider(data_source.get());
+
+ // TODO(birenroy): clean up data source on stream close
+ sources_.emplace(stream_id, std::move(data_source));
+
+ int result = nghttp2_submit_response(session_->raw_ptr(), stream_id,
+ nvs.data(), nvs.size(), provider.get());
+ QUICHE_VLOG(1) << "Submitted response with " << nvs.size()
+ << " response headers; result = " << result;
+ return result;
+}
+
+int NgHttp2Adapter::SubmitTrailer(Http2StreamId stream_id,
+ absl::Span<const Header> trailers) {
+ auto nvs = GetNghttp2Nvs(trailers);
+ int result = nghttp2_submit_trailer(session_->raw_ptr(), stream_id,
+ nvs.data(), nvs.size());
+ QUICHE_VLOG(1) << "Submitted trailers with " << nvs.size()
+ << " response trailers; result = " << result;
+ return result;
+}
+
+void NgHttp2Adapter::SetStreamUserData(Http2StreamId stream_id,
+ void* stream_user_data) {
+ nghttp2_session_set_stream_user_data(session_->raw_ptr(), stream_id,
+ stream_user_data);
+}
+
+void* NgHttp2Adapter::GetStreamUserData(Http2StreamId stream_id) {
+ return nghttp2_session_get_stream_user_data(session_->raw_ptr(), stream_id);
+}
+
+bool NgHttp2Adapter::ResumeStream(Http2StreamId stream_id) {
+ return 0 == nghttp2_session_resume_data(session_->raw_ptr(), stream_id);
+}
+
NgHttp2Adapter::NgHttp2Adapter(Http2VisitorInterface& visitor,
- Perspective perspective)
- : Http2Adapter(visitor), visitor_(visitor), perspective_(perspective) {}
+ Perspective perspective,
+ const nghttp2_option* options)
+ : Http2Adapter(visitor),
+ visitor_(visitor),
+ options_(options),
+ perspective_(perspective) {}
NgHttp2Adapter::~NgHttp2Adapter() {}
void NgHttp2Adapter::Initialize() {
- nghttp2_option* options;
- nghttp2_option_new(&options);
- // Set some common options for compatibility.
- nghttp2_option_set_no_closed_streams(options, 1);
- nghttp2_option_set_no_auto_window_update(options, 1);
- nghttp2_option_set_max_send_header_block_length(options, 0x2000000);
- nghttp2_option_set_max_outbound_ack(options, 10000);
-
- session_ =
- absl::make_unique<NgHttp2Session>(perspective_, callbacks::Create(),
- options, static_cast<void*>(&visitor_));
+ nghttp2_option* owned_options = nullptr;
+ if (options_ == nullptr) {
+ nghttp2_option_new(&owned_options);
+ // Set some common options for compatibility.
+ nghttp2_option_set_no_closed_streams(owned_options, 1);
+ nghttp2_option_set_no_auto_window_update(owned_options, 1);
+ nghttp2_option_set_max_send_header_block_length(owned_options, 0x2000000);
+ nghttp2_option_set_max_outbound_ack(owned_options, 10000);
+ nghttp2_option_set_user_recv_extension_type(owned_options,
+ kMetadataFrameType);
+ options_ = owned_options;
+ }
+
+ session_ = absl::make_unique<NgHttp2Session>(perspective_,
+ callbacks::Create(), options_,
+ static_cast<void*>(&visitor_));
+ if (owned_options != nullptr) {
+ nghttp2_option_del(owned_options);
+ }
+ options_ = nullptr;
}
} // namespace adapter
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.h
index 13c2ffcc3f4..151f9a7ae5a 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.h
@@ -1,35 +1,34 @@
#ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_ADAPTER_H_
#define QUICHE_HTTP2_ADAPTER_NGHTTP2_ADAPTER_H_
+#include "absl/container/flat_hash_map.h"
#include "http2/adapter/http2_adapter.h"
#include "http2/adapter/http2_protocol.h"
#include "http2/adapter/nghttp2_session.h"
#include "http2/adapter/nghttp2_util.h"
+#include "common/platform/api/quiche_export.h"
namespace http2 {
namespace adapter {
-class NgHttp2Adapter : public Http2Adapter {
+class QUICHE_EXPORT_PRIVATE NgHttp2Adapter : public Http2Adapter {
public:
~NgHttp2Adapter() override;
- // Creates an adapter that functions as a client.
+ // Creates an adapter that functions as a client. Does not take ownership of
+ // |options|.
static std::unique_ptr<NgHttp2Adapter> CreateClientAdapter(
- Http2VisitorInterface& visitor);
+ Http2VisitorInterface& visitor, const nghttp2_option* options = nullptr);
- // Creates an adapter that functions as a server.
+ // Creates an adapter that functions as a server. Does not take ownership of
+ // |options|.
static std::unique_ptr<NgHttp2Adapter> CreateServerAdapter(
- Http2VisitorInterface& visitor);
+ Http2VisitorInterface& visitor, const nghttp2_option* options = nullptr);
- // Processes the incoming |bytes| as HTTP/2 and invokes callbacks on the
- // |visitor_| as appropriate.
- ssize_t ProcessBytes(absl::string_view bytes) override;
+ bool IsServerSession() const override;
- // Submits the |settings| to be written to the peer, e.g., as part of the
- // HTTP/2 connection preface.
+ ssize_t ProcessBytes(absl::string_view bytes) override;
void SubmitSettings(absl::Span<const Http2Setting> settings) override;
-
- // Submits a PRIORITY frame for the given stream.
void SubmitPriorityForStream(Http2StreamId stream_id,
Http2StreamId parent_stream_id,
int weight,
@@ -40,47 +39,58 @@ class NgHttp2Adapter : public Http2Adapter {
// this method to originate PINGs. See nghttp2_option_set_no_auto_ping_ack().
void SubmitPing(Http2PingId ping_id) override;
- // Submits a GOAWAY on the connection. Note that |last_accepted_stream_id|
- // refers to stream IDs initiated by the peer. For client-side, this last
- // stream ID must be even (or 0); for server-side, this last stream ID must be
- // odd (or 0).
- // TODO(birenroy): Add a graceful shutdown behavior to the API.
+ void SubmitShutdownNotice() override;
void SubmitGoAway(Http2StreamId last_accepted_stream_id,
Http2ErrorCode error_code,
absl::string_view opaque_data) override;
- // Submits a WINDOW_UPDATE for the given stream (a |stream_id| of 0 indicates
- // a connection-level WINDOW_UPDATE).
void SubmitWindowUpdate(Http2StreamId stream_id,
int window_increment) override;
- // Submits a METADATA frame for the given stream (a |stream_id| of 0 indicates
- // connection-level METADATA). If |end_metadata|, the frame will also have the
- // END_METADATA flag set.
- void SubmitMetadata(Http2StreamId stream_id, bool end_metadata) override;
+ void SubmitRst(Http2StreamId stream_id, Http2ErrorCode error_code) override;
+
+ void SubmitMetadata(Http2StreamId stream_id,
+ std::unique_ptr<MetadataSource> source) override;
+
+ int Send() override;
+
+ int GetSendWindowSize() const override;
+ int GetStreamSendWindowSize(Http2StreamId stream_id) const override;
- // Returns serialized bytes for writing to the wire. Writes should be
- // submitted to Nghttp2Adapter first, so that Nghttp2Adapter has data to
- // serialize and return in this method.
- std::string GetBytesToWrite(absl::optional<size_t> max_bytes) override;
+ int GetStreamReceiveWindowLimit(Http2StreamId stream_id) const override;
+ int GetStreamReceiveWindowSize(Http2StreamId stream_id) const override;
+ int GetReceiveWindowSize() const override;
- // Returns the connection-level flow control window for the peer.
- int GetPeerConnectionWindow() const override;
+ int GetHpackEncoderDynamicTableSize() const override;
+ int GetHpackDecoderDynamicTableSize() const override;
+
+ Http2StreamId GetHighestReceivedStreamId() const override;
- // Marks the given amount of data as consumed for the given stream, which
- // enables the nghttp2 layer to trigger WINDOW_UPDATEs as appropriate.
void MarkDataConsumedForStream(Http2StreamId stream_id,
size_t num_bytes) override;
- // Submits a RST_STREAM with the desired |error_code|.
- void SubmitRst(Http2StreamId stream_id, Http2ErrorCode error_code) override;
+ int32_t SubmitRequest(absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source,
+ void* user_data) override;
+
+ int SubmitResponse(Http2StreamId stream_id, absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source) override;
+
+ int SubmitTrailer(Http2StreamId stream_id,
+ absl::Span<const Header> trailers) override;
+
+ void SetStreamUserData(Http2StreamId stream_id, void* user_data) override;
+ void* GetStreamUserData(Http2StreamId stream_id) override;
+
+ bool ResumeStream(Http2StreamId stream_id) override;
// TODO(b/181586191): Temporary accessor until equivalent functionality is
// available in this adapter class.
NgHttp2Session& session() { return *session_; }
private:
- NgHttp2Adapter(Http2VisitorInterface& visitor, Perspective perspective);
+ NgHttp2Adapter(Http2VisitorInterface& visitor, Perspective perspective,
+ const nghttp2_option* options);
// Performs any necessary initialization of the underlying HTTP/2 session,
// such as preparing initial SETTINGS.
@@ -88,7 +98,10 @@ class NgHttp2Adapter : public Http2Adapter {
std::unique_ptr<NgHttp2Session> session_;
Http2VisitorInterface& visitor_;
+ const nghttp2_option* options_;
Perspective perspective_;
+
+ absl::flat_hash_map<int32_t, std::unique_ptr<DataFrameSource>> sources_;
};
} // namespace adapter
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter_test.cc
index 3aec115dbeb..785e476c94d 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter_test.cc
@@ -1,6 +1,7 @@
#include "http2/adapter/nghttp2_adapter.h"
#include "http2/adapter/mock_http2_visitor.h"
+#include "http2/adapter/nghttp2_test_utils.h"
#include "http2/adapter/test_frame_sequence.h"
#include "http2/adapter/test_utils.h"
#include "common/platform/api/quiche_test.h"
@@ -22,21 +23,46 @@ enum FrameType {
PING,
GOAWAY,
WINDOW_UPDATE,
+ CONTINUATION,
};
+// This send callback assumes |source|'s pointer is a TestDataSource, and
+// |user_data| is a Http2VisitorInterface.
+int TestSendCallback(nghttp2_session*, nghttp2_frame* /*frame*/,
+ const uint8_t* framehd, size_t length,
+ nghttp2_data_source* source, void* user_data) {
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ // Send the frame header via the visitor.
+ ssize_t result = visitor->OnReadyToSend(ToStringView(framehd, 9));
+ if (result == 0) {
+ return NGHTTP2_ERR_WOULDBLOCK;
+ }
+ auto* test_source = static_cast<TestDataSource*>(source->ptr);
+ absl::string_view payload = test_source->ReadNext(length);
+ // Send the frame payload via the visitor.
+ visitor->OnReadyToSend(payload);
+ return 0;
+}
+
TEST(NgHttp2AdapterTest, ClientConstruction) {
testing::StrictMock<MockHttp2Visitor> visitor;
auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
ASSERT_NE(nullptr, adapter);
EXPECT_TRUE(adapter->session().want_read());
EXPECT_FALSE(adapter->session().want_write());
+ EXPECT_FALSE(adapter->IsServerSession());
}
TEST(NgHttp2AdapterTest, ClientHandlesFrames) {
- testing::StrictMock<MockHttp2Visitor> visitor;
+ DataSavingVisitor visitor;
auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
- std::string serialized = adapter->GetBytesToWrite(absl::nullopt);
- EXPECT_THAT(serialized, testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix));
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(),
+ testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix));
+ visitor.Clear();
+
+ EXPECT_EQ(0, adapter->GetHighestReceivedStreamId());
const std::string initial_frames = TestFrameSequence()
.ServerPreface()
@@ -58,56 +84,95 @@ TEST(NgHttp2AdapterTest, ClientHandlesFrames) {
const ssize_t initial_result = adapter->ProcessBytes(initial_frames);
EXPECT_EQ(initial_frames.size(), initial_result);
- EXPECT_EQ(adapter->GetPeerConnectionWindow(),
- kDefaultInitialStreamWindowSize + 1000);
+ EXPECT_EQ(adapter->GetSendWindowSize(), kInitialFlowControlWindowSize + 1000);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, 8, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(PING, 0, 8, 0x1, 0));
+
+ result = adapter->Send();
// Some bytes should have been serialized.
- serialized = adapter->GetBytesToWrite(absl::nullopt);
- EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
- spdy::SpdyFrameType::PING}));
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::PING}));
+ visitor.Clear();
const std::vector<const Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
- const auto nvs1 = GetNghttp2Nvs(headers1);
const std::vector<const Header> headers2 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/two"}});
- const auto nvs2 = GetNghttp2Nvs(headers2);
const std::vector<const Header> headers3 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/three"}});
- const auto nvs3 = GetNghttp2Nvs(headers3);
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const char* kSentinel3 = "arbitrary pointer 3";
const int32_t stream_id1 =
- nghttp2_submit_request(adapter->session().raw_ptr(), nullptr, nvs1.data(),
- nvs1.size(), nullptr, nullptr);
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
ASSERT_GT(stream_id1, 0);
QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
- const int32_t stream_id2 =
- nghttp2_submit_request(adapter->session().raw_ptr(), nullptr, nvs2.data(),
- nvs2.size(), nullptr, nullptr);
+ const int32_t stream_id2 = adapter->SubmitRequest(headers2, nullptr, nullptr);
ASSERT_GT(stream_id2, 0);
QUICHE_LOG(INFO) << "Created stream: " << stream_id2;
const int32_t stream_id3 =
- nghttp2_submit_request(adapter->session().raw_ptr(), nullptr, nvs3.data(),
- nvs3.size(), nullptr, nullptr);
+ adapter->SubmitRequest(headers3, nullptr, const_cast<char*>(kSentinel3));
ASSERT_GT(stream_id3, 0);
QUICHE_LOG(INFO) << "Created stream: " << stream_id3;
- serialized = adapter->GetBytesToWrite(absl::nullopt);
- EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS,
- spdy::SpdyFrameType::HEADERS,
- spdy::SpdyFrameType::HEADERS}));
+ const char* kSentinel2 = "arbitrary pointer 2";
+ adapter->SetStreamUserData(stream_id2, const_cast<char*>(kSentinel2));
+ adapter->SetStreamUserData(stream_id3, nullptr);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 0x5, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id3, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id3, _, 0x5, 0));
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS,
+ spdy::SpdyFrameType::HEADERS,
+ spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ // All streams are active and have not yet received any data, so the receive
+ // window should be at the initial value.
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowSize(stream_id1));
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowSize(stream_id2));
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowSize(stream_id3));
+
+ // Upper bound on the flow control receive window should be the initial value.
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowLimit(stream_id1));
+
+ // Connection has not yet received any data.
+ EXPECT_EQ(kInitialFlowControlWindowSize, adapter->GetReceiveWindowSize());
+
+ EXPECT_EQ(0, adapter->GetHighestReceivedStreamId());
+
+ EXPECT_EQ(kSentinel1, adapter->GetStreamUserData(stream_id1));
+ EXPECT_EQ(kSentinel2, adapter->GetStreamUserData(stream_id2));
+ EXPECT_EQ(nullptr, adapter->GetStreamUserData(stream_id3));
+
+ EXPECT_EQ(0, adapter->GetHpackDecoderDynamicTableSize());
const std::string stream_frames =
TestFrameSequence()
@@ -140,6 +205,29 @@ TEST(NgHttp2AdapterTest, ClientHandlesFrames) {
const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
EXPECT_EQ(stream_frames.size(), stream_result);
+ // First stream has received some data.
+ EXPECT_GT(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowSize(stream_id1));
+ // Second stream was closed.
+ EXPECT_EQ(-1, adapter->GetStreamReceiveWindowSize(stream_id2));
+ // Third stream has not received any data.
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowSize(stream_id3));
+
+ // Connection window should be the same as the first stream.
+ EXPECT_EQ(adapter->GetReceiveWindowSize(),
+ adapter->GetStreamReceiveWindowSize(stream_id1));
+
+ // Upper bound on the flow control receive window should still be the initial
+ // value.
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowLimit(stream_id1));
+
+ EXPECT_GT(adapter->GetHpackDecoderDynamicTableSize(), 0);
+
+ // Should be 3, but this method only works for server adapters.
+ EXPECT_EQ(0, adapter->GetHighestReceivedStreamId());
+
// Even though the client recieved a GOAWAY, streams 1 and 5 are still active.
EXPECT_TRUE(adapter->session().want_read());
@@ -154,14 +242,801 @@ TEST(NgHttp2AdapterTest, ClientHandlesFrames) {
.Data(1, "", true)
.RstStream(5, Http2ErrorCode::REFUSED_STREAM)
.Serialize());
+
+ // Should be 5, but this method only works for server adapters.
+ EXPECT_EQ(0, adapter->GetHighestReceivedStreamId());
+
// After receiving END_STREAM for 1 and RST_STREAM for 5, the session no
// longer expects reads.
EXPECT_FALSE(adapter->session().want_read());
// Client will not have anything else to write.
EXPECT_FALSE(adapter->session().want_write());
- serialized = adapter->GetBytesToWrite(absl::nullopt);
- EXPECT_THAT(serialized, testing::IsEmpty());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), testing::IsEmpty());
+}
+
+TEST(NgHttp2AdapterTest, ClientHandlesTrailers) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .Headers(1, {{"final-status", "A-OK"}},
+ /*fin=*/true)
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor,
+ OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 0));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, 26));
+ EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "final-status", "A-OK"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndStream(1));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+
+ const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+}
+
+TEST(NgHttp2AdapterTest, ClientHandlesMetadata) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Metadata(0, "Example connection metadata")
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Metadata(1, "Example stream metadata")
+ .Data(1, "This is the response body.", true)
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnMetadataEndForStream(0));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor,
+ OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnMetadataEndForStream(1));
+ EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 1));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, 26));
+ EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
+ EXPECT_CALL(visitor, OnEndStream(1));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+
+ const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+}
+
+TEST(NgHttp2AdapterTest, ClientHandlesInvalidTrailers) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .Headers(1, {{":bad-status", "9000"}},
+ /*fin=*/true)
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor,
+ OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 0));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, 26));
+ EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(
+ visitor,
+ OnErrorDebug("Invalid HTTP header field was received: frame type: 1, "
+ "stream: 1, name: [:bad-status], value: [9000]"));
+ EXPECT_CALL(visitor, OnInvalidFrame(1, -531));
+
+ // Bad status trailer will cause a PROTOCOL_ERROR. The header is never
+ // delivered in an OnHeaderForStream callback.
+
+ const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id1, 4, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, stream_id1, 4, 0x0, 1));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::PROTOCOL_ERROR));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::RST_STREAM}));
+}
+
+TEST(NgHttp2AdapterTest, ClientRstStreamWhileHandlingHeaders) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor,
+ OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"))
+ .WillOnce(testing::DoAll(
+ testing::InvokeWithoutArgs([&adapter]() {
+ adapter->SubmitRst(1, Http2ErrorCode::REFUSED_STREAM);
+ }),
+ testing::Return(Http2VisitorInterface::HEADER_RST_STREAM)));
+
+ const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id1, 4, 0x0));
+ EXPECT_CALL(visitor,
+ OnFrameSent(RST_STREAM, stream_id1, 4, 0x0,
+ static_cast<int>(Http2ErrorCode::REFUSED_STREAM)));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::REFUSED_STREAM));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::RST_STREAM}));
+}
+
+TEST(NgHttp2AdapterTest, ClientConnectionErrorWhileHandlingHeaders) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor,
+ OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"))
+ .WillOnce(
+ testing::Return(Http2VisitorInterface::HEADER_CONNECTION_ERROR));
+ EXPECT_CALL(visitor, OnConnectionError());
+
+ const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(-902 /* NGHTTP2_ERR_CALLBACK_FAILURE */, stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+}
+
+TEST(NgHttp2AdapterTest, ClientRejectsHeaders) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1))
+ .WillOnce(testing::Return(false));
+ // Rejecting headers leads to a connection error.
+ EXPECT_CALL(visitor, OnConnectionError());
+
+ const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+}
+
+TEST(NgHttp2AdapterTest, ClientSubmitRequest) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ // Client preface does not appear to include the mandatory SETTINGS frame.
+ EXPECT_THAT(visitor.data(),
+ testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix));
+ visitor.Clear();
+
+ const std::string initial_frames =
+ TestFrameSequence().ServerPreface().Serialize();
+ testing::InSequence s;
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ const ssize_t initial_result = adapter->ProcessBytes(initial_frames);
+ EXPECT_EQ(initial_frames.size(), initial_result);
+
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+ visitor.Clear();
+
+ EXPECT_EQ(0, adapter->GetHpackEncoderDynamicTableSize());
+ EXPECT_FALSE(adapter->session().want_write());
+ const char* kSentinel = "";
+ const absl::string_view kBody = "This is an example request body.";
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true);
+ body1->AppendPayload(kBody);
+ body1->EndData();
+ int stream_id =
+ adapter->SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ std::move(body1), const_cast<char*>(kSentinel));
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0));
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowSize(stream_id));
+ EXPECT_EQ(kInitialFlowControlWindowSize, adapter->GetReceiveWindowSize());
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowLimit(stream_id));
+
+ EXPECT_GT(adapter->GetHpackEncoderDynamicTableSize(), 0);
+
+ // Some data was sent, so the remaining send window size should be less than
+ // the default.
+ EXPECT_LT(adapter->GetStreamSendWindowSize(stream_id),
+ kInitialFlowControlWindowSize);
+ EXPECT_GT(adapter->GetStreamSendWindowSize(stream_id), 0);
+ // Send window for a nonexistent stream is not available.
+ EXPECT_EQ(-1, adapter->GetStreamSendWindowSize(stream_id + 2));
+
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS,
+ spdy::SpdyFrameType::DATA}));
+ EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody));
+ visitor.Clear();
+ EXPECT_FALSE(adapter->session().want_write());
+
+ stream_id =
+ adapter->SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ nullptr, nullptr);
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+ const char* kSentinel2 = "arbitrary pointer 2";
+ EXPECT_EQ(nullptr, adapter->GetStreamUserData(stream_id));
+ adapter->SetStreamUserData(stream_id, const_cast<char*>(kSentinel2));
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x5, 0));
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+
+ EXPECT_EQ(kSentinel2, adapter->GetStreamUserData(stream_id));
+
+ // No data was sent (just HEADERS), so the remaining send window size should
+ // still be the default.
+ EXPECT_EQ(adapter->GetStreamSendWindowSize(stream_id),
+ kInitialFlowControlWindowSize);
+}
+
+// This is really a test of the MakeZeroCopyDataFrameSource adapter, but I
+// wasn't sure where else to put it.
+TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProvider) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ // Client preface does not appear to include the mandatory SETTINGS frame.
+ EXPECT_THAT(visitor.data(),
+ testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix));
+ visitor.Clear();
+
+ const std::string initial_frames =
+ TestFrameSequence().ServerPreface().Serialize();
+ testing::InSequence s;
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ const ssize_t initial_result = adapter->ProcessBytes(initial_frames);
+ EXPECT_EQ(initial_frames.size(), initial_result);
+
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+ visitor.Clear();
+
+ EXPECT_FALSE(adapter->session().want_write());
+ const absl::string_view kBody = "This is an example request body.";
+ // This test will use TestDataSource as the source of the body payload data.
+ TestDataSource body1{kBody};
+ // The TestDataSource is wrapped in the nghttp2_data_provider data type.
+ nghttp2_data_provider provider = body1.MakeDataProvider();
+ nghttp2_send_data_callback send_callback = &TestSendCallback;
+
+ // This call transforms it back into a DataFrameSource, which is compatible
+ // with the Http2Adapter API.
+ std::unique_ptr<DataFrameSource> frame_source =
+ MakeZeroCopyDataFrameSource(provider, &visitor, std::move(send_callback));
+ int stream_id =
+ adapter->SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ std::move(frame_source), nullptr);
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0));
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS,
+ spdy::SpdyFrameType::DATA}));
+ EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody));
+ EXPECT_FALSE(adapter->session().want_write());
+}
+
+// This test verifies how nghttp2 behaves when a data source becomes
+// read-blocked.
+TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProviderAndReadBlock) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+ const absl::string_view kBody = "This is an example request body.";
+ // This test will use TestDataSource as the source of the body payload data.
+ TestDataSource body1{kBody};
+ body1.set_is_data_available(false);
+ // The TestDataSource is wrapped in the nghttp2_data_provider data type.
+ nghttp2_data_provider provider = body1.MakeDataProvider();
+ nghttp2_send_data_callback send_callback = &TestSendCallback;
+
+ // This call transforms it back into a DataFrameSource, which is compatible
+ // with the Http2Adapter API.
+ std::unique_ptr<DataFrameSource> frame_source =
+ MakeZeroCopyDataFrameSource(provider, &visitor, std::move(send_callback));
+ int stream_id =
+ adapter->SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ std::move(frame_source), nullptr);
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ // Client preface does not appear to include the mandatory SETTINGS frame.
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+ EXPECT_FALSE(adapter->session().want_write());
+
+ // Resume the deferred stream.
+ body1.set_is_data_available(true);
+ EXPECT_TRUE(adapter->ResumeStream(stream_id));
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0));
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::DATA}));
+ EXPECT_FALSE(adapter->session().want_write());
+
+ // Stream data is done, so this stream cannot be resumed.
+ EXPECT_FALSE(adapter->ResumeStream(stream_id));
+ EXPECT_FALSE(adapter->session().want_write());
+}
+
+// This test verifies how nghttp2 behaves when a data source is read block, then
+// ends with an empty DATA frame.
+TEST(NgHttp2AdapterTest, ClientSubmitRequestEmptyDataWithFin) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+ const absl::string_view kEmptyBody = "";
+ // This test will use TestDataSource as the source of the body payload data.
+ TestDataSource body1{kEmptyBody};
+ body1.set_is_data_available(false);
+ // The TestDataSource is wrapped in the nghttp2_data_provider data type.
+ nghttp2_data_provider provider = body1.MakeDataProvider();
+ nghttp2_send_data_callback send_callback = &TestSendCallback;
+
+ // This call transforms it back into a DataFrameSource, which is compatible
+ // with the Http2Adapter API.
+ std::unique_ptr<DataFrameSource> frame_source =
+ MakeZeroCopyDataFrameSource(provider, &visitor, std::move(send_callback));
+ int stream_id =
+ adapter->SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ std::move(frame_source), nullptr);
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ // Client preface does not appear to include the mandatory SETTINGS frame.
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+ EXPECT_FALSE(adapter->session().want_write());
+
+ // Resume the deferred stream.
+ body1.set_is_data_available(true);
+ EXPECT_TRUE(adapter->ResumeStream(stream_id));
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, 0, 0x1, 0));
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::DATA}));
+ EXPECT_FALSE(adapter->session().want_write());
+
+ // Stream data is done, so this stream cannot be resumed.
+ EXPECT_FALSE(adapter->ResumeStream(stream_id));
+ EXPECT_FALSE(adapter->session().want_write());
+}
+
+// This test verifies how nghttp2 behaves when a connection becomes
+// write-blocked.
+TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProviderAndWriteBlock) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+ const absl::string_view kBody = "This is an example request body.";
+ // This test will use TestDataSource as the source of the body payload data.
+ TestDataSource body1{kBody};
+ // The TestDataSource is wrapped in the nghttp2_data_provider data type.
+ nghttp2_data_provider provider = body1.MakeDataProvider();
+ nghttp2_send_data_callback send_callback = &TestSendCallback;
+
+ // This call transforms it back into a DataFrameSource, which is compatible
+ // with the Http2Adapter API.
+ std::unique_ptr<DataFrameSource> frame_source =
+ MakeZeroCopyDataFrameSource(provider, &visitor, std::move(send_callback));
+ int stream_id =
+ adapter->SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ std::move(frame_source), nullptr);
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ visitor.set_is_write_blocked(true);
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), testing::IsEmpty());
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0));
+
+ visitor.set_is_write_blocked(false);
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+
+ // Client preface does not appear to include the mandatory SETTINGS frame.
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS,
+ spdy::SpdyFrameType::DATA}));
+ EXPECT_FALSE(adapter->session().want_write());
}
TEST(NgHttp2AdapterTest, ServerConstruction) {
@@ -170,12 +1045,16 @@ TEST(NgHttp2AdapterTest, ServerConstruction) {
ASSERT_NE(nullptr, adapter);
EXPECT_TRUE(adapter->session().want_read());
EXPECT_FALSE(adapter->session().want_write());
+ EXPECT_TRUE(adapter->IsServerSession());
}
TEST(NgHttp2AdapterTest, ServerHandlesFrames) {
- testing::StrictMock<MockHttp2Visitor> visitor;
+ DataSavingVisitor visitor;
auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+ EXPECT_EQ(0, adapter->GetHighestReceivedStreamId());
+ EXPECT_EQ(0, adapter->GetHpackDecoderDynamicTableSize());
+
const std::string frames = TestFrameSequence()
.ClientPreface()
.Ping(42)
@@ -199,6 +1078,8 @@ TEST(NgHttp2AdapterTest, ServerHandlesFrames) {
.Serialize();
testing::InSequence s;
+ const char* kSentinel1 = "arbitrary pointer 1";
+
// Client preface (empty SETTINGS)
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
@@ -214,7 +1095,10 @@ TEST(NgHttp2AdapterTest, ServerHandlesFrames) {
EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
- EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1))
+ .WillOnce(testing::InvokeWithoutArgs([&adapter, kSentinel1]() {
+ adapter->SetStreamUserData(1, const_cast<char*>(kSentinel1));
+ }));
EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
EXPECT_CALL(visitor, OnWindowUpdate(1, 2000));
EXPECT_CALL(visitor, OnFrameHeader(1, 25, DATA, 0));
@@ -237,16 +1121,506 @@ TEST(NgHttp2AdapterTest, ServerHandlesFrames) {
const ssize_t result = adapter->ProcessBytes(frames);
EXPECT_EQ(frames.size(), result);
- EXPECT_EQ(adapter->GetPeerConnectionWindow(),
- kDefaultInitialStreamWindowSize + 1000);
+ EXPECT_EQ(kSentinel1, adapter->GetStreamUserData(1));
+
+ EXPECT_GT(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowSize(1));
+ EXPECT_EQ(adapter->GetStreamReceiveWindowSize(1),
+ adapter->GetReceiveWindowSize());
+ // Upper bound should still be the original value.
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ adapter->GetStreamReceiveWindowLimit(1));
+
+ EXPECT_GT(adapter->GetHpackDecoderDynamicTableSize(), 0);
+
+ // Because stream 3 has already been closed, it's not possible to set user
+ // data.
+ const char* kSentinel3 = "another arbitrary pointer";
+ adapter->SetStreamUserData(3, const_cast<char*>(kSentinel3));
+ EXPECT_EQ(nullptr, adapter->GetStreamUserData(3));
+
+ EXPECT_EQ(3, adapter->GetHighestReceivedStreamId());
+
+ EXPECT_EQ(adapter->GetSendWindowSize(), kInitialFlowControlWindowSize + 1000);
EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, 8, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(PING, 0, 8, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, 8, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(PING, 0, 8, 0x1, 0));
+
+ int send_result = adapter->Send();
// Some bytes should have been serialized.
- std::string serialized = adapter->GetBytesToWrite(absl::nullopt);
+ EXPECT_EQ(0, send_result);
// SETTINGS ack, two PING acks.
- EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
- spdy::SpdyFrameType::PING,
- spdy::SpdyFrameType::PING}));
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::PING,
+ spdy::SpdyFrameType::PING}));
+}
+
+TEST(NgHttp2AdapterTest, ServerErrorWhileHandlingHeaders) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "POST"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"},
+ {"accept", "some bogus value!"}},
+ /*fin=*/false)
+ .WindowUpdate(1, 2000)
+ .Data(1, "This is the request body.")
+ .WindowUpdate(0, 2000)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "accept", "some bogus value!"))
+ .WillOnce(testing::Return(Http2VisitorInterface::HEADER_RST_STREAM));
+ EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
+ EXPECT_CALL(visitor, OnWindowUpdate(1, 2000));
+ // DATA frame is not delivered to the visitor.
+ EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0));
+ EXPECT_CALL(visitor, OnWindowUpdate(0, 2000));
+
+ const ssize_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0));
+ EXPECT_CALL(visitor,
+ OnFrameSent(RST_STREAM, 1, 4, 0x0,
+ static_cast<int>(Http2ErrorCode::INTERNAL_ERROR)));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::INTERNAL_ERROR));
+
+ int send_result = adapter->Send();
+ // Some bytes should have been serialized.
+ EXPECT_EQ(0, send_result);
+ // SETTINGS ack
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::RST_STREAM}));
+}
+
+TEST(NgHttp2AdapterTest, ServerSubmitResponse) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+ EXPECT_FALSE(adapter->session().want_write());
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/true)
+ .Serialize();
+ testing::InSequence s;
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1))
+ .WillOnce(testing::InvokeWithoutArgs([&adapter, kSentinel1]() {
+ adapter->SetStreamUserData(1, const_cast<char*>(kSentinel1));
+ }));
+ EXPECT_CALL(visitor, OnEndStream(1));
+
+ const ssize_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+
+ EXPECT_EQ(1, adapter->GetHighestReceivedStreamId());
+
+ // Server will want to send a SETTINGS ack.
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+ int send_result = adapter->Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+ visitor.Clear();
+
+ EXPECT_EQ(0, adapter->GetHpackEncoderDynamicTableSize());
+
+ EXPECT_FALSE(adapter->session().want_write());
+ const absl::string_view kBody = "This is an example response body.";
+ // A data fin is not sent so that the stream remains open, and the flow
+ // control state can be verified.
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false);
+ body1->AppendPayload(kBody);
+ int submit_result = adapter->SubmitResponse(
+ 1,
+ ToHeaders({{":status", "404"},
+ {"x-comment", "I have no idea what you're talking about."}}),
+ std::move(body1));
+ EXPECT_EQ(submit_result, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ // Stream user data should have been set successfully after receiving headers.
+ EXPECT_EQ(kSentinel1, adapter->GetStreamUserData(1));
+ adapter->SetStreamUserData(1, nullptr);
+ EXPECT_EQ(nullptr, adapter->GetStreamUserData(1));
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0));
+
+ send_result = adapter->Send();
+ EXPECT_EQ(0, send_result);
+
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS,
+ spdy::SpdyFrameType::DATA}));
+ EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody));
+ EXPECT_FALSE(adapter->session().want_write());
+
+ // Some data was sent, so the remaining send window size should be less than
+ // the default.
+ EXPECT_LT(adapter->GetStreamSendWindowSize(1), kInitialFlowControlWindowSize);
+ EXPECT_GT(adapter->GetStreamSendWindowSize(1), 0);
+ // Send window for a nonexistent stream is not available.
+ EXPECT_EQ(adapter->GetStreamSendWindowSize(3), -1);
+
+ EXPECT_GT(adapter->GetHpackEncoderDynamicTableSize(), 0);
+}
+
+// Should also test: client attempts shutdown, server attempts shutdown after an
+// explicit GOAWAY.
+TEST(NgHttp2AdapterTest, ServerSendsShutdown) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "POST"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/false)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+
+ const ssize_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+
+ adapter->SubmitShutdownNotice();
+
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, 0));
+
+ int send_result = adapter->Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::GOAWAY}));
+}
+
+TEST(NgHttp2AdapterTest, ServerSendsTrailers) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+ EXPECT_FALSE(adapter->session().want_write());
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndStream(1));
+
+ const ssize_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+
+ // Server will want to send a SETTINGS ack.
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+ int send_result = adapter->Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+ visitor.Clear();
+
+ EXPECT_FALSE(adapter->session().want_write());
+ const absl::string_view kBody = "This is an example response body.";
+
+ // The body source must indicate that the end of the body is not the end of
+ // the stream.
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false);
+ body1->AppendPayload(kBody);
+ body1->EndData();
+ int submit_result = adapter->SubmitResponse(
+ 1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}),
+ std::move(body1));
+ EXPECT_EQ(submit_result, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0));
+
+ send_result = adapter->Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS,
+ spdy::SpdyFrameType::DATA}));
+ EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody));
+ visitor.Clear();
+ EXPECT_FALSE(adapter->session().want_write());
+
+ // The body source has been exhausted by the call to Send() above.
+ int trailer_result = adapter->SubmitTrailer(
+ 1, ToHeaders({{"final-status", "a-ok"},
+ {"x-comment", "trailers sure are cool"}}));
+ ASSERT_EQ(trailer_result, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+
+ send_result = adapter->Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+}
+
+TEST(NgHttp2AdapterTest, ClientSendsContinuation) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+ EXPECT_FALSE(adapter->session().want_write());
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/true,
+ /*add_continuation=*/true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 1));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, 4));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndStream(1));
+
+ const size_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+}
+
+TEST(NgHttp2AdapterTest, ClientSendsMetadataWithContinuation) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+ EXPECT_FALSE(adapter->session().want_write());
+
+ const std::string frames =
+ TestFrameSequence()
+ .ClientPreface()
+ .Metadata(0, "Example connection metadata in multiple frames", true)
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/false,
+ /*add_continuation=*/true)
+ .Metadata(1,
+ "Some stream metadata that's also sent in multiple frames",
+ true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Metadata on stream 0
+ EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 0));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnMetadataEndForStream(0));
+
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, 4));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ // Metadata on stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 0));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnMetadataEndForStream(1));
+
+ const size_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+ EXPECT_EQ(TestFrameSequence::MetadataBlockForPayload(
+ "Example connection metadata in multiple frames"),
+ absl::StrJoin(visitor.GetMetadata(0), ""));
+ EXPECT_EQ(TestFrameSequence::MetadataBlockForPayload(
+ "Some stream metadata that's also sent in multiple frames"),
+ absl::StrJoin(visitor.GetMetadata(1), ""));
+}
+
+TEST(NgHttp2AdapterTest, ServerSendsInvalidTrailers) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+ EXPECT_FALSE(adapter->session().want_write());
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndStream(1));
+
+ const ssize_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+
+ const absl::string_view kBody = "This is an example response body.";
+
+ // The body source must indicate that the end of the body is not the end of
+ // the stream.
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false);
+ body1->AppendPayload(kBody);
+ body1->EndData();
+ int submit_result = adapter->SubmitResponse(
+ 1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}),
+ std::move(body1));
+ EXPECT_EQ(submit_result, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0));
+
+ int send_result = adapter->Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::HEADERS,
+ spdy::SpdyFrameType::DATA}));
+ EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody));
+ visitor.Clear();
+ EXPECT_FALSE(adapter->session().want_write());
+
+ // The body source has been exhausted by the call to Send() above.
+ int trailer_result =
+ adapter->SubmitTrailer(1, ToHeaders({{":final-status", "a-ok"}}));
+ ASSERT_EQ(trailer_result, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+
+ send_result = adapter->Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS}));
}
} // namespace
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.cc
index 8774873c95f..4a2221ea2ee 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.cc
@@ -6,6 +6,7 @@
#include "absl/strings/string_view.h"
#include "http2/adapter/http2_protocol.h"
#include "http2/adapter/http2_visitor_interface.h"
+#include "http2/adapter/nghttp2_data_provider.h"
#include "http2/adapter/nghttp2_util.h"
#include "third_party/nghttp2/nghttp2.h"
#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
@@ -16,14 +17,33 @@ namespace http2 {
namespace adapter {
namespace callbacks {
+ssize_t OnReadyToSend(nghttp2_session* /* session */, const uint8_t* data,
+ size_t length, int flags, void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ const ssize_t result = visitor->OnReadyToSend(ToStringView(data, length));
+ QUICHE_VLOG(1) << "OnReadyToSend(length=" << length << ", flags=" << flags
+ << ") returning " << result;
+ if (result > 0) {
+ return result;
+ } else if (result == Http2VisitorInterface::kSendBlocked) {
+ return -504; // NGHTTP2_ERR_WOULDBLOCK
+ } else {
+ return -902; // NGHTTP2_ERR_CALLBACK_FAILURE
+ }
+}
+
int OnBeginFrame(nghttp2_session* /* session */,
const nghttp2_frame_hd* header,
void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
visitor->OnFrameHeader(header->stream_id, header->length, header->type,
header->flags);
if (header->type == NGHTTP2_DATA) {
visitor->OnBeginDataForStream(header->stream_id, header->length);
+ } else if (header->type == kMetadataFrameType) {
+ visitor->OnBeginMetadataForStream(header->stream_id, header->length);
}
return 0;
}
@@ -31,10 +51,9 @@ int OnBeginFrame(nghttp2_session* /* session */,
int OnFrameReceived(nghttp2_session* /* session */,
const nghttp2_frame* frame,
void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
const Http2StreamId stream_id = frame->hd.stream_id;
- QUICHE_VLOG(2) << "Frame " << static_cast<int>(frame->hd.type)
- << " for stream " << stream_id;
switch (frame->hd.type) {
// The beginning of the DATA frame is handled in OnBeginFrame(), and the
// beginning of the header block is handled in client/server-specific
@@ -71,7 +90,7 @@ int OnFrameReceived(nghttp2_session* /* session */,
visitor->OnSettingsAck();
} else {
visitor->OnSettingsStart();
- for (int i = 0; i < frame->settings.niv; ++i) {
+ for (size_t i = 0; i < frame->settings.niv; ++i) {
nghttp2_settings_entry entry = frame->settings.iv[i];
// The nghttp2_settings_entry uses int32_t for the ID; we must cast.
visitor->OnSetting(Http2Setting{
@@ -129,29 +148,67 @@ int OnFrameReceived(nghttp2_session* /* session */,
int OnBeginHeaders(nghttp2_session* /* session */,
const nghttp2_frame* frame,
void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
- visitor->OnBeginHeadersForStream(frame->hd.stream_id);
- return 0;
+ const bool result = visitor->OnBeginHeadersForStream(frame->hd.stream_id);
+ return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
}
-int OnHeader(nghttp2_session* /* session */,
- const nghttp2_frame* frame,
- nghttp2_rcbuf* name,
- nghttp2_rcbuf* value,
- uint8_t flags,
+int OnHeader(nghttp2_session* /* session */, const nghttp2_frame* frame,
+ nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t /*flags*/,
void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
- visitor->OnHeaderForStream(frame->hd.stream_id, ToStringView(name),
- ToStringView(value));
- return 0;
+ const Http2VisitorInterface::OnHeaderResult result =
+ visitor->OnHeaderForStream(frame->hd.stream_id, ToStringView(name),
+ ToStringView(value));
+ switch (result) {
+ case Http2VisitorInterface::HEADER_OK:
+ return 0;
+ case Http2VisitorInterface::HEADER_CONNECTION_ERROR:
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ case Http2VisitorInterface::HEADER_RST_STREAM:
+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+ }
}
-int OnDataChunk(nghttp2_session* /* session */,
- uint8_t flags,
- Http2StreamId stream_id,
- const uint8_t* data,
- size_t len,
+int OnBeforeFrameSent(nghttp2_session* /* session */,
+ const nghttp2_frame* frame, void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
+ LogBeforeSend(*frame);
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ return visitor->OnBeforeFrameSent(frame->hd.type, frame->hd.stream_id,
+ frame->hd.length, frame->hd.flags);
+}
+
+int OnFrameSent(nghttp2_session* /* session */, const nghttp2_frame* frame,
void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ uint32_t error_code = 0;
+ if (frame->hd.type == NGHTTP2_RST_STREAM) {
+ error_code = frame->rst_stream.error_code;
+ } else if (frame->hd.type == NGHTTP2_GOAWAY) {
+ error_code = frame->goaway.error_code;
+ }
+ return visitor->OnFrameSent(frame->hd.type, frame->hd.stream_id,
+ frame->hd.length, frame->hd.flags, error_code);
+}
+
+int OnInvalidFrameReceived(nghttp2_session* /* session */,
+ const nghttp2_frame* frame, int lib_error_code,
+ void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ const bool result =
+ visitor->OnInvalidFrame(frame->hd.stream_id, lib_error_code);
+ return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
+}
+
+int OnDataChunk(nghttp2_session* /* session */, uint8_t /*flags*/,
+ Http2StreamId stream_id, const uint8_t* data, size_t len,
+ void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
visitor->OnDataForStream(
stream_id, absl::string_view(reinterpret_cast<const char*>(data), len));
@@ -162,34 +219,63 @@ int OnStreamClosed(nghttp2_session* /* session */,
Http2StreamId stream_id,
uint32_t error_code,
void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
visitor->OnCloseStream(stream_id, ToHttp2ErrorCode(error_code));
return 0;
}
-ssize_t OnReadyToReadDataForStream(nghttp2_session* /* session */,
- Http2StreamId stream_id,
- uint8_t* dest_buffer,
- size_t max_length,
- uint32_t* data_flags,
- nghttp2_data_source* source,
- void* user_data) {
- auto* visitor = static_cast<Http2VisitorInterface*>(source->ptr);
- ssize_t bytes_to_send = 0;
- bool end_stream = false;
- visitor->OnReadyToSendDataForStream(stream_id,
- reinterpret_cast<char*>(dest_buffer),
- max_length, &bytes_to_send, &end_stream);
- if (bytes_to_send >= 0 && end_stream) {
- *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+int OnExtensionChunkReceived(nghttp2_session* /*session*/,
+ const nghttp2_frame_hd* hd, const uint8_t* data,
+ size_t len, void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ if (hd->type != kMetadataFrameType) {
+ QUICHE_LOG(ERROR) << "Unexpected frame type: "
+ << static_cast<int>(hd->type);
+ return NGHTTP2_ERR_CANCEL;
+ }
+ visitor->OnMetadataForStream(hd->stream_id, ToStringView(data, len));
+ return 0;
+}
+
+int OnUnpackExtensionCallback(nghttp2_session* /*session*/, void** /*payload*/,
+ const nghttp2_frame_hd* hd, void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ if (hd->flags == kMetadataEndFlag) {
+ const bool result = visitor->OnMetadataEndForStream(hd->stream_id);
+ if (!result) {
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
}
- return bytes_to_send;
+ return 0;
+}
+
+ssize_t OnPackExtensionCallback(nghttp2_session* /*session*/, uint8_t* buf,
+ size_t len, const nghttp2_frame* frame,
+ void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ ssize_t written = 0;
+ visitor->OnReadyToSendMetadataForStream(
+ frame->hd.stream_id, reinterpret_cast<char*>(buf), len, &written);
+ return written;
+}
+
+int OnError(nghttp2_session* /*session*/, int /*lib_error_code*/,
+ const char* msg, size_t len, void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ visitor->OnErrorDebug(absl::string_view(msg, len));
+ return 0;
}
nghttp2_session_callbacks_unique_ptr Create() {
nghttp2_session_callbacks* callbacks;
nghttp2_session_callbacks_new(&callbacks);
+ nghttp2_session_callbacks_set_send_callback(callbacks, &OnReadyToSend);
nghttp2_session_callbacks_set_on_begin_frame_callback(callbacks,
&OnBeginFrame);
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
@@ -201,6 +287,21 @@ nghttp2_session_callbacks_unique_ptr Create() {
&OnDataChunk);
nghttp2_session_callbacks_set_on_stream_close_callback(callbacks,
&OnStreamClosed);
+ nghttp2_session_callbacks_set_before_frame_send_callback(callbacks,
+ &OnBeforeFrameSent);
+ nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, &OnFrameSent);
+ nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
+ callbacks, &OnInvalidFrameReceived);
+ nghttp2_session_callbacks_set_error_callback2(callbacks, &OnError);
+ // on_frame_not_send_callback <- just ignored
+ nghttp2_session_callbacks_set_send_data_callback(
+ callbacks, &DataFrameSourceSendCallback);
+ nghttp2_session_callbacks_set_pack_extension_callback(
+ callbacks, &OnPackExtensionCallback);
+ nghttp2_session_callbacks_set_unpack_extension_callback(
+ callbacks, &OnUnpackExtensionCallback);
+ nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
+ callbacks, &OnExtensionChunkReceived);
return MakeCallbacksPtr(callbacks);
}
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.h
index 5fbaee041c5..696b6847509 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.h
@@ -13,6 +13,13 @@ namespace callbacks {
// beginning of its lifetime. It is expected that |user_data| holds an
// Http2VisitorInterface.
+// Callback once the library is ready to send serialized frames.
+ssize_t OnReadyToSend(nghttp2_session* session,
+ const uint8_t* data,
+ size_t length,
+ int flags,
+ void* user_data);
+
// Callback once a frame header has been received.
int OnBeginFrame(nghttp2_session* session, const nghttp2_frame_hd* header,
void* user_data);
@@ -31,7 +38,19 @@ int OnHeader(nghttp2_session* session, const nghttp2_frame* frame,
nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t flags,
void* user_data);
-// Callback once a chunk of data (from a DATA frame payload) has been received.
+// Invoked immediately before sending a frame.
+int OnBeforeFrameSent(nghttp2_session* session, const nghttp2_frame* frame,
+ void* user_data);
+
+// Invoked immediately after a frame is sent.
+int OnFrameSent(nghttp2_session* session, const nghttp2_frame* frame,
+ void* user_data);
+
+// Invoked when an invalid frame is received.
+int OnInvalidFrameReceived(nghttp2_session* session, const nghttp2_frame* frame,
+ int lib_error_code, void* user_data);
+
+// Invoked when a chunk of data (from a DATA frame payload) has been received.
int OnDataChunk(nghttp2_session* session, uint8_t flags,
Http2StreamId stream_id, const uint8_t* data, size_t len,
void* user_data);
@@ -40,13 +59,25 @@ int OnDataChunk(nghttp2_session* session, uint8_t flags,
int OnStreamClosed(nghttp2_session* session, Http2StreamId stream_id,
uint32_t error_code, void* user_data);
-// Callback once nghttp2 is ready to read data from |source| into |dest_buffer|.
-ssize_t OnReadyToReadDataForStream(nghttp2_session* session,
- Http2StreamId stream_id,
- uint8_t* dest_buffer, size_t max_length,
- uint32_t* data_flags,
- nghttp2_data_source* source,
- void* user_data);
+// Invoked when nghttp2 has a chunk of extension frame data to pass to the
+// application.
+int OnExtensionChunkReceived(nghttp2_session* session,
+ const nghttp2_frame_hd* hd, const uint8_t* data,
+ size_t len, void* user_data);
+
+// Invoked when nghttp2 wants the application to unpack an extension payload.
+int OnUnpackExtensionCallback(nghttp2_session* session, void** payload,
+ const nghttp2_frame_hd* hd, void* user_data);
+
+// Invoked when nghttp2 is ready to pack an extension payload. Returns the
+// number of bytes serialized to |buf|.
+ssize_t OnPackExtensionCallback(nghttp2_session* session, uint8_t* buf,
+ size_t len, const nghttp2_frame* frame,
+ void* user_data);
+
+// Invoked when the library has an error message to deliver.
+int OnError(nghttp2_session* session, int lib_error_code, const char* msg,
+ size_t len, void* user_data);
nghttp2_session_callbacks_unique_ptr Create();
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.cc
new file mode 100644
index 00000000000..200aa67ae29
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.cc
@@ -0,0 +1,63 @@
+#include "http2/adapter/nghttp2_data_provider.h"
+
+#include "http2/adapter/http2_visitor_interface.h"
+#include "http2/adapter/nghttp2_util.h"
+
+namespace http2 {
+namespace adapter {
+namespace callbacks {
+
+namespace {
+const size_t kFrameHeaderSize = 9;
+}
+
+ssize_t DataFrameSourceReadCallback(nghttp2_session* /* session */,
+ int32_t /* stream_id */,
+ uint8_t* /* buf */,
+ size_t length,
+ uint32_t* data_flags,
+ nghttp2_data_source* source,
+ void* /* user_data */) {
+ *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
+ auto* frame_source = static_cast<DataFrameSource*>(source->ptr);
+ auto [result_length, done] = frame_source->SelectPayloadLength(length);
+ if (result_length == 0 && !done) {
+ return NGHTTP2_ERR_DEFERRED;
+ } else if (result_length == DataFrameSource::kError) {
+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+ }
+ if (done) {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ }
+ if (!frame_source->send_fin()) {
+ *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
+ }
+ return result_length;
+}
+
+int DataFrameSourceSendCallback(nghttp2_session* /* session */,
+ nghttp2_frame* /* frame */,
+ const uint8_t* framehd,
+ size_t length,
+ nghttp2_data_source* source,
+ void* /* user_data */) {
+ auto* frame_source = static_cast<DataFrameSource*>(source->ptr);
+ frame_source->Send(ToStringView(framehd, kFrameHeaderSize), length);
+ return 0;
+}
+
+} // namespace callbacks
+
+std::unique_ptr<nghttp2_data_provider> MakeDataProvider(
+ DataFrameSource* source) {
+ if (source == nullptr) {
+ return nullptr;
+ }
+ auto provider = absl::make_unique<nghttp2_data_provider>();
+ provider->source.ptr = source;
+ provider->read_callback = &callbacks::DataFrameSourceReadCallback;
+ return provider;
+}
+
+} // namespace adapter
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.h
new file mode 100644
index 00000000000..241bab91cdd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.h
@@ -0,0 +1,37 @@
+#ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_DATA_PROVIDER_H_
+#define QUICHE_HTTP2_ADAPTER_NGHTTP2_DATA_PROVIDER_H_
+
+#include "http2/adapter/data_source.h"
+#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+
+namespace http2 {
+namespace adapter {
+namespace callbacks {
+
+// Assumes |source| is a DataFrameSource.
+ssize_t DataFrameSourceReadCallback(nghttp2_session* /*session */,
+ int32_t /* stream_id */,
+ uint8_t* /* buf */,
+ size_t length,
+ uint32_t* data_flags,
+ nghttp2_data_source* source,
+ void* /* user_data */);
+
+int DataFrameSourceSendCallback(nghttp2_session* /* session */,
+ nghttp2_frame* /* frame */,
+ const uint8_t* framehd,
+ size_t length,
+ nghttp2_data_source* source,
+ void* /* user_data */);
+
+} // namespace callbacks
+
+// Transforms a DataFrameSource into a nghttp2_data_provider. Does not take
+// ownership of |source|. Returns nullptr if |source| is nullptr.
+std::unique_ptr<nghttp2_data_provider> MakeDataProvider(
+ DataFrameSource* source);
+
+} // namespace adapter
+} // namespace http2
+
+#endif // QUICHE_HTTP2_ADAPTER_NGHTTP2_DATA_PROVIDER_H_
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider_test.cc
new file mode 100644
index 00000000000..af8d98187f2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider_test.cc
@@ -0,0 +1,117 @@
+#include "http2/adapter/nghttp2_data_provider.h"
+
+#include "http2/adapter/test_utils.h"
+#include "common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace adapter {
+namespace test {
+
+const size_t kFrameHeaderSize = 9;
+
+// Verifies that a nghttp2_data_provider derived from a DataFrameSource works
+// correctly with nghttp2-style callbacks when the amount of data read is less
+// than what the source provides.
+TEST(DataProviderTest, ReadLessThanSourceProvides) {
+ DataSavingVisitor visitor;
+ TestDataFrameSource source(visitor, true);
+ source.AppendPayload("Example payload");
+ source.EndData();
+ auto provider = MakeDataProvider(&source);
+ uint32_t data_flags = 0;
+ const int32_t kStreamId = 1;
+ const size_t kReadLength = 10;
+ // Read callback selects a payload length given an upper bound.
+ ssize_t result =
+ provider->read_callback(nullptr, kStreamId, nullptr, kReadLength,
+ &data_flags, &provider->source, nullptr);
+ ASSERT_EQ(kReadLength, result);
+ EXPECT_EQ(NGHTTP2_DATA_FLAG_NO_COPY, data_flags);
+
+ const uint8_t framehd[kFrameHeaderSize] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ // Sends the frame header and some payload bytes.
+ int send_result = callbacks::DataFrameSourceSendCallback(
+ nullptr, nullptr, framehd, result, &provider->source, nullptr);
+ EXPECT_EQ(0, send_result);
+ // Data accepted by the visitor includes a frame header and kReadLength bytes
+ // of payload.
+ EXPECT_EQ(visitor.data().size(), kFrameHeaderSize + kReadLength);
+}
+
+// Verifies that a nghttp2_data_provider derived from a DataFrameSource works
+// correctly with nghttp2-style callbacks when the amount of data read is more
+// than what the source provides.
+TEST(DataProviderTest, ReadMoreThanSourceProvides) {
+ DataSavingVisitor visitor;
+ const absl::string_view kPayload = "Example payload";
+ TestDataFrameSource source(visitor, true);
+ source.AppendPayload(kPayload);
+ source.EndData();
+ auto provider = MakeDataProvider(&source);
+ uint32_t data_flags = 0;
+ const int32_t kStreamId = 1;
+ const size_t kReadLength = 30;
+ // Read callback selects a payload length given an upper bound.
+ ssize_t result =
+ provider->read_callback(nullptr, kStreamId, nullptr, kReadLength,
+ &data_flags, &provider->source, nullptr);
+ ASSERT_EQ(kPayload.size(), result);
+ EXPECT_EQ(NGHTTP2_DATA_FLAG_NO_COPY | NGHTTP2_DATA_FLAG_EOF, data_flags);
+
+ const uint8_t framehd[kFrameHeaderSize] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ // Sends the frame header and some payload bytes.
+ int send_result = callbacks::DataFrameSourceSendCallback(
+ nullptr, nullptr, framehd, result, &provider->source, nullptr);
+ EXPECT_EQ(0, send_result);
+ // Data accepted by the visitor includes a frame header and the entire
+ // payload.
+ EXPECT_EQ(visitor.data().size(), kFrameHeaderSize + kPayload.size());
+}
+
+// Verifies that a nghttp2_data_provider derived from a DataFrameSource works
+// correctly with nghttp2-style callbacks when the source is blocked.
+TEST(DataProviderTest, ReadFromBlockedSource) {
+ DataSavingVisitor visitor;
+ // Source has no payload, but also no fin, so it's blocked.
+ TestDataFrameSource source(visitor, false);
+ auto provider = MakeDataProvider(&source);
+ uint32_t data_flags = 0;
+ const int32_t kStreamId = 1;
+ const size_t kReadLength = 10;
+ ssize_t result =
+ provider->read_callback(nullptr, kStreamId, nullptr, kReadLength,
+ &data_flags, &provider->source, nullptr);
+ // Read operation is deferred, since the source is blocked.
+ EXPECT_EQ(NGHTTP2_ERR_DEFERRED, result);
+}
+
+// Verifies that a nghttp2_data_provider derived from a DataFrameSource works
+// correctly with nghttp2-style callbacks when the source provides only fin and
+// no data.
+TEST(DataProviderTest, ReadFromZeroLengthSource) {
+ DataSavingVisitor visitor;
+ // Empty payload and fin=true indicates the source is done.
+ TestDataFrameSource source(visitor, true);
+ source.EndData();
+ auto provider = MakeDataProvider(&source);
+ uint32_t data_flags = 0;
+ const int32_t kStreamId = 1;
+ const size_t kReadLength = 10;
+ ssize_t result =
+ provider->read_callback(nullptr, kStreamId, nullptr, kReadLength,
+ &data_flags, &provider->source, nullptr);
+ ASSERT_EQ(0, result);
+ EXPECT_EQ(NGHTTP2_DATA_FLAG_NO_COPY | NGHTTP2_DATA_FLAG_EOF, data_flags);
+
+ const uint8_t framehd[kFrameHeaderSize] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ int send_result = callbacks::DataFrameSourceSendCallback(
+ nullptr, nullptr, framehd, result, &provider->source, nullptr);
+ EXPECT_EQ(0, send_result);
+ // Data accepted by the visitor includes a frame header with fin and zero
+ // bytes of payload.
+ EXPECT_EQ(visitor.data().size(), kFrameHeaderSize);
+}
+
+} // namespace test
+} // namespace adapter
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.cc
index d434b06e0f5..9868958bc67 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.cc
@@ -4,35 +4,21 @@
namespace http2 {
namespace adapter {
-namespace {
-
-void DeleteOptions(nghttp2_option* options) {
- if (options) {
- nghttp2_option_del(options);
- }
-}
-
-} // namespace
NgHttp2Session::NgHttp2Session(Perspective perspective,
nghttp2_session_callbacks_unique_ptr callbacks,
- nghttp2_option* options,
- void* userdata)
- : session_(MakeSessionPtr(nullptr)),
- options_(options, DeleteOptions),
- perspective_(perspective) {
+ const nghttp2_option* options, void* userdata)
+ : session_(MakeSessionPtr(nullptr)), perspective_(perspective) {
nghttp2_session* session;
switch (perspective) {
case Perspective::kClient:
- nghttp2_session_client_new2(&session, callbacks.get(), userdata,
- options_.get());
+ nghttp2_session_client_new2(&session, callbacks.get(), userdata, options);
break;
case Perspective::kServer:
- nghttp2_session_server_new2(&session, callbacks.get(), userdata,
- options_.get());
+ nghttp2_session_server_new2(&session, callbacks.get(), userdata, options);
break;
}
- session_.reset(session);
+ session_ = MakeSessionPtr(session);
}
NgHttp2Session::~NgHttp2Session() {
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.h
index d446a07c75b..4339875588c 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.h
@@ -4,18 +4,18 @@
#include "http2/adapter/http2_session.h"
#include "http2/adapter/nghttp2_util.h"
#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+#include "common/platform/api/quiche_export.h"
namespace http2 {
namespace adapter {
// A C++ wrapper around common nghttp2_session operations.
-class NgHttp2Session : public Http2Session {
+class QUICHE_EXPORT_PRIVATE NgHttp2Session : public Http2Session {
public:
- // Takes ownership of |options|.
+ // Does not take ownership of |options|.
NgHttp2Session(Perspective perspective,
nghttp2_session_callbacks_unique_ptr callbacks,
- nghttp2_option* options,
- void* userdata);
+ const nghttp2_option* options, void* userdata);
~NgHttp2Session() override;
ssize_t ProcessBytes(absl::string_view bytes) override;
@@ -29,10 +29,7 @@ class NgHttp2Session : public Http2Session {
nghttp2_session* raw_ptr() const { return session_.get(); }
private:
- using OptionsDeleter = void (&)(nghttp2_option*);
-
nghttp2_session_unique_ptr session_;
- std::unique_ptr<nghttp2_option, OptionsDeleter> options_;
Perspective perspective_;
};
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session_test.cc
index 169d2f20dd0..487843bcc52 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session_test.cc
@@ -26,47 +26,36 @@ enum FrameType {
WINDOW_UPDATE,
};
-ssize_t SaveSessionOutput(nghttp2_session* /* session*/,
- const uint8_t* data,
- size_t length,
- int /* flags */,
- void* user_data) {
- auto visitor = static_cast<DataSavingVisitor*>(user_data);
- visitor->Save(ToStringView(data, length));
- return length;
-}
-
class NgHttp2SessionTest : public testing::Test {
public:
- nghttp2_option* CreateOptions() {
- nghttp2_option* options;
- nghttp2_option_new(&options);
- nghttp2_option_set_no_auto_window_update(options, 1);
- return options;
+ void SetUp() override {
+ nghttp2_option_new(&options_);
+ nghttp2_option_set_no_auto_window_update(options_, 1);
}
+ void TearDown() override { nghttp2_option_del(options_); }
+
nghttp2_session_callbacks_unique_ptr CreateCallbacks() {
nghttp2_session_callbacks_unique_ptr callbacks = callbacks::Create();
- nghttp2_session_callbacks_set_send_callback(callbacks.get(),
- &SaveSessionOutput);
return callbacks;
}
DataSavingVisitor visitor_;
+ nghttp2_option* options_ = nullptr;
};
TEST_F(NgHttp2SessionTest, ClientConstruction) {
- NgHttp2Session session(Perspective::kClient, CreateCallbacks(),
- CreateOptions(), &visitor_);
+ NgHttp2Session session(Perspective::kClient, CreateCallbacks(), options_,
+ &visitor_);
EXPECT_TRUE(session.want_read());
EXPECT_FALSE(session.want_write());
- EXPECT_EQ(session.GetRemoteWindowSize(), kDefaultInitialStreamWindowSize);
+ EXPECT_EQ(session.GetRemoteWindowSize(), kInitialFlowControlWindowSize);
EXPECT_NE(session.raw_ptr(), nullptr);
}
TEST_F(NgHttp2SessionTest, ClientHandlesFrames) {
- NgHttp2Session session(Perspective::kClient, CreateCallbacks(),
- CreateOptions(), &visitor_);
+ NgHttp2Session session(Perspective::kClient, CreateCallbacks(), options_,
+ &visitor_);
ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
ASSERT_GT(visitor_.data().size(), 0);
@@ -92,7 +81,13 @@ TEST_F(NgHttp2SessionTest, ClientHandlesFrames) {
EXPECT_EQ(initial_frames.size(), initial_result);
EXPECT_EQ(session.GetRemoteWindowSize(),
- kDefaultInitialStreamWindowSize + 1000);
+ kInitialFlowControlWindowSize + 1000);
+
+ EXPECT_CALL(visitor_, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor_, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor_, OnBeforeFrameSent(PING, 0, 8, 0x1));
+ EXPECT_CALL(visitor_, OnFrameSent(PING, 0, 8, 0x1, 0));
+
ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
// Some bytes should have been serialized.
absl::string_view serialized = visitor_.data();
@@ -139,6 +134,13 @@ TEST_F(NgHttp2SessionTest, ClientHandlesFrames) {
ASSERT_GT(stream_id3, 0);
QUICHE_LOG(INFO) << "Created stream: " << stream_id3;
+ EXPECT_CALL(visitor_, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
+ EXPECT_CALL(visitor_, OnFrameSent(HEADERS, 1, _, 0x5, 0));
+ EXPECT_CALL(visitor_, OnBeforeFrameSent(HEADERS, 3, _, 0x5));
+ EXPECT_CALL(visitor_, OnFrameSent(HEADERS, 3, _, 0x5, 0));
+ EXPECT_CALL(visitor_, OnBeforeFrameSent(HEADERS, 5, _, 0x5));
+ EXPECT_CALL(visitor_, OnFrameSent(HEADERS, 5, _, 0x5, 0));
+
ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
serialized = visitor_.data();
EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS,
@@ -203,17 +205,17 @@ TEST_F(NgHttp2SessionTest, ClientHandlesFrames) {
}
TEST_F(NgHttp2SessionTest, ServerConstruction) {
- NgHttp2Session session(Perspective::kServer, CreateCallbacks(),
- CreateOptions(), &visitor_);
+ NgHttp2Session session(Perspective::kServer, CreateCallbacks(), options_,
+ &visitor_);
EXPECT_TRUE(session.want_read());
EXPECT_FALSE(session.want_write());
- EXPECT_EQ(session.GetRemoteWindowSize(), kDefaultInitialStreamWindowSize);
+ EXPECT_EQ(session.GetRemoteWindowSize(), kInitialFlowControlWindowSize);
EXPECT_NE(session.raw_ptr(), nullptr);
}
TEST_F(NgHttp2SessionTest, ServerHandlesFrames) {
- NgHttp2Session session(Perspective::kServer, CreateCallbacks(),
- CreateOptions(), &visitor_);
+ NgHttp2Session session(Perspective::kServer, CreateCallbacks(), options_,
+ &visitor_);
const std::string frames = TestFrameSequence()
.ClientPreface()
@@ -277,7 +279,14 @@ TEST_F(NgHttp2SessionTest, ServerHandlesFrames) {
EXPECT_EQ(frames.size(), result);
EXPECT_EQ(session.GetRemoteWindowSize(),
- kDefaultInitialStreamWindowSize + 1000);
+ kInitialFlowControlWindowSize + 1000);
+
+ EXPECT_CALL(visitor_, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor_, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor_, OnBeforeFrameSent(PING, 0, 8, 0x1));
+ EXPECT_CALL(visitor_, OnFrameSent(PING, 0, 8, 0x1, 0));
+ EXPECT_CALL(visitor_, OnBeforeFrameSent(PING, 0, 8, 0x1));
+ EXPECT_CALL(visitor_, OnFrameSent(PING, 0, 8, 0x1, 0));
EXPECT_TRUE(session.want_write());
ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test.cc
new file mode 100644
index 00000000000..0b977dc1815
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test.cc
@@ -0,0 +1,205 @@
+#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+
+#include "absl/strings/str_cat.h"
+#include "http2/adapter/mock_nghttp2_callbacks.h"
+#include "http2/adapter/nghttp2_test_utils.h"
+#include "http2/adapter/nghttp2_util.h"
+#include "http2/adapter/test_frame_sequence.h"
+#include "http2/adapter/test_utils.h"
+#include "common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace adapter {
+namespace test {
+namespace {
+
+using testing::_;
+
+enum FrameType {
+ DATA,
+ HEADERS,
+ PRIORITY,
+ RST_STREAM,
+ SETTINGS,
+ PUSH_PROMISE,
+ PING,
+ GOAWAY,
+ WINDOW_UPDATE,
+};
+
+nghttp2_option* GetOptions() {
+ nghttp2_option* options;
+ nghttp2_option_new(&options);
+ // Set some common options for compatibility.
+ nghttp2_option_set_no_closed_streams(options, 1);
+ nghttp2_option_set_no_auto_window_update(options, 1);
+ nghttp2_option_set_max_send_header_block_length(options, 0x2000000);
+ nghttp2_option_set_max_outbound_ack(options, 10000);
+ return options;
+}
+
+class Nghttp2Test : public testing::Test {
+ public:
+ Nghttp2Test() : session_(MakeSessionPtr(nullptr)) {}
+
+ void SetUp() override { InitializeSession(); }
+
+ virtual Perspective GetPerspective() = 0;
+
+ void InitializeSession() {
+ auto nghttp2_callbacks = MockNghttp2Callbacks::GetCallbacks();
+ nghttp2_option* options = GetOptions();
+ nghttp2_session* ptr;
+ if (GetPerspective() == Perspective::kClient) {
+ nghttp2_session_client_new2(&ptr, nghttp2_callbacks.get(),
+ &mock_callbacks_, options);
+ } else {
+ nghttp2_session_server_new2(&ptr, nghttp2_callbacks.get(),
+ &mock_callbacks_, options);
+ }
+ nghttp2_option_del(options);
+
+ // Sets up the Send() callback to append to |serialized_|.
+ EXPECT_CALL(mock_callbacks_, Send(_, _, _))
+ .WillRepeatedly(
+ [this](const uint8_t* data, size_t length, int /*flags*/) {
+ absl::StrAppend(&serialized_, ToStringView(data, length));
+ return length;
+ });
+ // Sets up the SendData() callback to fetch and append data from a
+ // TestDataSource.
+ EXPECT_CALL(mock_callbacks_, SendData(_, _, _, _))
+ .WillRepeatedly([this](nghttp2_frame* /*frame*/, const uint8_t* framehd,
+ size_t length, nghttp2_data_source* source) {
+ QUICHE_LOG(INFO) << "Appending frame header and " << length
+ << " bytes of data";
+ auto* s = static_cast<TestDataSource*>(source->ptr);
+ absl::StrAppend(&serialized_, ToStringView(framehd, 9),
+ s->ReadNext(length));
+ return 0;
+ });
+ session_ = MakeSessionPtr(ptr);
+ }
+
+ testing::StrictMock<MockNghttp2Callbacks> mock_callbacks_;
+ nghttp2_session_unique_ptr session_;
+ std::string serialized_;
+};
+
+class Nghttp2ClientTest : public Nghttp2Test {
+ public:
+ Perspective GetPerspective() override { return Perspective::kClient; }
+};
+
+// Verifies nghttp2 behavior when acting as a client.
+TEST_F(Nghttp2ClientTest, ClientReceivesUnexpectedHeaders) {
+ const std::string initial_frames = TestFrameSequence()
+ .ServerPreface()
+ .Ping(42)
+ .WindowUpdate(0, 1000)
+ .Serialize();
+
+ testing::InSequence seq;
+ EXPECT_CALL(mock_callbacks_, OnBeginFrame(HasFrameHeader(0, SETTINGS, 0)));
+ EXPECT_CALL(mock_callbacks_, OnFrameRecv(IsSettings(testing::IsEmpty())));
+ EXPECT_CALL(mock_callbacks_, OnBeginFrame(HasFrameHeader(0, PING, 0)));
+ EXPECT_CALL(mock_callbacks_, OnFrameRecv(IsPing(42)));
+ EXPECT_CALL(mock_callbacks_,
+ OnBeginFrame(HasFrameHeader(0, WINDOW_UPDATE, 0)));
+ EXPECT_CALL(mock_callbacks_, OnFrameRecv(IsWindowUpdate(1000)));
+
+ ssize_t result = nghttp2_session_mem_recv(
+ session_.get(), ToUint8Ptr(initial_frames.data()), initial_frames.size());
+ ASSERT_EQ(result, initial_frames.size());
+
+ const std::string unexpected_stream_frames =
+ TestFrameSequence()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .RstStream(3, Http2ErrorCode::INTERNAL_ERROR)
+ .GoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "calm down!!")
+ .Serialize();
+
+ EXPECT_CALL(mock_callbacks_, OnBeginFrame(HasFrameHeader(1, HEADERS, _)));
+ EXPECT_CALL(mock_callbacks_, OnInvalidFrameRecv(IsHeaders(1, _, _), _));
+ // No events from the DATA, RST_STREAM or GOAWAY.
+
+ nghttp2_session_mem_recv(session_.get(),
+ ToUint8Ptr(unexpected_stream_frames.data()),
+ unexpected_stream_frames.size());
+}
+
+// Tests the request-sending behavior of nghttp2 when acting as a client.
+TEST_F(Nghttp2ClientTest, ClientSendsRequest) {
+ int result = nghttp2_session_send(session_.get());
+ ASSERT_EQ(result, 0);
+
+ EXPECT_THAT(serialized_, testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized_.clear();
+
+ const std::string initial_frames =
+ TestFrameSequence().ServerPreface().Serialize();
+ testing::InSequence s;
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(mock_callbacks_, OnBeginFrame(HasFrameHeader(0, SETTINGS, 0)));
+ EXPECT_CALL(mock_callbacks_, OnFrameRecv(IsSettings(testing::IsEmpty())));
+
+ ssize_t recv_result = nghttp2_session_mem_recv(
+ session_.get(), ToUint8Ptr(initial_frames.data()), initial_frames.size());
+ EXPECT_EQ(initial_frames.size(), recv_result);
+
+ // Client wants to send a SETTINGS ack.
+ EXPECT_CALL(mock_callbacks_, BeforeFrameSend(IsSettings(testing::IsEmpty())));
+ EXPECT_CALL(mock_callbacks_, OnFrameSend(IsSettings(testing::IsEmpty())));
+ EXPECT_TRUE(nghttp2_session_want_write(session_.get()));
+ result = nghttp2_session_send(session_.get());
+ EXPECT_THAT(serialized_, EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+ serialized_.clear();
+
+ EXPECT_FALSE(nghttp2_session_want_write(session_.get()));
+
+ // The following sets up the client request.
+ std::vector<std::pair<absl::string_view, absl::string_view>> headers = {
+ {":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}};
+ std::vector<nghttp2_nv> nvs;
+ for (const auto& h : headers) {
+ nvs.push_back({.name = ToUint8Ptr(h.first.data()),
+ .value = ToUint8Ptr(h.second.data()),
+ .namelen = h.first.size(),
+ .valuelen = h.second.size()});
+ }
+ const absl::string_view kBody = "This is an example request body.";
+ TestDataSource source{kBody};
+ nghttp2_data_provider provider = source.MakeDataProvider();
+ // After submitting the request, the client will want to write.
+ int stream_id =
+ nghttp2_submit_request(session_.get(), nullptr /* pri_spec */, nvs.data(),
+ nvs.size(), &provider, nullptr /* stream_data */);
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(nghttp2_session_want_write(session_.get()));
+
+ // We expect that the client will want to write HEADERS, then DATA.
+ EXPECT_CALL(mock_callbacks_, BeforeFrameSend(IsHeaders(stream_id, _, _)));
+ EXPECT_CALL(mock_callbacks_, OnFrameSend(IsHeaders(stream_id, _, _)));
+ EXPECT_CALL(mock_callbacks_, OnFrameSend(IsData(stream_id, kBody.size(), _)));
+ nghttp2_session_send(session_.get());
+ EXPECT_THAT(serialized_, EqualsFrames({spdy::SpdyFrameType::HEADERS,
+ spdy::SpdyFrameType::DATA}));
+ EXPECT_THAT(serialized_, testing::HasSubstr(kBody));
+
+ // Once the request is flushed, the client no longer wants to write.
+ EXPECT_FALSE(nghttp2_session_want_write(session_.get()));
+}
+
+} // namespace
+} // namespace test
+} // namespace adapter
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.cc
new file mode 100644
index 00000000000..d9a04f27772
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.cc
@@ -0,0 +1,454 @@
+#include "http2/adapter/nghttp2_test_utils.h"
+
+#include "http2/adapter/nghttp2_util.h"
+#include "common/quiche_endian.h"
+
+namespace http2 {
+namespace adapter {
+namespace test {
+
+namespace {
+
+// Custom gMock matcher, used to implement HasFrameHeader().
+class FrameHeaderMatcher {
+ public:
+ FrameHeaderMatcher(int32_t streamid, uint8_t type,
+ const testing::Matcher<int> flags)
+ : stream_id_(streamid), type_(type), flags_(flags) {}
+
+ bool Match(const nghttp2_frame_hd& frame,
+ testing::MatchResultListener* listener) const {
+ bool matched = true;
+ if (stream_id_ != frame.stream_id) {
+ *listener << "; expected stream " << stream_id_ << ", saw "
+ << frame.stream_id;
+ matched = false;
+ }
+ if (type_ != frame.type) {
+ *listener << "; expected frame type " << type_ << ", saw "
+ << static_cast<int>(frame.type);
+ matched = false;
+ }
+ if (!flags_.MatchAndExplain(frame.flags, listener)) {
+ matched = false;
+ }
+ return matched;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "contains a frame header with stream " << stream_id_ << ", type "
+ << type_ << ", ";
+ flags_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "does not contain a frame header with stream " << stream_id_
+ << ", type " << type_ << ", ";
+ flags_.DescribeNegationTo(os);
+ }
+
+ private:
+ const int32_t stream_id_;
+ const int type_;
+ const testing::Matcher<int> flags_;
+};
+
+class PointerToFrameHeaderMatcher
+ : public FrameHeaderMatcher,
+ public testing::MatcherInterface<const nghttp2_frame_hd*> {
+ public:
+ PointerToFrameHeaderMatcher(int32_t streamid, uint8_t type,
+ const testing::Matcher<int> flags)
+ : FrameHeaderMatcher(streamid, type, flags) {}
+
+ bool MatchAndExplain(const nghttp2_frame_hd* frame,
+ testing::MatchResultListener* listener) const override {
+ return FrameHeaderMatcher::Match(*frame, listener);
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ FrameHeaderMatcher::DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ FrameHeaderMatcher::DescribeNegationTo(os);
+ }
+};
+
+class ReferenceToFrameHeaderMatcher
+ : public FrameHeaderMatcher,
+ public testing::MatcherInterface<const nghttp2_frame_hd&> {
+ public:
+ ReferenceToFrameHeaderMatcher(int32_t streamid, uint8_t type,
+ const testing::Matcher<int> flags)
+ : FrameHeaderMatcher(streamid, type, flags) {}
+
+ bool MatchAndExplain(const nghttp2_frame_hd& frame,
+ testing::MatchResultListener* listener) const override {
+ return FrameHeaderMatcher::Match(frame, listener);
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ FrameHeaderMatcher::DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ FrameHeaderMatcher::DescribeNegationTo(os);
+ }
+};
+
+class DataMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
+ public:
+ DataMatcher(const testing::Matcher<uint32_t> stream_id,
+ const testing::Matcher<size_t> length,
+ const testing::Matcher<int> flags)
+ : stream_id_(stream_id), length_(length), flags_(flags) {}
+
+ bool MatchAndExplain(const nghttp2_frame* frame,
+ testing::MatchResultListener* listener) const override {
+ if (frame->hd.type != NGHTTP2_DATA) {
+ *listener << "; expected DATA frame, saw frame of type "
+ << static_cast<int>(frame->hd.type);
+ return false;
+ }
+ bool matched = true;
+ if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) {
+ matched = false;
+ }
+ if (!length_.MatchAndExplain(frame->hd.length, listener)) {
+ matched = false;
+ }
+ if (!flags_.MatchAndExplain(frame->hd.flags, listener)) {
+ matched = false;
+ }
+ return matched;
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "contains a DATA frame, ";
+ stream_id_.DescribeTo(os);
+ length_.DescribeTo(os);
+ flags_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "does not contain a DATA frame, ";
+ stream_id_.DescribeNegationTo(os);
+ length_.DescribeNegationTo(os);
+ flags_.DescribeNegationTo(os);
+ }
+
+ private:
+ const testing::Matcher<uint32_t> stream_id_;
+ const testing::Matcher<size_t> length_;
+ const testing::Matcher<int> flags_;
+};
+
+class HeadersMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
+ public:
+ HeadersMatcher(const testing::Matcher<uint32_t> stream_id,
+ const testing::Matcher<int> flags,
+ const testing::Matcher<int> category)
+ : stream_id_(stream_id), flags_(flags), category_(category) {}
+
+ bool MatchAndExplain(const nghttp2_frame* frame,
+ testing::MatchResultListener* listener) const override {
+ if (frame->hd.type != NGHTTP2_HEADERS) {
+ *listener << "; expected HEADERS frame, saw frame of type "
+ << static_cast<int>(frame->hd.type);
+ return false;
+ }
+ bool matched = true;
+ if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) {
+ matched = false;
+ }
+ if (!flags_.MatchAndExplain(frame->hd.flags, listener)) {
+ matched = false;
+ }
+ if (!category_.MatchAndExplain(frame->headers.cat, listener)) {
+ matched = false;
+ }
+ return matched;
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "contains a HEADERS frame, ";
+ stream_id_.DescribeTo(os);
+ flags_.DescribeTo(os);
+ category_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "does not contain a HEADERS frame, ";
+ stream_id_.DescribeNegationTo(os);
+ flags_.DescribeNegationTo(os);
+ category_.DescribeNegationTo(os);
+ }
+
+ private:
+ const testing::Matcher<uint32_t> stream_id_;
+ const testing::Matcher<int> flags_;
+ const testing::Matcher<int> category_;
+};
+
+class RstStreamMatcher
+ : public testing::MatcherInterface<const nghttp2_frame*> {
+ public:
+ RstStreamMatcher(const testing::Matcher<uint32_t> stream_id,
+ const testing::Matcher<uint32_t> error_code)
+ : stream_id_(stream_id), error_code_(error_code) {}
+
+ bool MatchAndExplain(const nghttp2_frame* frame,
+ testing::MatchResultListener* listener) const override {
+ if (frame->hd.type != NGHTTP2_RST_STREAM) {
+ *listener << "; expected RST_STREAM frame, saw frame of type "
+ << static_cast<int>(frame->hd.type);
+ return false;
+ }
+ bool matched = true;
+ if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) {
+ matched = false;
+ }
+ if (!error_code_.MatchAndExplain(frame->rst_stream.error_code, listener)) {
+ matched = false;
+ }
+ return matched;
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "contains a RST_STREAM frame, ";
+ stream_id_.DescribeTo(os);
+ error_code_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "does not contain a RST_STREAM frame, ";
+ stream_id_.DescribeNegationTo(os);
+ error_code_.DescribeNegationTo(os);
+ }
+
+ private:
+ const testing::Matcher<uint32_t> stream_id_;
+ const testing::Matcher<uint32_t> error_code_;
+};
+
+class SettingsMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
+ public:
+ SettingsMatcher(const testing::Matcher<std::vector<Http2Setting>> values)
+ : values_(values) {}
+
+ bool MatchAndExplain(const nghttp2_frame* frame,
+ testing::MatchResultListener* listener) const override {
+ if (frame->hd.type != NGHTTP2_SETTINGS) {
+ *listener << "; expected SETTINGS frame, saw frame of type "
+ << static_cast<int>(frame->hd.type);
+ return false;
+ }
+ std::vector<Http2Setting> settings;
+ settings.reserve(frame->settings.niv);
+ for (size_t i = 0; i < frame->settings.niv; ++i) {
+ const auto& p = frame->settings.iv[i];
+ settings.push_back({static_cast<uint16_t>(p.settings_id), p.value});
+ }
+ return values_.MatchAndExplain(settings, listener);
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "contains a SETTINGS frame, ";
+ values_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "does not contain a SETTINGS frame, ";
+ values_.DescribeNegationTo(os);
+ }
+
+ private:
+ const testing::Matcher<std::vector<Http2Setting>> values_;
+};
+
+class PingMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
+ public:
+ PingMatcher(const testing::Matcher<uint64_t> id, bool is_ack)
+ : id_(id), is_ack_(is_ack) {}
+
+ bool MatchAndExplain(const nghttp2_frame* frame,
+ testing::MatchResultListener* listener) const override {
+ if (frame->hd.type != NGHTTP2_PING) {
+ *listener << "; expected PING frame, saw frame of type "
+ << static_cast<int>(frame->hd.type);
+ return false;
+ }
+ bool matched = true;
+ bool frame_ack = frame->hd.flags & NGHTTP2_FLAG_ACK;
+ if (is_ack_ != frame_ack) {
+ *listener << "; expected is_ack=" << is_ack_ << ", saw " << frame_ack;
+ matched = false;
+ }
+ uint64_t data;
+ std::memcpy(&data, frame->ping.opaque_data, sizeof(data));
+ data = quiche::QuicheEndian::HostToNet64(data);
+ if (!id_.MatchAndExplain(data, listener)) {
+ matched = false;
+ }
+ return matched;
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "contains a PING frame, ";
+ id_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "does not contain a PING frame, ";
+ id_.DescribeNegationTo(os);
+ }
+
+ private:
+ const testing::Matcher<uint64_t> id_;
+ const bool is_ack_;
+};
+
+class GoAwayMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
+ public:
+ GoAwayMatcher(const testing::Matcher<uint32_t> last_stream_id,
+ const testing::Matcher<uint32_t> error_code,
+ const testing::Matcher<absl::string_view> opaque_data)
+ : last_stream_id_(last_stream_id),
+ error_code_(error_code),
+ opaque_data_(opaque_data) {}
+
+ bool MatchAndExplain(const nghttp2_frame* frame,
+ testing::MatchResultListener* listener) const override {
+ if (frame->hd.type != NGHTTP2_GOAWAY) {
+ *listener << "; expected GOAWAY frame, saw frame of type "
+ << static_cast<int>(frame->hd.type);
+ return false;
+ }
+ bool matched = true;
+ if (!last_stream_id_.MatchAndExplain(frame->goaway.last_stream_id,
+ listener)) {
+ matched = false;
+ }
+ if (!error_code_.MatchAndExplain(frame->goaway.error_code, listener)) {
+ matched = false;
+ }
+ auto opaque_data =
+ ToStringView(frame->goaway.opaque_data, frame->goaway.opaque_data_len);
+ if (!opaque_data_.MatchAndExplain(opaque_data, listener)) {
+ matched = false;
+ }
+ return matched;
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "contains a GOAWAY frame, ";
+ last_stream_id_.DescribeTo(os);
+ error_code_.DescribeTo(os);
+ opaque_data_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "does not contain a GOAWAY frame, ";
+ last_stream_id_.DescribeNegationTo(os);
+ error_code_.DescribeNegationTo(os);
+ opaque_data_.DescribeNegationTo(os);
+ }
+
+ private:
+ const testing::Matcher<uint32_t> last_stream_id_;
+ const testing::Matcher<uint32_t> error_code_;
+ const testing::Matcher<absl::string_view> opaque_data_;
+};
+
+class WindowUpdateMatcher
+ : public testing::MatcherInterface<const nghttp2_frame*> {
+ public:
+ WindowUpdateMatcher(const testing::Matcher<uint32_t> delta) : delta_(delta) {}
+
+ bool MatchAndExplain(const nghttp2_frame* frame,
+ testing::MatchResultListener* listener) const override {
+ if (frame->hd.type != NGHTTP2_WINDOW_UPDATE) {
+ *listener << "; expected WINDOW_UPDATE frame, saw frame of type "
+ << static_cast<int>(frame->hd.type);
+ return false;
+ }
+ return delta_.MatchAndExplain(frame->window_update.window_size_increment,
+ listener);
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "contains a WINDOW_UPDATE frame, ";
+ delta_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "does not contain a WINDOW_UPDATE frame, ";
+ delta_.DescribeNegationTo(os);
+ }
+
+ private:
+ const testing::Matcher<uint32_t> delta_;
+};
+
+} // namespace
+
+testing::Matcher<const nghttp2_frame_hd*> HasFrameHeader(
+ uint32_t streamid, uint8_t type, const testing::Matcher<int> flags) {
+ return MakeMatcher(new PointerToFrameHeaderMatcher(streamid, type, flags));
+}
+
+testing::Matcher<const nghttp2_frame_hd&> HasFrameHeaderRef(
+ uint32_t streamid, uint8_t type, const testing::Matcher<int> flags) {
+ return MakeMatcher(new ReferenceToFrameHeaderMatcher(streamid, type, flags));
+}
+
+testing::Matcher<const nghttp2_frame*> IsData(
+ const testing::Matcher<uint32_t> stream_id,
+ const testing::Matcher<size_t> length, const testing::Matcher<int> flags) {
+ return MakeMatcher(new DataMatcher(stream_id, length, flags));
+}
+
+testing::Matcher<const nghttp2_frame*> IsHeaders(
+ const testing::Matcher<uint32_t> stream_id,
+ const testing::Matcher<int> flags, const testing::Matcher<int> category) {
+ return MakeMatcher(new HeadersMatcher(stream_id, flags, category));
+}
+
+testing::Matcher<const nghttp2_frame*> IsRstStream(
+ const testing::Matcher<uint32_t> stream_id,
+ const testing::Matcher<uint32_t> error_code) {
+ return MakeMatcher(new RstStreamMatcher(stream_id, error_code));
+}
+
+testing::Matcher<const nghttp2_frame*> IsSettings(
+ const testing::Matcher<std::vector<Http2Setting>> values) {
+ return MakeMatcher(new SettingsMatcher(values));
+}
+
+testing::Matcher<const nghttp2_frame*> IsPing(
+ const testing::Matcher<uint64_t> id) {
+ return MakeMatcher(new PingMatcher(id, false));
+}
+
+testing::Matcher<const nghttp2_frame*> IsPingAck(
+ const testing::Matcher<uint64_t> id) {
+ return MakeMatcher(new PingMatcher(id, true));
+}
+
+testing::Matcher<const nghttp2_frame*> IsGoAway(
+ const testing::Matcher<uint32_t> last_stream_id,
+ const testing::Matcher<uint32_t> error_code,
+ const testing::Matcher<absl::string_view> opaque_data) {
+ return MakeMatcher(
+ new GoAwayMatcher(last_stream_id, error_code, opaque_data));
+}
+
+testing::Matcher<const nghttp2_frame*> IsWindowUpdate(
+ const testing::Matcher<uint32_t> delta) {
+ return MakeMatcher(new WindowUpdateMatcher(delta));
+}
+
+} // namespace test
+} // namespace adapter
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.h
new file mode 100644
index 00000000000..9b772ffda63
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.h
@@ -0,0 +1,99 @@
+#ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_TEST_UTILS_H_
+#define QUICHE_HTTP2_ADAPTER_NGHTTP2_TEST_UTILS_H_
+
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "http2/adapter/http2_protocol.h"
+#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+#include "common/platform/api/quiche_export.h"
+#include "common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace adapter {
+namespace test {
+
+// A simple class that can easily be adapted to act as a nghttp2_data_source.
+class QUICHE_NO_EXPORT TestDataSource {
+ public:
+ explicit TestDataSource(absl::string_view data) : data_(std::string(data)) {}
+
+ absl::string_view ReadNext(size_t size) {
+ const size_t to_send = std::min(size, remaining_.size());
+ auto ret = remaining_.substr(0, to_send);
+ remaining_.remove_prefix(to_send);
+ return ret;
+ }
+
+ size_t SelectPayloadLength(size_t max_length) {
+ return std::min(max_length, remaining_.size());
+ }
+
+ nghttp2_data_provider MakeDataProvider() {
+ return nghttp2_data_provider{
+ .source = {.ptr = this},
+ .read_callback = [](nghttp2_session*, int32_t, uint8_t*, size_t length,
+ uint32_t* data_flags, nghttp2_data_source* source,
+ void*) -> ssize_t {
+ *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
+ auto* s = static_cast<TestDataSource*>(source->ptr);
+ if (!s->is_data_available()) {
+ return NGHTTP2_ERR_DEFERRED;
+ }
+ const ssize_t ret = s->SelectPayloadLength(length);
+ if (ret < static_cast<ssize_t>(length)) {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ }
+ return ret;
+ }};
+ }
+
+ bool is_data_available() const { return is_data_available_; }
+ void set_is_data_available(bool value) { is_data_available_ = value; }
+
+ private:
+ const std::string data_;
+ absl::string_view remaining_ = data_;
+ bool is_data_available_ = true;
+};
+
+// Matchers for nghttp2 data types.
+testing::Matcher<const nghttp2_frame_hd*> HasFrameHeader(
+ uint32_t streamid, uint8_t type, const testing::Matcher<int> flags);
+testing::Matcher<const nghttp2_frame_hd&> HasFrameHeaderRef(
+ uint32_t streamid, uint8_t type, const testing::Matcher<int> flags);
+
+testing::Matcher<const nghttp2_frame*> IsData(
+ const testing::Matcher<uint32_t> stream_id,
+ const testing::Matcher<size_t> length, const testing::Matcher<int> flags);
+
+testing::Matcher<const nghttp2_frame*> IsHeaders(
+ const testing::Matcher<uint32_t> stream_id,
+ const testing::Matcher<int> flags, const testing::Matcher<int> category);
+
+testing::Matcher<const nghttp2_frame*> IsRstStream(
+ const testing::Matcher<uint32_t> stream_id,
+ const testing::Matcher<uint32_t> error_code);
+
+testing::Matcher<const nghttp2_frame*> IsSettings(
+ const testing::Matcher<std::vector<Http2Setting>> values);
+
+testing::Matcher<const nghttp2_frame*> IsPing(
+ const testing::Matcher<uint64_t> id);
+
+testing::Matcher<const nghttp2_frame*> IsPingAck(
+ const testing::Matcher<uint64_t> id);
+
+testing::Matcher<const nghttp2_frame*> IsGoAway(
+ const testing::Matcher<uint32_t> last_stream_id,
+ const testing::Matcher<uint32_t> error_code,
+ const testing::Matcher<absl::string_view> opaque_data);
+
+testing::Matcher<const nghttp2_frame*> IsWindowUpdate(
+ const testing::Matcher<uint32_t> delta);
+
+} // namespace test
+} // namespace adapter
+} // namespace http2
+
+#endif // QUICHE_HTTP2_ADAPTER_NGHTTP2_TEST_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.cc
index 8d23a55278a..efb811fc35c 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.cc
@@ -2,10 +2,13 @@
#include <cstdint>
+#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
#include "http2/adapter/http2_protocol.h"
#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
#include "common/platform/api/quiche_logging.h"
+#include "common/quiche_endian.h"
namespace http2 {
namespace adapter {
@@ -28,11 +31,11 @@ void DeleteSession(nghttp2_session* session) {
nghttp2_session_callbacks_unique_ptr MakeCallbacksPtr(
nghttp2_session_callbacks* callbacks) {
- return nghttp2_session_callbacks_unique_ptr(callbacks, DeleteCallbacks);
+ return nghttp2_session_callbacks_unique_ptr(callbacks, &DeleteCallbacks);
}
nghttp2_session_unique_ptr MakeSessionPtr(nghttp2_session* session) {
- return nghttp2_session_unique_ptr(session, DeleteSession);
+ return nghttp2_session_unique_ptr(session, &DeleteSession);
}
uint8_t* ToUint8Ptr(char* str) { return reinterpret_cast<uint8_t*>(str); }
@@ -56,7 +59,8 @@ absl::string_view ToStringView(const uint8_t* pointer, size_t length) {
std::vector<nghttp2_nv> GetNghttp2Nvs(absl::Span<const Header> headers) {
const int num_headers = headers.size();
- auto nghttp2_nvs = std::vector<nghttp2_nv>(num_headers);
+ std::vector<nghttp2_nv> nghttp2_nvs;
+ nghttp2_nvs.reserve(num_headers);
for (int i = 0; i < num_headers; ++i) {
nghttp2_nv header;
uint8_t flags = NGHTTP2_NV_FLAG_NONE;
@@ -85,7 +89,8 @@ std::vector<nghttp2_nv> GetResponseNghttp2Nvs(
absl::string_view response_code) {
// Allocate enough for all headers and also the :status pseudoheader.
const int num_headers = headers.size();
- auto nghttp2_nvs = std::vector<nghttp2_nv>(num_headers + 1);
+ std::vector<nghttp2_nv> nghttp2_nvs;
+ nghttp2_nvs.reserve(num_headers + 1);
// Add the :status pseudoheader first.
nghttp2_nv status;
@@ -97,7 +102,7 @@ std::vector<nghttp2_nv> GetResponseNghttp2Nvs(
nghttp2_nvs.push_back(std::move(status));
// Add the remaining headers.
- for (const auto header_pair : headers) {
+ for (const auto& header_pair : headers) {
nghttp2_nv header;
header.name = ToUint8Ptr(header_pair.first.data());
header.namelen = header_pair.first.size();
@@ -117,5 +122,148 @@ Http2ErrorCode ToHttp2ErrorCode(uint32_t wire_error_code) {
return static_cast<Http2ErrorCode>(wire_error_code);
}
+class Nghttp2DataFrameSource : public DataFrameSource {
+ public:
+ Nghttp2DataFrameSource(nghttp2_data_provider provider,
+ nghttp2_send_data_callback send_data,
+ void* user_data)
+ : provider_(std::move(provider)),
+ send_data_(std::move(send_data)),
+ user_data_(user_data) {}
+
+ std::pair<ssize_t, bool> SelectPayloadLength(size_t max_length) override {
+ const int32_t stream_id = 0;
+ uint32_t data_flags = 0;
+ QUICHE_LOG(INFO) << "Invoking read callback";
+ ssize_t result = provider_.read_callback(
+ nullptr /* session */, stream_id, nullptr /* buf */, max_length,
+ &data_flags, &provider_.source, nullptr /* user_data */);
+ if (result == NGHTTP2_ERR_DEFERRED) {
+ return {kBlocked, false};
+ } else if (result < 0) {
+ return {kError, false};
+ } else if ((data_flags & NGHTTP2_DATA_FLAG_NO_COPY) == 0) {
+ QUICHE_LOG(ERROR) << "Source did not use the zero-copy API!";
+ return {kError, false};
+ } else {
+ if (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) {
+ send_fin_ = false;
+ }
+ const bool eof = data_flags & NGHTTP2_DATA_FLAG_EOF;
+ return {result, eof};
+ }
+ }
+
+ bool Send(absl::string_view frame_header, size_t payload_length) override {
+ nghttp2_frame frame;
+ frame.hd.type = 0;
+ frame.hd.length = payload_length;
+ frame.hd.flags = 0;
+ frame.hd.stream_id = 0;
+ frame.data.padlen = 0;
+ const int result = send_data_(
+ nullptr /* session */, &frame, ToUint8Ptr(frame_header.data()),
+ payload_length, &provider_.source, user_data_);
+ QUICHE_LOG_IF(ERROR, result < 0 && result != NGHTTP2_ERR_WOULDBLOCK)
+ << "Unexpected error code from send: " << result;
+ return result == 0;
+ }
+
+ bool send_fin() const override { return send_fin_; }
+
+ private:
+ nghttp2_data_provider provider_;
+ nghttp2_send_data_callback send_data_;
+ void* user_data_;
+ bool send_fin_ = true;
+};
+
+std::unique_ptr<DataFrameSource> MakeZeroCopyDataFrameSource(
+ nghttp2_data_provider provider,
+ void* user_data,
+ nghttp2_send_data_callback send_data) {
+ return absl::make_unique<Nghttp2DataFrameSource>(
+ std::move(provider), std::move(send_data), user_data);
+}
+
+absl::string_view ErrorString(uint32_t error_code) {
+ return Http2ErrorCodeToString(static_cast<Http2ErrorCode>(error_code));
+}
+
+size_t PaddingLength(uint8_t flags, size_t padlen) {
+ return (flags & 0x8 ? 1 : 0) + padlen;
+}
+
+struct NvFormatter {
+ void operator()(std::string* out, const nghttp2_nv& nv) {
+ absl::StrAppend(out, ToStringView(nv.name, nv.namelen), ": ",
+ ToStringView(nv.value, nv.valuelen));
+ }
+};
+
+std::string NvsAsString(nghttp2_nv* nva, size_t nvlen) {
+ return absl::StrJoin(absl::MakeConstSpan(nva, nvlen), ", ", NvFormatter());
+}
+
+#define HTTP2_FRAME_SEND_LOG QUICHE_VLOG(1)
+
+void LogBeforeSend(const nghttp2_frame& frame) {
+ switch (static_cast<FrameType>(frame.hd.type)) {
+ case FrameType::DATA:
+ HTTP2_FRAME_SEND_LOG << "Sending DATA on stream " << frame.hd.stream_id
+ << " with length "
+ << frame.hd.length - PaddingLength(frame.hd.flags,
+ frame.data.padlen)
+ << " and padding "
+ << PaddingLength(frame.hd.flags, frame.data.padlen);
+ break;
+ case FrameType::HEADERS:
+ HTTP2_FRAME_SEND_LOG << "Sending HEADERS on stream " << frame.hd.stream_id
+ << " with headers ["
+ << NvsAsString(frame.headers.nva,
+ frame.headers.nvlen)
+ << "]";
+ break;
+ case FrameType::PRIORITY:
+ HTTP2_FRAME_SEND_LOG << "Sending PRIORITY";
+ break;
+ case FrameType::RST_STREAM:
+ HTTP2_FRAME_SEND_LOG << "Sending RST_STREAM on stream "
+ << frame.hd.stream_id << " with error code "
+ << ErrorString(frame.rst_stream.error_code);
+ break;
+ case FrameType::SETTINGS:
+ HTTP2_FRAME_SEND_LOG << "Sending SETTINGS with " << frame.settings.niv
+ << " entries, is_ack: " << (frame.hd.flags & 0x01);
+ break;
+ case FrameType::PUSH_PROMISE:
+ HTTP2_FRAME_SEND_LOG << "Sending PUSH_PROMISE";
+ break;
+ case FrameType::PING: {
+ Http2PingId ping_id;
+ std::memcpy(&ping_id, frame.ping.opaque_data, sizeof(Http2PingId));
+ HTTP2_FRAME_SEND_LOG << "Sending PING with unique_id "
+ << quiche::QuicheEndian::NetToHost64(ping_id)
+ << ", is_ack: " << (frame.hd.flags & 0x01);
+ break;
+ }
+ case FrameType::GOAWAY:
+ HTTP2_FRAME_SEND_LOG << "Sending GOAWAY with last_stream: "
+ << frame.goaway.last_stream_id << " and error "
+ << ErrorString(frame.goaway.error_code);
+ break;
+ case FrameType::WINDOW_UPDATE:
+ HTTP2_FRAME_SEND_LOG << "Sending WINDOW_UPDATE on stream "
+ << frame.hd.stream_id << " with update delta "
+ << frame.window_update.window_size_increment;
+ break;
+ case FrameType::CONTINUATION:
+ HTTP2_FRAME_SEND_LOG << "Sending CONTINUATION, which is unexpected";
+ break;
+ }
+}
+
+#undef HTTP2_FRAME_SEND_LOG
+
} // namespace adapter
} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.h
index e2c3e7a37c7..78d4702a3c5 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.h
@@ -8,6 +8,7 @@
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
+#include "http2/adapter/data_source.h"
#include "http2/adapter/http2_protocol.h"
#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
#include "spdy/core/spdy_header_block.h"
@@ -20,8 +21,8 @@ inline constexpr int kStreamCallbackFailureStatus =
NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
inline constexpr int kCancelStatus = NGHTTP2_ERR_CANCEL;
-using CallbacksDeleter = void (&)(nghttp2_session_callbacks*);
-using SessionDeleter = void (&)(nghttp2_session*);
+using CallbacksDeleter = void (*)(nghttp2_session_callbacks*);
+using SessionDeleter = void (*)(nghttp2_session*);
using nghttp2_session_callbacks_unique_ptr =
std::unique_ptr<nghttp2_session_callbacks, CallbacksDeleter>;
@@ -55,6 +56,16 @@ std::vector<nghttp2_nv> GetResponseNghttp2Nvs(
// based on the RFC 7540 Section 7 suggestion.
Http2ErrorCode ToHttp2ErrorCode(uint32_t wire_error_code);
+// Transforms a nghttp2_data_provider into a DataFrameSource. Assumes that
+// |provider| uses the zero-copy nghttp2_data_source_read_callback API. Unsafe
+// otherwise.
+std::unique_ptr<DataFrameSource> MakeZeroCopyDataFrameSource(
+ nghttp2_data_provider provider,
+ void* user_data,
+ nghttp2_send_data_callback send_data);
+
+void LogBeforeSend(const nghttp2_frame& frame);
+
} // namespace adapter
} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util_test.cc
new file mode 100644
index 00000000000..1ec00ba32ec
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util_test.cc
@@ -0,0 +1,109 @@
+#include "http2/adapter/nghttp2_util.h"
+
+#include "http2/adapter/nghttp2_test_utils.h"
+#include "http2/adapter/test_utils.h"
+#include "common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace adapter {
+namespace test {
+namespace {
+
+// This send callback assumes |source|'s pointer is a TestDataSource, and
+// |user_data| is a std::string.
+int FakeSendCallback(nghttp2_session*, nghttp2_frame* /*frame*/,
+ const uint8_t* framehd, size_t length,
+ nghttp2_data_source* source, void* user_data) {
+ auto* dest = static_cast<std::string*>(user_data);
+ // Appends the frame header to the string.
+ absl::StrAppend(dest, ToStringView(framehd, 9));
+ auto* test_source = static_cast<TestDataSource*>(source->ptr);
+ absl::string_view payload = test_source->ReadNext(length);
+ // Appends the frame payload to the string.
+ absl::StrAppend(dest, payload);
+ return 0;
+}
+
+TEST(MakeZeroCopyDataFrameSource, EmptyPayload) {
+ std::string result;
+
+ const absl::string_view kEmptyBody = "";
+ TestDataSource body1{kEmptyBody};
+ // The TestDataSource is wrapped in the nghttp2_data_provider data type.
+ nghttp2_data_provider provider = body1.MakeDataProvider();
+
+ // This call transforms it back into a DataFrameSource, which is compatible
+ // with the Http2Adapter API.
+ std::unique_ptr<DataFrameSource> frame_source =
+ MakeZeroCopyDataFrameSource(provider, &result, FakeSendCallback);
+ auto [length, eof] = frame_source->SelectPayloadLength(100);
+ EXPECT_EQ(length, 0);
+ EXPECT_TRUE(eof);
+ frame_source->Send("ninebytes", 0);
+ EXPECT_EQ(result, "ninebytes");
+}
+
+TEST(MakeZeroCopyDataFrameSource, ShortPayload) {
+ std::string result;
+
+ const absl::string_view kShortBody =
+ "<html><head><title>Example Page!</title></head>"
+ "<body><div><span><table><tr><th><blink>Wow!!"
+ "</blink></th></tr></table></span></div></body>"
+ "</html>";
+ TestDataSource body1{kShortBody};
+ // The TestDataSource is wrapped in the nghttp2_data_provider data type.
+ nghttp2_data_provider provider = body1.MakeDataProvider();
+
+ // This call transforms it back into a DataFrameSource, which is compatible
+ // with the Http2Adapter API.
+ std::unique_ptr<DataFrameSource> frame_source =
+ MakeZeroCopyDataFrameSource(provider, &result, FakeSendCallback);
+ auto [length, eof] = frame_source->SelectPayloadLength(200);
+ EXPECT_EQ(length, kShortBody.size());
+ EXPECT_TRUE(eof);
+ frame_source->Send("ninebytes", length);
+ EXPECT_EQ(result, absl::StrCat("ninebytes", kShortBody));
+}
+
+TEST(MakeZeroCopyDataFrameSource, MultiFramePayload) {
+ std::string result;
+
+ const absl::string_view kShortBody =
+ "<html><head><title>Example Page!</title></head>"
+ "<body><div><span><table><tr><th><blink>Wow!!"
+ "</blink></th></tr></table></span></div></body>"
+ "</html>";
+ TestDataSource body1{kShortBody};
+ // The TestDataSource is wrapped in the nghttp2_data_provider data type.
+ nghttp2_data_provider provider = body1.MakeDataProvider();
+
+ // This call transforms it back into a DataFrameSource, which is compatible
+ // with the Http2Adapter API.
+ std::unique_ptr<DataFrameSource> frame_source =
+ MakeZeroCopyDataFrameSource(provider, &result, FakeSendCallback);
+ auto ret = frame_source->SelectPayloadLength(50);
+ EXPECT_EQ(ret.first, 50);
+ EXPECT_FALSE(ret.second);
+ frame_source->Send("ninebyte1", ret.first);
+
+ ret = frame_source->SelectPayloadLength(50);
+ EXPECT_EQ(ret.first, 50);
+ EXPECT_FALSE(ret.second);
+ frame_source->Send("ninebyte2", ret.first);
+
+ ret = frame_source->SelectPayloadLength(50);
+ EXPECT_EQ(ret.first, 44);
+ EXPECT_TRUE(ret.second);
+ frame_source->Send("ninebyte3", ret.first);
+
+ EXPECT_EQ(result,
+ "ninebyte1<html><head><title>Example Page!</title></head><bo"
+ "ninebyte2dy><div><span><table><tr><th><blink>Wow!!</blink><"
+ "ninebyte3/th></tr></table></span></div></body></html>");
+}
+
+} // namespace
+} // namespace test
+} // namespace adapter
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.cc b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.cc
index 9f287facb9f..27f011ec62f 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.cc
@@ -30,6 +30,10 @@ std::unique_ptr<OgHttp2Adapter> OgHttp2Adapter::Create(
OgHttp2Adapter::~OgHttp2Adapter() {}
+bool OgHttp2Adapter::IsServerSession() const {
+ return session_->IsServerSession();
+}
+
ssize_t OgHttp2Adapter::ProcessBytes(absl::string_view bytes) {
return session_->ProcessBytes(bytes);
}
@@ -54,6 +58,10 @@ void OgHttp2Adapter::SubmitPing(Http2PingId ping_id) {
session_->EnqueueFrame(absl::make_unique<SpdyPingIR>(ping_id));
}
+void OgHttp2Adapter::SubmitShutdownNotice() {
+ session_->StartGracefulShutdown();
+}
+
void OgHttp2Adapter::SubmitGoAway(Http2StreamId last_accepted_stream_id,
Http2ErrorCode error_code,
absl::string_view opaque_data) {
@@ -67,18 +75,45 @@ void OgHttp2Adapter::SubmitWindowUpdate(Http2StreamId stream_id,
absl::make_unique<SpdyWindowUpdateIR>(stream_id, window_increment));
}
-void OgHttp2Adapter::SubmitMetadata(Http2StreamId stream_id, bool fin) {
- QUICHE_BUG(oghttp2_submit_metadata) << "Not implemented";
+void OgHttp2Adapter::SubmitMetadata(Http2StreamId stream_id,
+ std::unique_ptr<MetadataSource> source) {
+ session_->SubmitMetadata(stream_id, std::move(source));
}
-std::string OgHttp2Adapter::GetBytesToWrite(absl::optional<size_t> max_bytes) {
- return session_->GetBytesToWrite(max_bytes);
-}
+int OgHttp2Adapter::Send() { return session_->Send(); }
-int OgHttp2Adapter::GetPeerConnectionWindow() const {
+int OgHttp2Adapter::GetSendWindowSize() const {
return session_->GetRemoteWindowSize();
}
+int OgHttp2Adapter::GetStreamSendWindowSize(Http2StreamId stream_id) const {
+ return session_->GetStreamSendWindowSize(stream_id);
+}
+
+int OgHttp2Adapter::GetStreamReceiveWindowLimit(Http2StreamId stream_id) const {
+ return session_->GetStreamReceiveWindowLimit(stream_id);
+}
+
+int OgHttp2Adapter::GetStreamReceiveWindowSize(Http2StreamId stream_id) const {
+ return session_->GetStreamReceiveWindowSize(stream_id);
+}
+
+int OgHttp2Adapter::GetReceiveWindowSize() const {
+ return session_->GetReceiveWindowSize();
+}
+
+int OgHttp2Adapter::GetHpackEncoderDynamicTableSize() const {
+ return session_->GetHpackEncoderDynamicTableSize();
+}
+
+int OgHttp2Adapter::GetHpackDecoderDynamicTableSize() const {
+ return session_->GetHpackDecoderDynamicTableSize();
+}
+
+Http2StreamId OgHttp2Adapter::GetHighestReceivedStreamId() const {
+ return session_->GetHighestReceivedStreamId();
+}
+
void OgHttp2Adapter::MarkDataConsumedForStream(Http2StreamId stream_id,
size_t num_bytes) {
session_->Consume(stream_id, num_bytes);
@@ -90,6 +125,36 @@ void OgHttp2Adapter::SubmitRst(Http2StreamId stream_id,
stream_id, TranslateErrorCode(error_code)));
}
+int32_t OgHttp2Adapter::SubmitRequest(
+ absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source, void* user_data) {
+ return session_->SubmitRequest(headers, std::move(data_source), user_data);
+}
+
+int OgHttp2Adapter::SubmitResponse(
+ Http2StreamId stream_id, absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source) {
+ return session_->SubmitResponse(stream_id, headers, std::move(data_source));
+}
+
+int OgHttp2Adapter::SubmitTrailer(Http2StreamId stream_id,
+ absl::Span<const Header> trailers) {
+ return session_->SubmitTrailer(stream_id, trailers);
+}
+
+void OgHttp2Adapter::SetStreamUserData(Http2StreamId stream_id,
+ void* user_data) {
+ session_->SetStreamUserData(stream_id, user_data);
+}
+
+void* OgHttp2Adapter::GetStreamUserData(Http2StreamId stream_id) {
+ return session_->GetStreamUserData(stream_id);
+}
+
+bool OgHttp2Adapter::ResumeStream(Http2StreamId stream_id) {
+ return session_->ResumeStream(stream_id);
+}
+
const Http2Session& OgHttp2Adapter::session() const {
return *session_;
}
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.h b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.h
index 572ba04e9fc..9c7bad3c575 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.h
@@ -6,11 +6,12 @@
#include "http2/adapter/http2_adapter.h"
#include "http2/adapter/http2_session.h"
#include "http2/adapter/oghttp2_session.h"
+#include "common/platform/api/quiche_export.h"
namespace http2 {
namespace adapter {
-class OgHttp2Adapter : public Http2Adapter {
+class QUICHE_EXPORT_PRIVATE OgHttp2Adapter : public Http2Adapter {
public:
using Options = OgHttp2Session::Options;
static std::unique_ptr<OgHttp2Adapter> Create(Http2VisitorInterface& visitor,
@@ -19,6 +20,7 @@ class OgHttp2Adapter : public Http2Adapter {
~OgHttp2Adapter();
// From Http2Adapter.
+ bool IsServerSession() const override;
ssize_t ProcessBytes(absl::string_view bytes) override;
void SubmitSettings(absl::Span<const Http2Setting> settings) override;
void SubmitPriorityForStream(Http2StreamId stream_id,
@@ -26,17 +28,38 @@ class OgHttp2Adapter : public Http2Adapter {
int weight,
bool exclusive) override;
void SubmitPing(Http2PingId ping_id) override;
+ void SubmitShutdownNotice() override;
void SubmitGoAway(Http2StreamId last_accepted_stream_id,
Http2ErrorCode error_code,
absl::string_view opaque_data) override;
void SubmitWindowUpdate(Http2StreamId stream_id,
int window_increment) override;
- void SubmitMetadata(Http2StreamId stream_id, bool fin) override;
- std::string GetBytesToWrite(absl::optional<size_t> max_bytes) override;
- int GetPeerConnectionWindow() const override;
+ void SubmitMetadata(Http2StreamId stream_id,
+ std::unique_ptr<MetadataSource> source) override;
+ int Send() override;
+ int GetSendWindowSize() const override;
+ int GetStreamSendWindowSize(Http2StreamId stream_id) const override;
+ int GetStreamReceiveWindowLimit(Http2StreamId stream_id) const override;
+ int GetStreamReceiveWindowSize(Http2StreamId stream_id) const override;
+ int GetReceiveWindowSize() const override;
+ int GetHpackEncoderDynamicTableSize() const override;
+ int GetHpackDecoderDynamicTableSize() const override;
+ Http2StreamId GetHighestReceivedStreamId() const override;
void MarkDataConsumedForStream(Http2StreamId stream_id,
size_t num_bytes) override;
void SubmitRst(Http2StreamId stream_id, Http2ErrorCode error_code) override;
+ int32_t SubmitRequest(absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source,
+ void* user_data) override;
+ int SubmitResponse(Http2StreamId stream_id, absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source) override;
+
+ int SubmitTrailer(Http2StreamId stream_id,
+ absl::Span<const Header> trailers) override;
+
+ void SetStreamUserData(Http2StreamId stream_id, void* user_data) override;
+ void* GetStreamUserData(Http2StreamId stream_id) override;
+ bool ResumeStream(Http2StreamId stream_id) override;
const Http2Session& session() const;
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter_test.cc
index 4c849f6fd6d..03b45df6aa9 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter_test.cc
@@ -1,6 +1,8 @@
#include "http2/adapter/oghttp2_adapter.h"
+#include "absl/strings/str_join.h"
#include "http2/adapter/mock_http2_visitor.h"
+#include "http2/adapter/oghttp2_util.h"
#include "http2/adapter/test_frame_sequence.h"
#include "http2/adapter/test_utils.h"
#include "common/platform/api/quiche_test.h"
@@ -11,6 +13,23 @@ namespace adapter {
namespace test {
namespace {
+using testing::_;
+
+enum FrameType {
+ DATA,
+ HEADERS,
+ PRIORITY,
+ RST_STREAM,
+ SETTINGS,
+ PUSH_PROMISE,
+ PING,
+ GOAWAY,
+ WINDOW_UPDATE,
+ CONTINUATION,
+};
+
+using spdy::SpdyFrameType;
+
class OgHttp2AdapterTest : public testing::Test {
protected:
void SetUp() override {
@@ -18,10 +37,14 @@ class OgHttp2AdapterTest : public testing::Test {
adapter_ = OgHttp2Adapter::Create(http2_visitor_, options);
}
- testing::StrictMock<MockHttp2Visitor> http2_visitor_;
+ DataSavingVisitor http2_visitor_;
std::unique_ptr<OgHttp2Adapter> adapter_;
};
+TEST_F(OgHttp2AdapterTest, IsServerSession) {
+ EXPECT_TRUE(adapter_->IsServerSession());
+}
+
TEST_F(OgHttp2AdapterTest, ProcessBytes) {
testing::InSequence seq;
EXPECT_CALL(http2_visitor_, OnFrameHeader(0, 0, 4, 0));
@@ -33,13 +56,527 @@ TEST_F(OgHttp2AdapterTest, ProcessBytes) {
TestFrameSequence().ClientPreface().Ping(17).Serialize());
}
+TEST(OgHttp2AdapterClientTest, ClientHandlesTrailers) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .Headers(1, {{"final-status", "A-OK"}},
+ /*fin=*/true)
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor,
+ OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 0));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, 26));
+ EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "final-status", "A-OK"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndStream(1));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+
+ const size_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+}
+
+TEST(OgHttp2AdapterClientTest, ClientHandlesMetadata) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Metadata(0, "Example connection metadata")
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Metadata(1, "Example stream metadata")
+ .Data(1, "This is the response body.", true)
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnMetadataEndForStream(0));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor,
+ OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnMetadataEndForStream(1));
+ EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 1));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, 26));
+ EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
+ EXPECT_CALL(visitor, OnEndStream(1));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+
+ const size_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+}
+
+TEST(OgHttp2AdapterClientTest, ClientRstStreamWhileHandlingHeaders) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor,
+ OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"))
+ .WillOnce(testing::DoAll(
+ testing::InvokeWithoutArgs([&adapter]() {
+ adapter->SubmitRst(1, Http2ErrorCode::REFUSED_STREAM);
+ }),
+ testing::Return(Http2VisitorInterface::HEADER_RST_STREAM)));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
+ EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
+
+ const size_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id1, 4, 0x0));
+ EXPECT_CALL(visitor,
+ OnFrameSent(RST_STREAM, stream_id1, 4, 0x0,
+ static_cast<int>(Http2ErrorCode::REFUSED_STREAM)));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::RST_STREAM}));
+}
+
+TEST(OgHttp2AdapterClientTest, ClientConnectionErrorWhileHandlingHeaders) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor,
+ OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"))
+ .WillOnce(
+ testing::Return(Http2VisitorInterface::HEADER_CONNECTION_ERROR));
+ EXPECT_CALL(visitor, OnConnectionError());
+ // Note: OgHttp2Adapter continues processing bytes until the input is
+ // complete.
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
+ EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
+
+ const size_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+}
+
+TEST(OgHttp2AdapterClientTest, ClientRejectsHeaders) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1))
+ .WillOnce(testing::Return(false));
+ // Rejecting headers leads to a connection error.
+ EXPECT_CALL(visitor, OnConnectionError());
+ // Note: OgHttp2Adapter continues processing bytes until the input is
+ // complete.
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
+ EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
+
+ const size_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_result, stream_frames.size());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+}
+
+// TODO(birenroy): Validate headers and re-enable this test. The library should
+// invoke OnErrorDebug() with an error message for the invalid header. The
+// library should also invoke OnInvalidFrame() for the invalid HEADERS frame.
+TEST(OgHttp2AdapterClientTest, DISABLED_ClientHandlesInvalidTrailers) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 =
+ ToHeaders({{":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}});
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ const int32_t stream_id1 =
+ adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
+ ASSERT_GT(stream_id1, 0);
+ QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ absl::string_view data = visitor.data();
+ EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::HEADERS}));
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .Headers(1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/false)
+ .Data(1, "This is the response body.")
+ .Headers(1, {{":bad-status", "9000"}},
+ /*fin=*/true)
+ .Serialize();
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor,
+ OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 0));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, 26));
+ EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+
+ // Bad status trailer will cause a PROTOCOL_ERROR. The header is never
+ // delivered in an OnHeaderForStream callback.
+
+ const size_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), stream_result);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id1, 4, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, stream_id1, 4, 0x0, 1));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::PROTOCOL_ERROR));
+
+ EXPECT_TRUE(adapter->session().want_write());
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::RST_STREAM}));
+}
+
TEST_F(OgHttp2AdapterTest, SubmitMetadata) {
- EXPECT_QUICHE_BUG(adapter_->SubmitMetadata(3, true), "Not implemented");
+ auto source = absl::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders(
+ {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}})));
+ adapter_->SubmitMetadata(1, std::move(source));
+ EXPECT_TRUE(adapter_->session().want_write());
+
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0));
+
+ int result = adapter_->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(
+ http2_visitor_.data(),
+ EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ static_cast<spdy::SpdyFrameType>(kMetadataFrameType)}));
+ EXPECT_FALSE(adapter_->session().want_write());
+}
+
+TEST_F(OgHttp2AdapterTest, SubmitConnectionMetadata) {
+ auto source = absl::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders(
+ {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}})));
+ adapter_->SubmitMetadata(0, std::move(source));
+ EXPECT_TRUE(adapter_->session().want_write());
+
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(kMetadataFrameType, 0, _, 0x4));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(kMetadataFrameType, 0, _, 0x4, 0));
+
+ int result = adapter_->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(
+ http2_visitor_.data(),
+ EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ static_cast<spdy::SpdyFrameType>(kMetadataFrameType)}));
+ EXPECT_FALSE(adapter_->session().want_write());
}
-TEST_F(OgHttp2AdapterTest, GetPeerConnectionWindow) {
- const int peer_window = adapter_->GetPeerConnectionWindow();
- EXPECT_GT(peer_window, 0);
+TEST_F(OgHttp2AdapterTest, GetSendWindowSize) {
+ const int peer_window = adapter_->GetSendWindowSize();
+ EXPECT_EQ(peer_window, kInitialFlowControlWindowSize);
}
TEST_F(OgHttp2AdapterTest, MarkDataConsumedForStream) {
@@ -62,12 +599,27 @@ TEST_F(OgHttp2AdapterTest, TestSerialize) {
adapter_->SubmitWindowUpdate(3, 127);
EXPECT_TRUE(adapter_->session().want_write());
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(PRIORITY, 3, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(PRIORITY, 3, _, 0x0, 0));
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(RST_STREAM, 3, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(RST_STREAM, 3, _, 0x0, 0x8));
+ EXPECT_CALL(http2_visitor_, OnCloseStream(3, Http2ErrorCode::NO_ERROR));
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(PING, 0, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(PING, 0, _, 0x0, 0));
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(GOAWAY, 0, _, 0x0, 0));
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(WINDOW_UPDATE, 3, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(WINDOW_UPDATE, 3, _, 0x0, 0));
+
+ int result = adapter_->Send();
+ EXPECT_EQ(0, result);
EXPECT_THAT(
- adapter_->GetBytesToWrite(absl::nullopt),
- EqualsFrames(
- {spdy::SpdyFrameType::SETTINGS, spdy::SpdyFrameType::PRIORITY,
- spdy::SpdyFrameType::RST_STREAM, spdy::SpdyFrameType::PING,
- spdy::SpdyFrameType::GOAWAY, spdy::SpdyFrameType::WINDOW_UPDATE}));
+ http2_visitor_.data(),
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::PRIORITY,
+ SpdyFrameType::RST_STREAM, SpdyFrameType::PING,
+ SpdyFrameType::GOAWAY, SpdyFrameType::WINDOW_UPDATE}));
EXPECT_FALSE(adapter_->session().want_write());
}
@@ -80,14 +632,271 @@ TEST_F(OgHttp2AdapterTest, TestPartialSerialize) {
adapter_->SubmitPing(42);
EXPECT_TRUE(adapter_->session().want_write());
- const std::string first_part = adapter_->GetBytesToWrite(10);
+ http2_visitor_.set_send_limit(20);
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ int result = adapter_->Send();
+ EXPECT_EQ(0, result);
+ EXPECT_TRUE(adapter_->session().want_write());
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(GOAWAY, 0, _, 0x0, 0));
+ result = adapter_->Send();
+ EXPECT_EQ(0, result);
EXPECT_TRUE(adapter_->session().want_write());
- const std::string second_part = adapter_->GetBytesToWrite(absl::nullopt);
+ EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(PING, 0, _, 0x0));
+ EXPECT_CALL(http2_visitor_, OnFrameSent(PING, 0, _, 0x0, 0));
+ result = adapter_->Send();
+ EXPECT_EQ(0, result);
EXPECT_FALSE(adapter_->session().want_write());
- EXPECT_THAT(
- absl::StrCat(first_part, second_part),
- EqualsFrames({spdy::SpdyFrameType::SETTINGS, spdy::SpdyFrameType::GOAWAY,
- spdy::SpdyFrameType::PING}));
+ EXPECT_THAT(http2_visitor_.data(),
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY,
+ SpdyFrameType::PING}));
+}
+
+TEST(OgHttp2AdapterServerTest, ClientSendsContinuation) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kServer};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+ EXPECT_FALSE(adapter->session().want_write());
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/true,
+ /*add_continuation=*/true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 1));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, 4));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndStream(1));
+
+ const size_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+}
+
+TEST(OgHttp2AdapterServerTest, ClientSendsMetadataWithContinuation) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kServer};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+ EXPECT_FALSE(adapter->session().want_write());
+
+ const std::string frames =
+ TestFrameSequence()
+ .ClientPreface()
+ .Metadata(0, "Example connection metadata in multiple frames", true)
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/false,
+ /*add_continuation=*/true)
+ .Metadata(1,
+ "Some stream metadata that's also sent in multiple frames",
+ true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Metadata on stream 0
+ EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 0));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(0, _));
+ EXPECT_CALL(visitor, OnMetadataEndForStream(0));
+
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, 4));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ // Metadata on stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 0));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4));
+ EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnMetadataForStream(1, _));
+ EXPECT_CALL(visitor, OnMetadataEndForStream(1));
+
+ const size_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+ EXPECT_EQ(TestFrameSequence::MetadataBlockForPayload(
+ "Example connection metadata in multiple frames"),
+ absl::StrJoin(visitor.GetMetadata(0), ""));
+ EXPECT_EQ(TestFrameSequence::MetadataBlockForPayload(
+ "Some stream metadata that's also sent in multiple frames"),
+ absl::StrJoin(visitor.GetMetadata(1), ""));
+}
+
+TEST(OgHttp2AdapterServerTest, ServerSendsInvalidTrailers) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kServer};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+ EXPECT_FALSE(adapter->session().want_write());
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndStream(1));
+
+ const size_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+
+ const absl::string_view kBody = "This is an example response body.";
+
+ // The body source must indicate that the end of the body is not the end of
+ // the stream.
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false);
+ body1->AppendPayload(kBody);
+ body1->EndData();
+ int submit_result = adapter->SubmitResponse(
+ 1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}),
+ std::move(body1));
+ EXPECT_EQ(submit_result, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0));
+
+ int send_result = adapter->Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames(
+ {spdy::SpdyFrameType::SETTINGS, spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::HEADERS, spdy::SpdyFrameType::DATA}));
+ EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody));
+ visitor.Clear();
+ EXPECT_FALSE(adapter->session().want_write());
+
+ // The body source has been exhausted by the call to Send() above.
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+ int trailer_result =
+ adapter->SubmitTrailer(1, ToHeaders({{":final-status", "a-ok"}}));
+ ASSERT_EQ(trailer_result, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0));
+
+ send_result = adapter->Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+}
+
+TEST(OgHttp2AdapterServerTest, ServerErrorWhileHandlingHeaders) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kServer};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "POST"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"},
+ {"accept", "some bogus value!"}},
+ /*fin=*/false)
+ .WindowUpdate(1, 2000)
+ .Data(1, "This is the request body.")
+ .WindowUpdate(0, 2000)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, "accept", "some bogus value!"))
+ .WillOnce(testing::Return(Http2VisitorInterface::HEADER_RST_STREAM));
+ EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
+ EXPECT_CALL(visitor, OnWindowUpdate(1, 2000));
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
+ EXPECT_CALL(visitor, OnDataForStream(1, "This is the request body."));
+ EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0));
+ EXPECT_CALL(visitor, OnWindowUpdate(0, 2000));
+
+ const size_t result = adapter->ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+
+ EXPECT_TRUE(adapter->session().want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0));
+ EXPECT_CALL(visitor,
+ OnFrameSent(RST_STREAM, 1, 4, 0x0,
+ static_cast<int>(Http2ErrorCode::INTERNAL_ERROR)));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+
+ int send_result = adapter->Send();
+ // Some bytes should have been serialized.
+ EXPECT_EQ(0, send_result);
+ // SETTINGS ack
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::RST_STREAM}));
}
} // namespace
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.cc b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.cc
index 2222fed4b6b..3a32b2e70ea 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.cc
@@ -1,29 +1,150 @@
#include "http2/adapter/oghttp2_session.h"
+#include <tuple>
+
#include "absl/strings/escaping.h"
+#include "http2/adapter/oghttp2_util.h"
namespace http2 {
namespace adapter {
+namespace {
+
+const size_t kMaxMetadataFrameSize = 16384;
+
+// TODO(birenroy): Consider incorporating spdy::FlagsSerializionVisitor here.
+class FrameAttributeCollector : public spdy::SpdyFrameVisitor {
+ public:
+ FrameAttributeCollector() = default;
+ void VisitData(const spdy::SpdyDataIR& data) override {
+ frame_type_ = static_cast<uint8_t>(data.frame_type());
+ stream_id_ = data.stream_id();
+ length_ =
+ data.data_len() + (data.padded() ? 1 : 0) + data.padding_payload_len();
+ flags_ = (data.fin() ? 0x1 : 0) | (data.padded() ? 0x8 : 0);
+ }
+ void VisitHeaders(const spdy::SpdyHeadersIR& headers) override {
+ frame_type_ = static_cast<uint8_t>(headers.frame_type());
+ stream_id_ = headers.stream_id();
+ length_ = headers.size() - spdy::kFrameHeaderSize;
+ flags_ = 0x4 | (headers.fin() ? 0x1 : 0) | (headers.padded() ? 0x8 : 0) |
+ (headers.has_priority() ? 0x20 : 0);
+ }
+ void VisitPriority(const spdy::SpdyPriorityIR& priority) override {
+ frame_type_ = static_cast<uint8_t>(priority.frame_type());
+ frame_type_ = 2;
+ length_ = 5;
+ stream_id_ = priority.stream_id();
+ }
+ void VisitRstStream(const spdy::SpdyRstStreamIR& rst_stream) override {
+ frame_type_ = static_cast<uint8_t>(rst_stream.frame_type());
+ frame_type_ = 3;
+ length_ = 4;
+ stream_id_ = rst_stream.stream_id();
+ error_code_ = rst_stream.error_code();
+ }
+ void VisitSettings(const spdy::SpdySettingsIR& settings) override {
+ frame_type_ = static_cast<uint8_t>(settings.frame_type());
+ frame_type_ = 4;
+ length_ = 6 * settings.values().size();
+ flags_ = (settings.is_ack() ? 0x1 : 0);
+ }
+ void VisitPushPromise(const spdy::SpdyPushPromiseIR& push_promise) override {
+ frame_type_ = static_cast<uint8_t>(push_promise.frame_type());
+ frame_type_ = 5;
+ length_ = push_promise.size() - spdy::kFrameHeaderSize;
+ stream_id_ = push_promise.stream_id();
+ flags_ = (push_promise.padded() ? 0x8 : 0);
+ }
+ void VisitPing(const spdy::SpdyPingIR& ping) override {
+ frame_type_ = static_cast<uint8_t>(ping.frame_type());
+ frame_type_ = 6;
+ length_ = 8;
+ flags_ = (ping.is_ack() ? 0x1 : 0);
+ }
+ void VisitGoAway(const spdy::SpdyGoAwayIR& goaway) override {
+ frame_type_ = static_cast<uint8_t>(goaway.frame_type());
+ frame_type_ = 7;
+ length_ = goaway.size() - spdy::kFrameHeaderSize;
+ error_code_ = goaway.error_code();
+ }
+ void VisitWindowUpdate(
+ const spdy::SpdyWindowUpdateIR& window_update) override {
+ frame_type_ = static_cast<uint8_t>(window_update.frame_type());
+ frame_type_ = 8;
+ length_ = 4;
+ stream_id_ = window_update.stream_id();
+ }
+ void VisitContinuation(
+ const spdy::SpdyContinuationIR& continuation) override {
+ frame_type_ = static_cast<uint8_t>(continuation.frame_type());
+ stream_id_ = continuation.stream_id();
+ flags_ = continuation.end_headers() ? 0x4 : 0;
+ length_ = continuation.size() - spdy::kFrameHeaderSize;
+ }
+ void VisitUnknown(const spdy::SpdyUnknownIR& unknown) override {
+ frame_type_ = static_cast<uint8_t>(unknown.frame_type());
+ stream_id_ = unknown.stream_id();
+ flags_ = unknown.flags();
+ length_ = unknown.size() - spdy::kFrameHeaderSize;
+ }
+ void VisitAltSvc(const spdy::SpdyAltSvcIR& /*altsvc*/) override {}
+ void VisitPriorityUpdate(
+ const spdy::SpdyPriorityUpdateIR& /*priority_update*/) override {}
+ void VisitAcceptCh(const spdy::SpdyAcceptChIR& /*accept_ch*/) override {}
+
+ uint32_t stream_id() { return stream_id_; }
+ uint32_t length() { return length_; }
+ uint32_t error_code() { return error_code_; }
+ uint8_t frame_type() { return frame_type_; }
+ uint8_t flags() { return flags_; }
+
+ private:
+ uint32_t stream_id_ = 0;
+ uint32_t length_ = 0;
+ uint32_t error_code_ = 0;
+ uint8_t frame_type_ = 0;
+ uint8_t flags_ = 0;
+};
+
+} // namespace
+
void OgHttp2Session::PassthroughHeadersHandler::OnHeaderBlockStart() {
- visitor_.OnBeginHeadersForStream(stream_id_);
+ const bool status = visitor_.OnBeginHeadersForStream(stream_id_);
+ if (!status) {
+ result_ = Http2VisitorInterface::HEADER_CONNECTION_ERROR;
+ }
}
void OgHttp2Session::PassthroughHeadersHandler::OnHeader(
absl::string_view key,
absl::string_view value) {
- visitor_.OnHeaderForStream(stream_id_, key, value);
+ if (result_ == Http2VisitorInterface::HEADER_OK) {
+ result_ = visitor_.OnHeaderForStream(stream_id_, key, value);
+ }
}
void OgHttp2Session::PassthroughHeadersHandler::OnHeaderBlockEnd(
size_t /* uncompressed_header_bytes */,
size_t /* compressed_header_bytes */) {
- visitor_.OnEndHeadersForStream(stream_id_);
+ if (result_ == Http2VisitorInterface::HEADER_OK) {
+ visitor_.OnEndHeadersForStream(stream_id_);
+ } else {
+ session_.OnHeaderStatus(stream_id_, result_);
+ }
}
OgHttp2Session::OgHttp2Session(Http2VisitorInterface& visitor, Options options)
- : visitor_(visitor), headers_handler_(visitor), options_(options) {
+ : visitor_(visitor),
+ headers_handler_(*this, visitor),
+ connection_window_manager_(kInitialFlowControlWindowSize,
+ [this](size_t window_update_delta) {
+ SendWindowUpdate(kConnectionStreamId,
+ window_update_delta);
+ }),
+ options_(options) {
decoder_.set_visitor(this);
+ decoder_.set_extension_visitor(this);
if (options_.perspective == Perspective::kServer) {
remaining_preface_ = {spdy::kHttp2ConnectionHeaderPrefix,
spdy::kHttp2ConnectionHeaderPrefixSize};
@@ -32,6 +153,70 @@ OgHttp2Session::OgHttp2Session(Http2VisitorInterface& visitor, Options options)
OgHttp2Session::~OgHttp2Session() {}
+void OgHttp2Session::SetStreamUserData(Http2StreamId stream_id,
+ void* user_data) {
+ auto it = stream_map_.find(stream_id);
+ if (it != stream_map_.end()) {
+ it->second.user_data = user_data;
+ }
+}
+
+void* OgHttp2Session::GetStreamUserData(Http2StreamId stream_id) {
+ auto it = stream_map_.find(stream_id);
+ if (it != stream_map_.end()) {
+ return it->second.user_data;
+ }
+ return nullptr;
+}
+
+bool OgHttp2Session::ResumeStream(Http2StreamId stream_id) {
+ auto it = stream_map_.find(stream_id);
+ if (it->second.outbound_body == nullptr ||
+ !write_scheduler_.StreamRegistered(stream_id)) {
+ return false;
+ }
+ write_scheduler_.MarkStreamReady(stream_id, /*add_to_front=*/false);
+ return true;
+}
+
+int OgHttp2Session::GetStreamSendWindowSize(Http2StreamId stream_id) const {
+ auto it = stream_map_.find(stream_id);
+ if (it != stream_map_.end()) {
+ return it->second.send_window;
+ }
+ return -1;
+}
+
+int OgHttp2Session::GetStreamReceiveWindowLimit(Http2StreamId stream_id) const {
+ auto it = stream_map_.find(stream_id);
+ if (it != stream_map_.end()) {
+ return it->second.window_manager.WindowSizeLimit();
+ }
+ return -1;
+}
+
+int OgHttp2Session::GetStreamReceiveWindowSize(Http2StreamId stream_id) const {
+ auto it = stream_map_.find(stream_id);
+ if (it != stream_map_.end()) {
+ return it->second.window_manager.CurrentWindowSize();
+ }
+ return -1;
+}
+
+int OgHttp2Session::GetReceiveWindowSize() const {
+ return connection_window_manager_.CurrentWindowSize();
+}
+
+int OgHttp2Session::GetHpackEncoderDynamicTableSize() const {
+ const spdy::HpackEncoder* encoder = framer_.GetHpackEncoder();
+ return encoder == nullptr ? 0 : encoder->GetDynamicTableSize();
+}
+
+int OgHttp2Session::GetHpackDecoderDynamicTableSize() const {
+ const spdy::HpackDecoderAdapter* decoder = decoder_.GetHpackDecoder();
+ return decoder == nullptr ? 0 : decoder->GetDynamicTableSize();
+}
+
ssize_t OgHttp2Session::ProcessBytes(absl::string_view bytes) {
ssize_t preface_consumed = 0;
if (!remaining_preface_.empty()) {
@@ -68,27 +253,317 @@ int OgHttp2Session::Consume(Http2StreamId stream_id, size_t num_bytes) {
} else {
it->second.window_manager.MarkDataFlushed(num_bytes);
}
+ connection_window_manager_.MarkDataFlushed(num_bytes);
return 0; // Remove?
}
+void OgHttp2Session::StartGracefulShutdown() {
+ if (options_.perspective == Perspective::kServer) {
+ if (!queued_goaway_) {
+ EnqueueFrame(absl::make_unique<spdy::SpdyGoAwayIR>(
+ std::numeric_limits<int32_t>::max(), spdy::ERROR_CODE_NO_ERROR,
+ "graceful_shutdown"));
+ }
+ } else {
+ QUICHE_LOG(ERROR) << "Graceful shutdown not needed for clients.";
+ }
+}
+
void OgHttp2Session::EnqueueFrame(std::unique_ptr<spdy::SpdyFrameIR> frame) {
+ if (frame->frame_type() == spdy::SpdyFrameType::GOAWAY) {
+ queued_goaway_ = true;
+ } else if (frame->frame_type() == spdy::SpdyFrameType::RST_STREAM) {
+ streams_reset_.insert(frame->stream_id());
+ auto iter = stream_map_.find(frame->stream_id());
+ if (iter != stream_map_.end()) {
+ iter->second.half_closed_local = true;
+ }
+ }
frames_.push_back(std::move(frame));
}
-std::string OgHttp2Session::GetBytesToWrite(absl::optional<size_t> max_bytes) {
- const size_t serialized_max =
- max_bytes ? max_bytes.value() : std::numeric_limits<size_t>::max();
- std::string serialized = std::move(serialized_prefix_);
- while (serialized.size() < serialized_max && !frames_.empty()) {
- spdy::SpdySerializedFrame frame = framer_.SerializeFrame(*frames_.front());
- absl::StrAppend(&serialized, absl::string_view(frame));
- frames_.pop_front();
+int OgHttp2Session::Send() {
+ MaybeSetupPreface();
+ ssize_t result = std::numeric_limits<ssize_t>::max();
+ // Flush any serialized prefix.
+ while (result > 0 && !serialized_prefix_.empty()) {
+ result = visitor_.OnReadyToSend(serialized_prefix_);
+ if (result > 0) {
+ serialized_prefix_.erase(0, result);
+ }
+ }
+ if (!serialized_prefix_.empty()) {
+ return result < 0 ? result : 0;
+ }
+ bool continue_writing = SendQueuedFrames();
+ while (continue_writing && !connection_metadata_.empty()) {
+ continue_writing = SendMetadata(0, connection_metadata_);
+ }
+ // Wake streams for writes.
+ while (continue_writing && write_scheduler_.HasReadyStreams() &&
+ connection_send_window_ > 0) {
+ const Http2StreamId stream_id = write_scheduler_.PopNextReadyStream();
+ // TODO(birenroy): Add a return value to indicate write blockage, so streams
+ // aren't woken unnecessarily.
+ continue_writing = WriteForStream(stream_id);
+ }
+ if (continue_writing) {
+ SendQueuedFrames();
+ }
+ return 0;
+}
+
+bool OgHttp2Session::SendQueuedFrames() {
+ // Serialize and send frames in the queue.
+ while (!frames_.empty()) {
+ const auto& frame_ptr = frames_.front();
+ FrameAttributeCollector c;
+ frame_ptr->Visit(&c);
+ visitor_.OnBeforeFrameSent(c.frame_type(), c.stream_id(), c.length(),
+ c.flags());
+ spdy::SpdySerializedFrame frame = framer_.SerializeFrame(*frame_ptr);
+ const ssize_t result = visitor_.OnReadyToSend(absl::string_view(frame));
+ if (result < 0) {
+ visitor_.OnConnectionError();
+ return false;
+ } else if (result == 0) {
+ // Write blocked.
+ return false;
+ } else {
+ visitor_.OnFrameSent(c.frame_type(), c.stream_id(), c.length(), c.flags(),
+ c.error_code());
+ if (static_cast<FrameType>(c.frame_type()) == FrameType::RST_STREAM) {
+ // If this endpoint is resetting the stream, the stream should be
+ // closed. This endpoint is already aware of the outbound RST_STREAM and
+ // its error code, so close with NO_ERROR.
+ visitor_.OnCloseStream(c.stream_id(), Http2ErrorCode::NO_ERROR);
+ }
+
+ frames_.pop_front();
+ if (static_cast<size_t>(result) < frame.size()) {
+ // The frame was partially written, so the rest must be buffered.
+ serialized_prefix_.assign(frame.data() + result, frame.size() - result);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool OgHttp2Session::WriteForStream(Http2StreamId stream_id) {
+ auto it = stream_map_.find(stream_id);
+ if (it == stream_map_.end()) {
+ QUICHE_LOG(ERROR) << "Can't find stream " << stream_id
+ << " which is ready to write!";
+ return true;
+ }
+ StreamState& state = it->second;
+ bool connection_can_write = true;
+ if (!state.outbound_metadata.empty()) {
+ connection_can_write = SendMetadata(stream_id, state.outbound_metadata);
+ }
+
+ if (state.outbound_body == nullptr) {
+ // No data to send, but there might be trailers.
+ if (state.trailers != nullptr) {
+ auto block_ptr = std::move(state.trailers);
+ if (state.half_closed_local) {
+ QUICHE_LOG(ERROR) << "Sent fin; can't send trailers.";
+ } else {
+ SendTrailers(stream_id, std::move(*block_ptr));
+ MaybeCloseWithRstStream(stream_id, state);
+ }
+ }
+ return true;
+ }
+ bool source_can_produce = true;
+ int32_t available_window = std::min(
+ std::min(connection_send_window_, state.send_window), max_frame_payload_);
+ while (connection_can_write && available_window > 0 &&
+ state.outbound_body != nullptr) {
+ ssize_t length;
+ bool end_data;
+ std::tie(length, end_data) =
+ state.outbound_body->SelectPayloadLength(available_window);
+ if (length == 0 && !end_data) {
+ source_can_produce = false;
+ break;
+ } else if (length == DataFrameSource::kError) {
+ source_can_produce = false;
+ visitor_.OnCloseStream(stream_id, Http2ErrorCode::INTERNAL_ERROR);
+ break;
+ }
+ const bool fin = end_data ? state.outbound_body->send_fin() : false;
+ if (length > 0 || fin) {
+ spdy::SpdyDataIR data(stream_id);
+ data.set_fin(fin);
+ data.SetDataShallow(length);
+ spdy::SpdySerializedFrame header =
+ spdy::SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField(
+ data);
+ QUICHE_DCHECK(serialized_prefix_.empty() && frames_.empty());
+ const bool success =
+ state.outbound_body->Send(absl::string_view(header), length);
+ if (!success) {
+ connection_can_write = false;
+ break;
+ }
+ visitor_.OnFrameSent(/* DATA */ 0, stream_id, length, fin ? 0x1 : 0x0, 0);
+ connection_send_window_ -= length;
+ state.send_window -= length;
+ available_window =
+ std::min(std::min(connection_send_window_, state.send_window),
+ max_frame_payload_);
+ }
+ if (end_data) {
+ bool sent_trailers = false;
+ if (state.trailers != nullptr) {
+ auto block_ptr = std::move(state.trailers);
+ if (fin) {
+ QUICHE_LOG(ERROR) << "Sent fin; can't send trailers.";
+ } else {
+ SendTrailers(stream_id, std::move(*block_ptr));
+ sent_trailers = true;
+ }
+ }
+ state.outbound_body = nullptr;
+ if (fin || sent_trailers) {
+ MaybeCloseWithRstStream(stream_id, state);
+ }
+ }
+ }
+ // If the stream still has data to send, it should be marked as ready in the
+ // write scheduler.
+ if (source_can_produce && state.send_window > 0 &&
+ state.outbound_body != nullptr) {
+ write_scheduler_.MarkStreamReady(stream_id, false);
+ }
+ // Streams can continue writing as long as the connection is not write-blocked
+ // and there is additional flow control quota available.
+ return connection_can_write && available_window > 0;
+}
+
+bool OgHttp2Session::SendMetadata(Http2StreamId stream_id,
+ OgHttp2Session::MetadataSequence& sequence) {
+ auto payload_buffer = absl::make_unique<uint8_t[]>(kMaxMetadataFrameSize);
+ while (!sequence.empty()) {
+ MetadataSource& source = *sequence.front();
+
+ ssize_t written;
+ bool end_metadata;
+ std::tie(written, end_metadata) =
+ source.Pack(payload_buffer.get(), kMaxMetadataFrameSize);
+ if (written < 0) {
+ // Did not touch the connection, so perhaps writes are still possible.
+ return true;
+ }
+ QUICHE_DCHECK_LE(static_cast<size_t>(written), kMaxMetadataFrameSize);
+ auto payload = absl::string_view(
+ reinterpret_cast<const char*>(payload_buffer.get()), written);
+ EnqueueFrame(absl::make_unique<spdy::SpdyUnknownIR>(
+ stream_id, kMetadataFrameType, end_metadata ? kMetadataEndFlag : 0u,
+ std::string(payload)));
+ if (end_metadata) {
+ sequence.erase(sequence.begin());
+ }
+ }
+ return SendQueuedFrames();
+}
+
+int32_t OgHttp2Session::SubmitRequest(
+ absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source, void* user_data) {
+ // TODO(birenroy): return an error for the incorrect perspective
+ const Http2StreamId stream_id = next_stream_id_;
+ next_stream_id_ += 2;
+ // Convert headers to header block, create headers frame.
+ auto frame =
+ absl::make_unique<spdy::SpdyHeadersIR>(stream_id, ToHeaderBlock(headers));
+ // Add data source and user data to stream state
+ auto iter = CreateStream(stream_id);
+ write_scheduler_.MarkStreamReady(stream_id, false);
+ if (data_source == nullptr) {
+ frame->set_fin(true);
+ iter->second.half_closed_local = true;
+ } else {
+ iter->second.outbound_body = std::move(data_source);
+ }
+ iter->second.user_data = user_data;
+ // Enqueue headers frame
+ EnqueueFrame(std::move(frame));
+ return stream_id;
+}
+
+int OgHttp2Session::SubmitResponse(
+ Http2StreamId stream_id, absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source) {
+ // TODO(birenroy): return an error for the incorrect perspective
+ auto iter = stream_map_.find(stream_id);
+ if (iter == stream_map_.end()) {
+ QUICHE_LOG(ERROR) << "Unable to find stream " << stream_id;
+ return -501; // NGHTTP2_ERR_INVALID_ARGUMENT
+ }
+ // Convert headers to header block, create headers frame
+ auto frame =
+ absl::make_unique<spdy::SpdyHeadersIR>(stream_id, ToHeaderBlock(headers));
+ if (data_source == nullptr) {
+ frame->set_fin(true);
+ if (iter->second.half_closed_remote) {
+ visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR);
+ }
+ // TODO(birenroy): the server adapter should probably delete stream state
+ // when calling visitor_.OnCloseStream.
+ } else {
+ // Add data source to stream state
+ iter->second.outbound_body = std::move(data_source);
+ write_scheduler_.MarkStreamReady(stream_id, false);
+ }
+ EnqueueFrame(std::move(frame));
+ return 0;
+}
+
+int OgHttp2Session::SubmitTrailer(Http2StreamId stream_id,
+ absl::Span<const Header> trailers) {
+ // TODO(birenroy): Reject trailers when acting as a client?
+ auto iter = stream_map_.find(stream_id);
+ if (iter == stream_map_.end()) {
+ QUICHE_LOG(ERROR) << "Unable to find stream " << stream_id;
+ return -501; // NGHTTP2_ERR_INVALID_ARGUMENT
+ }
+ StreamState& state = iter->second;
+ if (state.half_closed_local) {
+ QUICHE_LOG(ERROR) << "Stream " << stream_id << " is half closed (local)";
+ return -514; // NGHTTP2_ERR_INVALID_STREAM_STATE
}
- if (serialized.size() > serialized_max) {
- serialized_prefix_ = serialized.substr(serialized_max);
- serialized.resize(serialized_max);
+ if (state.trailers != nullptr) {
+ QUICHE_LOG(ERROR) << "Stream " << stream_id
+ << " already has trailers queued";
+ return -514; // NGHTTP2_ERR_INVALID_STREAM_STATE
+ }
+ if (state.outbound_body == nullptr) {
+ // Enqueue trailers immediately.
+ SendTrailers(stream_id, ToHeaderBlock(trailers));
+ MaybeCloseWithRstStream(stream_id, state);
+ } else {
+ QUICHE_LOG_IF(ERROR, state.outbound_body->send_fin())
+ << "DataFrameSource will send fin, preventing trailers!";
+ // Save trailers so they can be written once data is done.
+ state.trailers =
+ absl::make_unique<spdy::SpdyHeaderBlock>(ToHeaderBlock(trailers));
+ write_scheduler_.MarkStreamReady(stream_id, false);
+ }
+ return 0;
+}
+
+void OgHttp2Session::SubmitMetadata(Http2StreamId stream_id,
+ std::unique_ptr<MetadataSource> source) {
+ if (stream_id == 0) {
+ connection_metadata_.push_back(std::move(source));
+ } else {
+ auto iter = CreateStream(stream_id);
+ iter->second.outbound_metadata.push_back(std::move(source));
+ write_scheduler_.MarkStreamReady(stream_id, false);
}
- return serialized;
}
void OgHttp2Session::OnError(http2::Http2DecoderAdapter::SpdyFramerError error,
@@ -103,32 +578,47 @@ void OgHttp2Session::OnCommonHeader(spdy::SpdyStreamId stream_id,
size_t length,
uint8_t type,
uint8_t flags) {
+ highest_received_stream_id_ = std::max(static_cast<Http2StreamId>(stream_id),
+ highest_received_stream_id_);
visitor_.OnFrameHeader(stream_id, length, type, flags);
}
void OgHttp2Session::OnDataFrameHeader(spdy::SpdyStreamId stream_id,
- size_t length,
- bool fin) {
+ size_t length, bool /*fin*/) {
visitor_.OnBeginDataForStream(stream_id, length);
}
void OgHttp2Session::OnStreamFrameData(spdy::SpdyStreamId stream_id,
const char* data,
size_t len) {
+ MarkDataBuffered(stream_id, len);
visitor_.OnDataForStream(stream_id, absl::string_view(data, len));
}
void OgHttp2Session::OnStreamEnd(spdy::SpdyStreamId stream_id) {
+ auto iter = stream_map_.find(stream_id);
+ if (iter != stream_map_.end()) {
+ iter->second.half_closed_remote = true;
+ }
visitor_.OnEndStream(stream_id);
+ if (iter != stream_map_.end() && iter->second.half_closed_local &&
+ options_.perspective == Perspective::kClient) {
+ // From the client's perspective, the stream can be closed if it's already
+ // half_closed_local.
+ visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR);
+ }
}
-void OgHttp2Session::OnStreamPadLength(spdy::SpdyStreamId /*stream_id*/,
- size_t /*value*/) {
- // TODO(181586191): handle padding
+void OgHttp2Session::OnStreamPadLength(spdy::SpdyStreamId stream_id,
+ size_t value) {
+ MarkDataBuffered(stream_id, 1 + value);
+ // TODO(181586191): Pass padding to the visitor?
}
-void OgHttp2Session::OnStreamPadding(spdy::SpdyStreamId stream_id, size_t len) {
- // TODO(181586191): handle padding
+void OgHttp2Session::OnStreamPadding(spdy::SpdyStreamId /*stream_id*/, size_t
+ /*len*/) {
+ // Flow control was accounted for in OnStreamPadLength().
+ // TODO(181586191): Pass padding to the visitor?
}
spdy::SpdyHeadersHandlerInterface* OgHttp2Session::OnHeaderFrameStart(
@@ -137,13 +627,21 @@ spdy::SpdyHeadersHandlerInterface* OgHttp2Session::OnHeaderFrameStart(
return &headers_handler_;
}
-void OgHttp2Session::OnHeaderFrameEnd(spdy::SpdyStreamId stream_id) {
+void OgHttp2Session::OnHeaderFrameEnd(spdy::SpdyStreamId /*stream_id*/) {
headers_handler_.set_stream_id(0);
}
void OgHttp2Session::OnRstStream(spdy::SpdyStreamId stream_id,
spdy::SpdyErrorCode error_code) {
+ auto iter = stream_map_.find(stream_id);
+ if (iter != stream_map_.end()) {
+ iter->second.half_closed_remote = true;
+ iter->second.outbound_body = nullptr;
+ write_scheduler_.UnregisterStream(stream_id);
+ }
visitor_.OnRstStream(stream_id, TranslateErrorCode(error_code));
+ // TODO(birenroy): Consider bundling "close stream" behavior into a dedicated
+ // method that also cleans up the stream map.
visitor_.OnCloseStream(stream_id, TranslateErrorCode(error_code));
}
@@ -153,10 +651,16 @@ void OgHttp2Session::OnSettings() {
void OgHttp2Session::OnSetting(spdy::SpdySettingsId id, uint32_t value) {
visitor_.OnSetting({id, value});
+ if (id == kMetadataExtensionId) {
+ peer_supports_metadata_ = (value != 0);
+ }
}
void OgHttp2Session::OnSettingsEnd() {
visitor_.OnSettingsEnd();
+ auto settings = absl::make_unique<spdy::SpdySettingsIR>();
+ settings->set_is_ack(true);
+ EnqueueFrame(std::move(settings));
}
void OgHttp2Session::OnSettingsAck() {
@@ -174,39 +678,46 @@ void OgHttp2Session::OnGoAway(spdy::SpdyStreamId last_accepted_stream_id,
"");
}
-bool OgHttp2Session::OnGoAwayFrameData(const char* goaway_data, size_t len) {
+bool OgHttp2Session::OnGoAwayFrameData(const char* /*goaway_data*/, size_t
+ /*len*/) {
// Opaque data is currently ignored.
return true;
}
void OgHttp2Session::OnHeaders(spdy::SpdyStreamId stream_id,
- bool has_priority,
- int weight,
- spdy::SpdyStreamId parent_stream_id,
- bool exclusive,
- bool fin,
- bool end) {}
+ bool /*has_priority*/, int /*weight*/,
+ spdy::SpdyStreamId /*parent_stream_id*/,
+ bool /*exclusive*/, bool /*fin*/, bool /*end*/) {
+ if (options_.perspective == Perspective::kServer) {
+ CreateStream(stream_id);
+ }
+}
void OgHttp2Session::OnWindowUpdate(spdy::SpdyStreamId stream_id,
int delta_window_size) {
if (stream_id == 0) {
- peer_window_ += delta_window_size;
+ connection_send_window_ += delta_window_size;
} else {
auto it = stream_map_.find(stream_id);
if (it == stream_map_.end()) {
QUICHE_VLOG(1) << "Stream " << stream_id << " not found!";
} else {
+ if (it->second.send_window == 0) {
+ // The stream was blocked on flow control.
+ write_scheduler_.MarkStreamReady(stream_id, false);
+ }
it->second.send_window += delta_window_size;
}
}
visitor_.OnWindowUpdate(stream_id, delta_window_size);
}
-void OgHttp2Session::OnPushPromise(spdy::SpdyStreamId stream_id,
- spdy::SpdyStreamId promised_stream_id,
- bool end) {}
+void OgHttp2Session::OnPushPromise(spdy::SpdyStreamId /*stream_id*/,
+ spdy::SpdyStreamId /*promised_stream_id*/,
+ bool /*end*/) {}
-void OgHttp2Session::OnContinuation(spdy::SpdyStreamId stream_id, bool end) {}
+void OgHttp2Session::OnContinuation(spdy::SpdyStreamId /*stream_id*/, bool
+ /*end*/) {}
void OgHttp2Session::OnAltSvc(spdy::SpdyStreamId /*stream_id*/,
absl::string_view /*origin*/,
@@ -214,18 +725,141 @@ void OgHttp2Session::OnAltSvc(spdy::SpdyStreamId /*stream_id*/,
AlternativeServiceVector& /*altsvc_vector*/) {
}
-void OgHttp2Session::OnPriority(spdy::SpdyStreamId stream_id,
- spdy::SpdyStreamId parent_stream_id,
- int weight,
- bool exclusive) {}
+void OgHttp2Session::OnPriority(spdy::SpdyStreamId /*stream_id*/,
+ spdy::SpdyStreamId /*parent_stream_id*/,
+ int /*weight*/, bool /*exclusive*/) {}
-void OgHttp2Session::OnPriorityUpdate(spdy::SpdyStreamId prioritized_stream_id,
- absl::string_view priority_field_value) {}
+void OgHttp2Session::OnPriorityUpdate(
+ spdy::SpdyStreamId /*prioritized_stream_id*/,
+ absl::string_view /*priority_field_value*/) {}
-bool OgHttp2Session::OnUnknownFrame(spdy::SpdyStreamId stream_id,
- uint8_t frame_type) {
+bool OgHttp2Session::OnUnknownFrame(spdy::SpdyStreamId /*stream_id*/,
+ uint8_t /*frame_type*/) {
return true;
}
+void OgHttp2Session::OnHeaderStatus(
+ Http2StreamId stream_id, Http2VisitorInterface::OnHeaderResult result) {
+ QUICHE_DCHECK_NE(result, Http2VisitorInterface::HEADER_OK);
+ if (result == Http2VisitorInterface::HEADER_RST_STREAM) {
+ auto it = streams_reset_.find(stream_id);
+ if (it == streams_reset_.end()) {
+ EnqueueFrame(absl::make_unique<spdy::SpdyRstStreamIR>(
+ stream_id, spdy::ERROR_CODE_INTERNAL_ERROR));
+ }
+ } else if (result == Http2VisitorInterface::HEADER_CONNECTION_ERROR) {
+ visitor_.OnConnectionError();
+ }
+}
+
+bool OgHttp2Session::OnFrameHeader(spdy::SpdyStreamId stream_id, size_t length,
+ uint8_t type, uint8_t flags) {
+ if (type == kMetadataFrameType) {
+ QUICHE_DCHECK_EQ(metadata_length_, 0u);
+ visitor_.OnBeginMetadataForStream(stream_id, length);
+ metadata_stream_id_ = stream_id;
+ metadata_length_ = length;
+ end_metadata_ = flags & kMetadataEndFlag;
+ return true;
+ } else {
+ QUICHE_DLOG(INFO) << "Unexpected frame type " << static_cast<int>(type)
+ << " received by the extension visitor.";
+ return false;
+ }
+}
+
+void OgHttp2Session::OnFramePayload(const char* data, size_t len) {
+ if (metadata_length_ > 0) {
+ QUICHE_DCHECK_LE(len, metadata_length_);
+ visitor_.OnMetadataForStream(metadata_stream_id_,
+ absl::string_view(data, len));
+ metadata_length_ -= len;
+ if (metadata_length_ == 0 && end_metadata_) {
+ visitor_.OnMetadataEndForStream(metadata_stream_id_);
+ metadata_stream_id_ = 0;
+ end_metadata_ = false;
+ }
+ } else {
+ QUICHE_DLOG(INFO) << "Unexpected metadata payload for stream "
+ << metadata_stream_id_;
+ }
+}
+
+void OgHttp2Session::MaybeSetupPreface() {
+ if (!queued_preface_) {
+ if (options_.perspective == Perspective::kClient) {
+ serialized_prefix_.assign(spdy::kHttp2ConnectionHeaderPrefix,
+ spdy::kHttp2ConnectionHeaderPrefixSize);
+ }
+ // First frame must be a non-ack SETTINGS.
+ if (frames_.empty() ||
+ frames_.front()->frame_type() != spdy::SpdyFrameType::SETTINGS ||
+ reinterpret_cast<spdy::SpdySettingsIR*>(frames_.front().get())
+ ->is_ack()) {
+ frames_.push_front(absl::make_unique<spdy::SpdySettingsIR>());
+ }
+ queued_preface_ = true;
+ }
+}
+
+void OgHttp2Session::SendWindowUpdate(Http2StreamId stream_id,
+ size_t update_delta) {
+ EnqueueFrame(
+ absl::make_unique<spdy::SpdyWindowUpdateIR>(stream_id, update_delta));
+}
+
+void OgHttp2Session::SendTrailers(Http2StreamId stream_id,
+ spdy::SpdyHeaderBlock trailers) {
+ auto frame =
+ absl::make_unique<spdy::SpdyHeadersIR>(stream_id, std::move(trailers));
+ frame->set_fin(true);
+ EnqueueFrame(std::move(frame));
+}
+
+void OgHttp2Session::MaybeCloseWithRstStream(Http2StreamId stream_id,
+ StreamState& state) {
+ state.half_closed_local = true;
+ if (options_.perspective == Perspective::kServer) {
+ if (state.half_closed_remote) {
+ visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR);
+ } else {
+ // Since the peer has not yet ended the stream, this endpoint should
+ // send a RST_STREAM NO_ERROR. See RFC 7540 Section 8.1.
+ EnqueueFrame(absl::make_unique<spdy::SpdyRstStreamIR>(
+ stream_id, spdy::SpdyErrorCode::ERROR_CODE_NO_ERROR));
+ // Enqueuing the RST_STREAM also invokes OnCloseStream.
+ }
+ // TODO(birenroy): the server adapter should probably delete stream state
+ // when calling visitor_.OnCloseStream.
+ }
+}
+
+void OgHttp2Session::MarkDataBuffered(Http2StreamId stream_id, size_t bytes) {
+ connection_window_manager_.MarkDataBuffered(bytes);
+ auto it = stream_map_.find(stream_id);
+ if (it != stream_map_.end()) {
+ it->second.window_manager.MarkDataBuffered(bytes);
+ }
+}
+
+OgHttp2Session::StreamStateMap::iterator OgHttp2Session::CreateStream(
+ Http2StreamId stream_id) {
+ WindowManager::WindowUpdateListener listener =
+ [this, stream_id](size_t window_update_delta) {
+ SendWindowUpdate(stream_id, window_update_delta);
+ };
+ absl::flat_hash_map<Http2StreamId, StreamState>::iterator iter;
+ bool inserted;
+ std::tie(iter, inserted) = stream_map_.try_emplace(
+ stream_id,
+ StreamState(stream_receive_window_limit_, std::move(listener)));
+ if (inserted) {
+ // Add the stream to the write scheduler.
+ const WriteScheduler::StreamPrecedenceType precedence(3);
+ write_scheduler_.RegisterStream(stream_id, precedence);
+ }
+ return iter;
+}
+
} // namespace adapter
} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.h b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.h
index b0bc28b7002..77b9da73c38 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.h
@@ -3,11 +3,14 @@
#include <list>
+#include "http2/adapter/data_source.h"
#include "http2/adapter/http2_session.h"
#include "http2/adapter/http2_util.h"
#include "http2/adapter/http2_visitor_interface.h"
#include "http2/adapter/window_manager.h"
+#include "http2/core/priority_write_scheduler.h"
#include "common/platform/api/quiche_bug_tracker.h"
+#include "common/platform/api/quiche_export.h"
#include "spdy/core/http2_frame_decoder_adapter.h"
#include "spdy/core/spdy_framer.h"
@@ -15,33 +18,80 @@ namespace http2 {
namespace adapter {
// This class manages state associated with a single multiplexed HTTP/2 session.
-class OgHttp2Session : public Http2Session,
- public spdy::SpdyFramerVisitorInterface {
+class QUICHE_EXPORT_PRIVATE OgHttp2Session
+ : public Http2Session,
+ public spdy::SpdyFramerVisitorInterface,
+ public spdy::ExtensionVisitorInterface {
public:
- struct Options {
+ struct QUICHE_EXPORT_PRIVATE Options {
Perspective perspective = Perspective::kClient;
};
- OgHttp2Session(Http2VisitorInterface& visitor, Options /*options*/);
+ OgHttp2Session(Http2VisitorInterface& visitor, Options options);
~OgHttp2Session() override;
// Enqueues a frame for transmission to the peer.
void EnqueueFrame(std::unique_ptr<spdy::SpdyFrameIR> frame);
- // If |want_write()| returns true, this method will return a non-empty string
- // containing serialized HTTP/2 frames to write to the peer.
- std::string GetBytesToWrite(absl::optional<size_t> max_bytes);
+ // Starts a graceful shutdown sequence. No-op if a GOAWAY has already been
+ // sent.
+ void StartGracefulShutdown();
+
+ // Invokes the visitor's OnReadyToSend() method for serialized frame data.
+ int Send();
+
+ int32_t SubmitRequest(absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source,
+ void* user_data);
+ int SubmitResponse(Http2StreamId stream_id, absl::Span<const Header> headers,
+ std::unique_ptr<DataFrameSource> data_source);
+ int SubmitTrailer(Http2StreamId stream_id, absl::Span<const Header> trailers);
+ void SubmitMetadata(Http2StreamId stream_id,
+ std::unique_ptr<MetadataSource> source);
+
+ bool IsServerSession() const {
+ return options_.perspective == Perspective::kServer;
+ }
+ Http2StreamId GetHighestReceivedStreamId() const {
+ return highest_received_stream_id_;
+ }
+ void SetStreamUserData(Http2StreamId stream_id, void* user_data);
+ void* GetStreamUserData(Http2StreamId stream_id);
+
+ // Resumes a stream that was previously blocked. Returns true on success.
+ bool ResumeStream(Http2StreamId stream_id);
+
+ // Returns the peer's outstanding stream receive window for the given stream.
+ int GetStreamSendWindowSize(Http2StreamId stream_id) const;
+
+ // Returns the current upper bound on the flow control receive window for this
+ // stream.
+ int GetStreamReceiveWindowLimit(Http2StreamId stream_id) const;
+
+ // Returns the outstanding stream receive window, or -1 if the stream does not
+ // exist.
+ int GetStreamReceiveWindowSize(Http2StreamId stream_id) const;
+
+ // Returns the outstanding connection receive window.
+ int GetReceiveWindowSize() const;
+
+ // Returns the size of the HPACK encoder's dynamic table, including the
+ // per-entry overhead from the specification.
+ int GetHpackEncoderDynamicTableSize() const;
+
+ // Returns the size of the HPACK decoder's dynamic table, including the
+ // per-entry overhead from the specification.
+ int GetHpackDecoderDynamicTableSize() const;
// From Http2Session.
ssize_t ProcessBytes(absl::string_view bytes) override;
int Consume(Http2StreamId stream_id, size_t num_bytes) override;
bool want_read() const override { return !received_goaway_; }
bool want_write() const override {
- return !frames_.empty() || !serialized_prefix_.empty();
- }
- int GetRemoteWindowSize() const override {
- return peer_window_;
+ return !frames_.empty() || !serialized_prefix_.empty() ||
+ write_scheduler_.HasReadyStreams() || !connection_metadata_.empty();
}
+ int GetRemoteWindowSize() const override { return connection_send_window_; }
// From SpdyFramerVisitorInterface
void OnError(http2::Http2DecoderAdapter::SpdyFramerError error,
@@ -72,7 +122,7 @@ class OgHttp2Session : public Http2Session,
void OnPing(spdy::SpdyPingId unique_id, bool is_ack) override;
void OnGoAway(spdy::SpdyStreamId last_accepted_stream_id,
spdy::SpdyErrorCode error_code) override;
- bool OnGoAwayFrameData(const char* goaway_data, size_t len);
+ bool OnGoAwayFrameData(const char* goaway_data, size_t len) override;
void OnHeaders(spdy::SpdyStreamId stream_id,
bool has_priority,
int weight,
@@ -86,10 +136,9 @@ class OgHttp2Session : public Http2Session,
spdy::SpdyStreamId promised_stream_id,
bool end) override;
void OnContinuation(spdy::SpdyStreamId stream_id, bool end) override;
- void OnAltSvc(spdy::SpdyStreamId /*stream_id*/,
- absl::string_view /*origin*/,
+ void OnAltSvc(spdy::SpdyStreamId /*stream_id*/, absl::string_view /*origin*/,
const spdy::SpdyAltSvcWireFormat::
- AlternativeServiceVector& /*altsvc_vector*/);
+ AlternativeServiceVector& /*altsvc_vector*/) override;
void OnPriority(spdy::SpdyStreamId stream_id,
spdy::SpdyStreamId parent_stream_id,
int weight,
@@ -99,40 +148,136 @@ class OgHttp2Session : public Http2Session,
bool OnUnknownFrame(spdy::SpdyStreamId stream_id,
uint8_t frame_type) override;
+ // Invoked when header processing encounters an invalid or otherwise
+ // problematic header.
+ void OnHeaderStatus(Http2StreamId stream_id,
+ Http2VisitorInterface::OnHeaderResult result);
+
+ // Returns true if a recognized extension frame is received.
+ bool OnFrameHeader(spdy::SpdyStreamId stream_id, size_t length, uint8_t type,
+ uint8_t flags) override;
+
+ // Handles the payload for a recognized extension frame.
+ void OnFramePayload(const char* data, size_t len) override;
+
private:
- struct StreamState {
+ using MetadataSequence = std::vector<std::unique_ptr<MetadataSource>>;
+ struct QUICHE_EXPORT_PRIVATE StreamState {
+ StreamState(int32_t stream_receive_window,
+ WindowManager::WindowUpdateListener listener)
+ : window_manager(stream_receive_window, std::move(listener)) {}
+
WindowManager window_manager;
- int32_t send_window = 65535;
+ std::unique_ptr<DataFrameSource> outbound_body;
+ MetadataSequence outbound_metadata;
+ std::unique_ptr<spdy::SpdyHeaderBlock> trailers;
+ void* user_data = nullptr;
+ int32_t send_window = kInitialFlowControlWindowSize;
bool half_closed_local = false;
bool half_closed_remote = false;
};
+ using StreamStateMap = absl::flat_hash_map<Http2StreamId, StreamState>;
- class PassthroughHeadersHandler : public spdy::SpdyHeadersHandlerInterface {
+ class QUICHE_EXPORT_PRIVATE PassthroughHeadersHandler
+ : public spdy::SpdyHeadersHandlerInterface {
public:
- explicit PassthroughHeadersHandler(Http2VisitorInterface& visitor)
- : visitor_(visitor) {}
- void set_stream_id(Http2StreamId stream_id) { stream_id_ = stream_id; }
+ explicit PassthroughHeadersHandler(OgHttp2Session& session,
+ Http2VisitorInterface& visitor)
+ : session_(session), visitor_(visitor) {}
+ void set_stream_id(Http2StreamId stream_id) {
+ stream_id_ = stream_id;
+ result_ = Http2VisitorInterface::HEADER_OK;
+ }
void OnHeaderBlockStart() override;
void OnHeader(absl::string_view key, absl::string_view value) override;
void OnHeaderBlockEnd(size_t /* uncompressed_header_bytes */,
size_t /* compressed_header_bytes */) override;
private:
+ OgHttp2Session& session_;
Http2VisitorInterface& visitor_;
Http2StreamId stream_id_ = 0;
+ Http2VisitorInterface::OnHeaderResult result_ =
+ Http2VisitorInterface::HEADER_OK;
};
+ // Queues the connection preface, if not already done.
+ void MaybeSetupPreface();
+
+ void SendWindowUpdate(Http2StreamId stream_id, size_t update_delta);
+
+ // Sends queued frames, returning true if all frames were flushed
+ // successfully.
+ bool SendQueuedFrames();
+
+ // Returns false if the connection is write-blocked (due to flow control or
+ // some other reason).
+ bool WriteForStream(Http2StreamId stream_id);
+
+ bool SendMetadata(Http2StreamId stream_id, MetadataSequence& sequence);
+
+ void SendTrailers(Http2StreamId stream_id, spdy::SpdyHeaderBlock trailers);
+
+ // Encapsulates the RST_STREAM NO_ERROR behavior described in RFC 7540
+ // Section 8.1.
+ void MaybeCloseWithRstStream(Http2StreamId stream_id, StreamState& state);
+
+ // Performs flow control accounting for data sent by the peer.
+ void MarkDataBuffered(Http2StreamId stream_id, size_t bytes);
+
+ // Creates a stream and returns an iterator pointing to it.
+ StreamStateMap::iterator CreateStream(Http2StreamId stream_id);
+
+ // Receives events when inbound frames are parsed.
Http2VisitorInterface& visitor_;
+
+ // Encodes outbound frames.
spdy::SpdyFramer framer_{spdy::SpdyFramer::ENABLE_COMPRESSION};
+
+ // Decodes inbound frames.
http2::Http2DecoderAdapter decoder_;
- absl::flat_hash_map<Http2StreamId, StreamState> stream_map_;
+
+ // Maintains the state of all streams known to this session.
+ StreamStateMap stream_map_;
+
+ // Maintains the queue of outbound frames, and any serialized bytes that have
+ // not yet been consumed.
std::list<std::unique_ptr<spdy::SpdyFrameIR>> frames_;
- PassthroughHeadersHandler headers_handler_;
std::string serialized_prefix_;
+
+ // Maintains the set of streams ready to write data to the peer.
+ using WriteScheduler = PriorityWriteScheduler<Http2StreamId>;
+ WriteScheduler write_scheduler_;
+
+ // Delivers header name-value pairs to the visitor.
+ PassthroughHeadersHandler headers_handler_;
+
+ // Tracks the remaining client connection preface, in the case of a server
+ // session.
absl::string_view remaining_preface_;
- int peer_window_ = 65535;
+
+ WindowManager connection_window_manager_;
+
+ absl::flat_hash_set<Http2StreamId> streams_reset_;
+
+ MetadataSequence connection_metadata_;
+
+ Http2StreamId next_stream_id_ = 1;
+ Http2StreamId highest_received_stream_id_ = 0;
+ Http2StreamId metadata_stream_id_ = 0;
+ size_t metadata_length_ = 0;
+ int connection_send_window_ = kInitialFlowControlWindowSize;
+ // The initial flow control receive window size for any newly created streams.
+ int stream_receive_window_limit_ = kInitialFlowControlWindowSize;
+ int max_frame_payload_ = 16384;
Options options_;
bool received_goaway_ = false;
+ bool queued_preface_ = false;
+ bool peer_supports_metadata_ = false;
+ bool end_metadata_ = false;
+
+ // Replace this with a stream ID, for multiple GOAWAY support.
+ bool queued_goaway_ = false;
};
} // namespace adapter
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session_test.cc
index ab155697d5d..3f24ea200ef 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session_test.cc
@@ -2,6 +2,7 @@
#include "http2/adapter/mock_http2_visitor.h"
#include "http2/adapter/test_frame_sequence.h"
+#include "http2/adapter/test_utils.h"
#include "common/platform/api/quiche_test.h"
namespace http2 {
@@ -9,6 +10,7 @@ namespace adapter {
namespace test {
namespace {
+using spdy::SpdyFrameType;
using testing::_;
enum FrameType {
@@ -31,7 +33,9 @@ TEST(OgHttp2SessionTest, ClientConstruction) {
visitor, OgHttp2Session::Options{.perspective = Perspective::kClient});
EXPECT_TRUE(session.want_read());
EXPECT_FALSE(session.want_write());
- EXPECT_EQ(session.GetRemoteWindowSize(), kDefaultInitialStreamWindowSize);
+ EXPECT_EQ(session.GetRemoteWindowSize(), kInitialFlowControlWindowSize);
+ EXPECT_FALSE(session.IsServerSession());
+ EXPECT_EQ(0, session.GetHighestReceivedStreamId());
}
TEST(OgHttp2SessionTest, ClientHandlesFrames) {
@@ -56,43 +60,433 @@ TEST(OgHttp2SessionTest, ClientHandlesFrames) {
EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0));
EXPECT_CALL(visitor, OnWindowUpdate(0, 1000));
- const ssize_t initial_result = session.ProcessBytes(initial_frames);
+ const size_t initial_result = session.ProcessBytes(initial_frames);
EXPECT_EQ(initial_frames.size(), initial_result);
EXPECT_EQ(session.GetRemoteWindowSize(),
- kDefaultInitialStreamWindowSize + 1000);
+ kInitialFlowControlWindowSize + 1000);
+ EXPECT_EQ(0, session.GetHighestReceivedStreamId());
+
+ // Connection has not yet received any data.
+ EXPECT_EQ(kInitialFlowControlWindowSize, session.GetReceiveWindowSize());
+
+ EXPECT_EQ(0, session.GetHpackDecoderDynamicTableSize());
// Should OgHttp2Session require that streams 1 and 3 have been created?
+ // Submit a request to ensure the first stream is created.
+ const char* kSentinel1 = "arbitrary pointer 1";
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true);
+ body1->AppendPayload("This is an example request body.");
+ body1->EndData();
+ int stream_id =
+ session.SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ std::move(body1), const_cast<char*>(kSentinel1));
+ EXPECT_EQ(stream_id, 1);
+
const std::string stream_frames =
TestFrameSequence()
- .Headers(1,
+ .Headers(stream_id,
{{":status", "200"},
{"server", "my-fake-server"},
{"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
/*fin=*/false)
- .Data(1, "This is the response body.")
+ .Data(stream_id, "This is the response body.")
.RstStream(3, Http2ErrorCode::INTERNAL_ERROR)
.GoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "calm down!!")
.Serialize();
- EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
- EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
- EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
- EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
+ EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id));
+ EXPECT_CALL(visitor, OnHeaderForStream(stream_id, ":status", "200"));
EXPECT_CALL(visitor,
- OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
- EXPECT_CALL(visitor, OnEndHeadersForStream(1));
- EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 0));
- EXPECT_CALL(visitor, OnBeginDataForStream(1, 26));
- EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
+ OnHeaderForStream(stream_id, "server", "my-fake-server"));
+ EXPECT_CALL(visitor, OnHeaderForStream(stream_id, "date",
+ "Tue, 6 Apr 2021 12:54:01 GMT"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id));
+ EXPECT_CALL(visitor, OnFrameHeader(stream_id, 26, DATA, 0));
+ EXPECT_CALL(visitor, OnBeginDataForStream(stream_id, 26));
+ EXPECT_CALL(visitor,
+ OnDataForStream(stream_id, "This is the response body."));
EXPECT_CALL(visitor, OnFrameHeader(3, 4, RST_STREAM, 0));
EXPECT_CALL(visitor, OnRstStream(3, Http2ErrorCode::INTERNAL_ERROR));
EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR));
EXPECT_CALL(visitor, OnFrameHeader(0, 19, GOAWAY, 0));
EXPECT_CALL(visitor, OnGoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, ""));
- const ssize_t stream_result = session.ProcessBytes(stream_frames);
+ const size_t stream_result = session.ProcessBytes(stream_frames);
EXPECT_EQ(stream_frames.size(), stream_result);
+ EXPECT_EQ(3, session.GetHighestReceivedStreamId());
+
+ // The first stream is active and has received some data.
+ EXPECT_GT(kInitialFlowControlWindowSize,
+ session.GetStreamReceiveWindowSize(stream_id));
+ // Connection receive window is equivalent to the first stream's.
+ EXPECT_EQ(session.GetReceiveWindowSize(),
+ session.GetStreamReceiveWindowSize(stream_id));
+ // Receive window upper bound is still the initial value.
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ session.GetStreamReceiveWindowLimit(stream_id));
+
+ EXPECT_GT(session.GetHpackDecoderDynamicTableSize(), 0);
+}
+
+// Verifies that a client session enqueues initial SETTINGS if Send() is called
+// before any frames are explicitly queued.
+TEST(OgHttp2SessionTest, ClientEnqueuesSettingsOnSend) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kClient});
+ EXPECT_FALSE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(serialized, EqualsFrames({SpdyFrameType::SETTINGS}));
+}
+
+// Verifies that a client session enqueues initial SETTINGS before whatever
+// frame type is passed to the first invocation of EnqueueFrame().
+TEST(OgHttp2SessionTest, ClientEnqueuesSettingsBeforeOtherFrame) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kClient});
+ EXPECT_FALSE(session.want_write());
+ session.EnqueueFrame(absl::make_unique<spdy::SpdyPingIR>(42));
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, 8, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(PING, 0, 8, 0x0, 0));
+
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(serialized,
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::PING}));
+}
+
+// Verifies that if the first call to EnqueueFrame() passes a SETTINGS frame,
+// the client session will not enqueue an additional SETTINGS frame.
+TEST(OgHttp2SessionTest, ClientEnqueuesSettingsOnce) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kClient});
+ EXPECT_FALSE(session.want_write());
+ session.EnqueueFrame(absl::make_unique<spdy::SpdySettingsIR>());
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(serialized, EqualsFrames({SpdyFrameType::SETTINGS}));
+}
+
+TEST(OgHttp2SessionTest, ClientSubmitRequest) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kClient});
+
+ EXPECT_FALSE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+
+ // Even though the user has not queued any frames for the session, it should
+ // still send the connection preface.
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ // Initial SETTINGS.
+ EXPECT_THAT(serialized, EqualsFrames({SpdyFrameType::SETTINGS}));
+ visitor.Clear();
+
+ const std::string initial_frames =
+ TestFrameSequence().ServerPreface().Serialize();
+ testing::InSequence s;
+
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ const size_t initial_result = session.ProcessBytes(initial_frames);
+ EXPECT_EQ(initial_frames.size(), initial_result);
+
+ // Session will want to write a SETTINGS ack.
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ result = session.Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS}));
+ visitor.Clear();
+
+ EXPECT_EQ(0, session.GetHpackEncoderDynamicTableSize());
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true);
+ body1->AppendPayload("This is an example request body.");
+ body1->EndData();
+ int stream_id =
+ session.SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ std::move(body1), const_cast<char*>(kSentinel1));
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(session.want_write());
+ EXPECT_EQ(kSentinel1, session.GetStreamUserData(stream_id));
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0));
+
+ result = session.Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS,
+ spdy::SpdyFrameType::DATA}));
+ visitor.Clear();
+ EXPECT_FALSE(session.want_write());
+
+ // Some data was sent, so the remaining send window size should be less than
+ // the default.
+ EXPECT_LT(session.GetStreamSendWindowSize(stream_id),
+ kInitialFlowControlWindowSize);
+ EXPECT_GT(session.GetStreamSendWindowSize(stream_id), 0);
+ // Send window for a nonexistent stream is not available.
+ EXPECT_EQ(-1, session.GetStreamSendWindowSize(stream_id + 2));
+
+ EXPECT_GT(session.GetHpackEncoderDynamicTableSize(), 0);
+
+ stream_id =
+ session.SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/two"}}),
+ nullptr, nullptr);
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(session.want_write());
+ const char* kSentinel2 = "arbitrary pointer 2";
+ EXPECT_EQ(nullptr, session.GetStreamUserData(stream_id));
+ session.SetStreamUserData(stream_id, const_cast<char*>(kSentinel2));
+ EXPECT_EQ(kSentinel2, session.GetStreamUserData(stream_id));
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x5, 0));
+
+ result = session.Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+
+ // No data was sent (just HEADERS), so the remaining send window size should
+ // still be the default.
+ EXPECT_EQ(session.GetStreamSendWindowSize(stream_id),
+ kInitialFlowControlWindowSize);
+}
+
+// This test exercises the case where the client request body source is read
+// blocked.
+TEST(OgHttp2SessionTest, ClientSubmitRequestWithReadBlock) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kClient});
+ EXPECT_FALSE(session.want_write());
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true);
+ TestDataFrameSource* body_ref = body1.get();
+ int stream_id =
+ session.SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ std::move(body1), const_cast<char*>(kSentinel1));
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(session.want_write());
+ EXPECT_EQ(kSentinel1, session.GetStreamUserData(stream_id));
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0));
+
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(serialized,
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS}));
+ // No data frame, as body1 was read blocked.
+ visitor.Clear();
+ EXPECT_FALSE(session.want_write());
+
+ body_ref->AppendPayload("This is an example request body.");
+ body_ref->EndData();
+ EXPECT_TRUE(session.ResumeStream(stream_id));
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0));
+
+ result = session.Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::DATA}));
+ EXPECT_FALSE(session.want_write());
+
+ // Stream data is done, so this stream cannot be resumed.
+ EXPECT_FALSE(session.ResumeStream(stream_id));
+ EXPECT_FALSE(session.want_write());
+}
+
+// This test exercises the case where the client request body source is read
+// blocked, then ends with an empty DATA frame.
+TEST(OgHttp2SessionTest, ClientSubmitRequestEmptyDataWithFin) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kClient});
+ EXPECT_FALSE(session.want_write());
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true);
+ TestDataFrameSource* body_ref = body1.get();
+ int stream_id =
+ session.SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ std::move(body1), const_cast<char*>(kSentinel1));
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(session.want_write());
+ EXPECT_EQ(kSentinel1, session.GetStreamUserData(stream_id));
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0));
+
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(serialized,
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS}));
+ // No data frame, as body1 was read blocked.
+ visitor.Clear();
+ EXPECT_FALSE(session.want_write());
+
+ body_ref->EndData();
+ EXPECT_TRUE(session.ResumeStream(stream_id));
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, 0, 0x1, 0));
+
+ result = session.Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::DATA}));
+ EXPECT_FALSE(session.want_write());
+
+ // Stream data is done, so this stream cannot be resumed.
+ EXPECT_FALSE(session.ResumeStream(stream_id));
+ EXPECT_FALSE(session.want_write());
+}
+
+// This test exercises the case where the connection to the peer is write
+// blocked.
+TEST(OgHttp2SessionTest, ClientSubmitRequestWithWriteBlock) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kClient});
+ EXPECT_FALSE(session.want_write());
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true);
+ body1->AppendPayload("This is an example request body.");
+ body1->EndData();
+ int stream_id =
+ session.SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ std::move(body1), const_cast<char*>(kSentinel1));
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(session.want_write());
+ EXPECT_EQ(kSentinel1, session.GetStreamUserData(stream_id));
+ visitor.set_is_write_blocked(true);
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+
+ EXPECT_THAT(visitor.data(), testing::IsEmpty());
+ EXPECT_TRUE(session.want_write());
+ visitor.set_is_write_blocked(false);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0));
+
+ result = session.Send();
+ EXPECT_EQ(0, result);
+
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(serialized,
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS,
+ SpdyFrameType::DATA}));
+ EXPECT_FALSE(session.want_write());
+}
+
+TEST(OgHttp2SessionTest, ClientStartShutdown) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kClient});
+
+ EXPECT_FALSE(session.want_write());
+
+ // No-op (except for logging) for a client implementation.
+ session.StartGracefulShutdown();
+ EXPECT_FALSE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+
+ absl::string_view serialized = visitor.data();
+ EXPECT_THAT(serialized,
+ testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
+ serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
+ EXPECT_THAT(serialized, EqualsFrames({SpdyFrameType::SETTINGS}));
}
TEST(OgHttp2SessionTest, ServerConstruction) {
@@ -101,14 +495,18 @@ TEST(OgHttp2SessionTest, ServerConstruction) {
visitor, OgHttp2Session::Options{.perspective = Perspective::kServer});
EXPECT_TRUE(session.want_read());
EXPECT_FALSE(session.want_write());
- EXPECT_EQ(session.GetRemoteWindowSize(), kDefaultInitialStreamWindowSize);
+ EXPECT_EQ(session.GetRemoteWindowSize(), kInitialFlowControlWindowSize);
+ EXPECT_TRUE(session.IsServerSession());
+ EXPECT_EQ(0, session.GetHighestReceivedStreamId());
}
TEST(OgHttp2SessionTest, ServerHandlesFrames) {
- testing::StrictMock<MockHttp2Visitor> visitor;
+ DataSavingVisitor visitor;
OgHttp2Session session(
visitor, OgHttp2Session::Options{.perspective = Perspective::kServer});
+ EXPECT_EQ(0, session.GetHpackDecoderDynamicTableSize());
+
const std::string frames = TestFrameSequence()
.ClientPreface()
.Ping(42)
@@ -132,6 +530,8 @@ TEST(OgHttp2SessionTest, ServerHandlesFrames) {
.Serialize();
testing::InSequence s;
+ const char* kSentinel1 = "arbitrary pointer 1";
+
// Client preface (empty SETTINGS)
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
@@ -147,7 +547,10 @@ TEST(OgHttp2SessionTest, ServerHandlesFrames) {
EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
- EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1))
+ .WillOnce(testing::InvokeWithoutArgs([&session, kSentinel1]() {
+ session.SetStreamUserData(1, const_cast<char*>(kSentinel1));
+ }));
EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
EXPECT_CALL(visitor, OnWindowUpdate(1, 2000));
EXPECT_CALL(visitor, OnFrameHeader(1, 25, DATA, 0));
@@ -167,11 +570,410 @@ TEST(OgHttp2SessionTest, ServerHandlesFrames) {
EXPECT_CALL(visitor, OnFrameHeader(0, 8, PING, 0));
EXPECT_CALL(visitor, OnPing(47, false));
- const ssize_t result = session.ProcessBytes(frames);
+ const size_t result = session.ProcessBytes(frames);
EXPECT_EQ(frames.size(), result);
+ EXPECT_EQ(kSentinel1, session.GetStreamUserData(1));
+
+ // The first stream is active and has received some data.
+ EXPECT_GT(kInitialFlowControlWindowSize,
+ session.GetStreamReceiveWindowSize(1));
+ // Connection receive window is equivalent to the first stream's.
+ EXPECT_EQ(session.GetReceiveWindowSize(),
+ session.GetStreamReceiveWindowSize(1));
+ // Receive window upper bound is still the initial value.
+ EXPECT_EQ(kInitialFlowControlWindowSize,
+ session.GetStreamReceiveWindowLimit(1));
+
+ EXPECT_GT(session.GetHpackDecoderDynamicTableSize(), 0);
+
+ // TODO(birenroy): drop stream state when streams are closed. It should no
+ // longer be possible to set user data.
+ const char* kSentinel3 = "another arbitrary pointer";
+ session.SetStreamUserData(3, const_cast<char*>(kSentinel3));
+ EXPECT_EQ(kSentinel3, session.GetStreamUserData(3));
+
EXPECT_EQ(session.GetRemoteWindowSize(),
- kDefaultInitialStreamWindowSize + 1000);
+ kInitialFlowControlWindowSize + 1000);
+ EXPECT_EQ(3, session.GetHighestReceivedStreamId());
+
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ // Some bytes should have been serialized.
+ int send_result = session.Send();
+ EXPECT_EQ(0, send_result);
+ // Initial SETTINGS, SETTINGS ack.
+ // TODO(birenroy): automatically queue PING acks.
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+ spdy::SpdyFrameType::SETTINGS}));
+}
+
+// Verifies that a server session enqueues initial SETTINGS before whatever
+// frame type is passed to the first invocation of EnqueueFrame().
+TEST(OgHttp2SessionTest, ServerEnqueuesSettingsBeforeOtherFrame) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kServer});
+ EXPECT_FALSE(session.want_write());
+ session.EnqueueFrame(absl::make_unique<spdy::SpdyPingIR>(42));
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(PING, 0, _, 0x0, 0));
+
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::PING}));
+}
+
+// Verifies that if the first call to EnqueueFrame() passes a SETTINGS frame,
+// the server session will not enqueue an additional SETTINGS frame.
+TEST(OgHttp2SessionTest, ServerEnqueuesSettingsOnce) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kServer});
+ EXPECT_FALSE(session.want_write());
+ session.EnqueueFrame(absl::make_unique<spdy::SpdySettingsIR>());
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS}));
+}
+
+TEST(OgHttp2SessionTest, ServerSubmitResponse) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kServer});
+
+ EXPECT_FALSE(session.want_write());
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/true)
+ .Serialize();
+ testing::InSequence s;
+
+ const char* kSentinel1 = "arbitrary pointer 1";
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1))
+ .WillOnce(testing::InvokeWithoutArgs([&session, kSentinel1]() {
+ session.SetStreamUserData(1, const_cast<char*>(kSentinel1));
+ }));
+ EXPECT_CALL(visitor, OnEndStream(1));
+
+ const size_t result = session.ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+
+ EXPECT_EQ(1, session.GetHighestReceivedStreamId());
+
+ EXPECT_EQ(0, session.GetHpackEncoderDynamicTableSize());
+
+ // Server will want to send initial SETTINGS, and a SETTINGS ack.
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ int send_result = session.Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS}));
+ visitor.Clear();
+
+ EXPECT_FALSE(session.want_write());
+ // A data fin is not sent so that the stream remains open, and the flow
+ // control state can be verified.
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false);
+ body1->AppendPayload("This is an example response body.");
+ int submit_result = session.SubmitResponse(
+ 1,
+ ToHeaders({{":status", "404"},
+ {"x-comment", "I have no idea what you're talking about."}}),
+ std::move(body1));
+ EXPECT_EQ(submit_result, 0);
+ EXPECT_TRUE(session.want_write());
+
+ // Stream user data should have been set successfully after receiving headers.
+ EXPECT_EQ(kSentinel1, session.GetStreamUserData(1));
+ session.SetStreamUserData(1, nullptr);
+ EXPECT_EQ(nullptr, session.GetStreamUserData(1));
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0));
+
+ send_result = session.Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames({SpdyFrameType::HEADERS, SpdyFrameType::DATA}));
+ EXPECT_FALSE(session.want_write());
+
+ // Some data was sent, so the remaining send window size should be less than
+ // the default.
+ EXPECT_LT(session.GetStreamSendWindowSize(1), kInitialFlowControlWindowSize);
+ EXPECT_GT(session.GetStreamSendWindowSize(1), 0);
+ // Send window for a nonexistent stream is not available.
+ EXPECT_EQ(session.GetStreamSendWindowSize(3), -1);
+
+ EXPECT_GT(session.GetHpackEncoderDynamicTableSize(), 0);
+}
+
+TEST(OgHttp2SessionTest, ServerStartShutdown) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kServer});
+
+ EXPECT_FALSE(session.want_write());
+
+ session.StartGracefulShutdown();
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, 0));
+
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY}));
+}
+
+TEST(OgHttp2SessionTest, ServerStartShutdownAfterGoaway) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kServer});
+
+ EXPECT_FALSE(session.want_write());
+
+ auto goaway = absl::make_unique<spdy::SpdyGoAwayIR>(
+ 1, spdy::ERROR_CODE_NO_ERROR, "and don't come back!");
+ session.EnqueueFrame(std::move(goaway));
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, 0));
+
+ int result = session.Send();
+ EXPECT_EQ(0, result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY}));
+
+ // No-op, since a GOAWAY has previously been enqueued.
+ session.StartGracefulShutdown();
+ EXPECT_FALSE(session.want_write());
+}
+
+// Tests the case where the server queues trailers after the data stream is
+// exhausted.
+TEST(OgHttp2SessionTest, ServerSendsTrailers) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kServer});
+
+ EXPECT_FALSE(session.want_write());
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndStream(1));
+
+ const size_t result = session.ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+
+ // Server will want to send initial SETTINGS, and a SETTINGS ack.
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ int send_result = session.Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS}));
+ visitor.Clear();
+
+ EXPECT_FALSE(session.want_write());
+
+ // The body source must indicate that the end of the body is not the end of
+ // the stream.
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false);
+ body1->AppendPayload("This is an example response body.");
+ body1->EndData();
+ int submit_result = session.SubmitResponse(
+ 1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}),
+ std::move(body1));
+ EXPECT_EQ(submit_result, 0);
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0));
+
+ send_result = session.Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames({SpdyFrameType::HEADERS, SpdyFrameType::DATA}));
+ visitor.Clear();
+ EXPECT_FALSE(session.want_write());
+
+ // The body source has been exhausted by the call to Send() above.
+ // TODO(birenroy): Fix this strange ordering.
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+ int trailer_result = session.SubmitTrailer(
+ 1, ToHeaders({{"final-status", "a-ok"},
+ {"x-comment", "trailers sure are cool"}}));
+ ASSERT_EQ(trailer_result, 0);
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0));
+
+ send_result = session.Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::HEADERS}));
+}
+
+// Tests the case where the server queues trailers immediately after headers and
+// data, and before any writes have taken place.
+TEST(OgHttp2SessionTest, ServerQueuesTrailersWithResponse) {
+ DataSavingVisitor visitor;
+ OgHttp2Session session(
+ visitor, OgHttp2Session::Options{.perspective = Perspective::kServer});
+
+ EXPECT_FALSE(session.want_write());
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "GET"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}},
+ /*fin=*/true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndStream(1));
+
+ const size_t result = session.ProcessBytes(frames);
+ EXPECT_EQ(frames.size(), result);
+
+ // Server will want to send initial SETTINGS, and a SETTINGS ack.
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ int send_result = session.Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS}));
+ visitor.Clear();
+
+ EXPECT_FALSE(session.want_write());
+
+ // The body source must indicate that the end of the body is not the end of
+ // the stream.
+ auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false);
+ body1->AppendPayload("This is an example response body.");
+ body1->EndData();
+ int submit_result = session.SubmitResponse(
+ 1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}),
+ std::move(body1));
+ EXPECT_EQ(submit_result, 0);
+ EXPECT_TRUE(session.want_write());
+ // There has not been a call to Send() yet, so neither headers nor body have
+ // been written.
+ int trailer_result = session.SubmitTrailer(
+ 1, ToHeaders({{"final-status", "a-ok"},
+ {"x-comment", "trailers sure are cool"}}));
+ ASSERT_EQ(trailer_result, 0);
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
+ EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0));
+
+ // TODO(birenroy): Fix this strange ordering.
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0));
+
+ send_result = session.Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames({SpdyFrameType::HEADERS, SpdyFrameType::DATA,
+ SpdyFrameType::HEADERS}));
}
} // namespace test
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_util.h b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_util.h
index e222c96ccfd..3134ff7b004 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_util.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_util.h
@@ -3,12 +3,14 @@
#include "absl/types/span.h"
#include "http2/adapter/http2_protocol.h"
+#include "common/platform/api/quiche_export.h"
#include "spdy/core/spdy_header_block.h"
namespace http2 {
namespace adapter {
-spdy::SpdyHeaderBlock ToHeaderBlock(absl::Span<const Header> headers);
+QUICHE_EXPORT_PRIVATE spdy::SpdyHeaderBlock ToHeaderBlock(
+ absl::Span<const Header> headers);
} // namespace adapter
} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.cc b/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.cc
index 78ec423e25e..75f3749e62c 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.cc
@@ -7,6 +7,11 @@ namespace http2 {
namespace adapter {
namespace test {
+ssize_t RecordingHttp2Visitor::OnReadyToSend(absl::string_view serialized) {
+ events_.push_back(absl::StrFormat("OnReadyToSend %d", serialized.size()));
+ return serialized.size();
+}
+
void RecordingHttp2Visitor::OnConnectionError() {
events_.push_back("OnConnectionError");
}
@@ -36,15 +41,16 @@ void RecordingHttp2Visitor::OnSettingsAck() {
events_.push_back("OnSettingsAck");
}
-void RecordingHttp2Visitor::OnBeginHeadersForStream(Http2StreamId stream_id) {
+bool RecordingHttp2Visitor::OnBeginHeadersForStream(Http2StreamId stream_id) {
events_.push_back(absl::StrFormat("OnBeginHeadersForStream %d", stream_id));
+ return true;
}
-void RecordingHttp2Visitor::OnHeaderForStream(Http2StreamId stream_id,
- absl::string_view name,
- absl::string_view value) {
+Http2VisitorInterface::OnHeaderResult RecordingHttp2Visitor::OnHeaderForStream(
+ Http2StreamId stream_id, absl::string_view name, absl::string_view value) {
events_.push_back(
absl::StrFormat("OnHeaderForStream %d %s %s", stream_id, name, value));
+ return HEADER_OK;
}
void RecordingHttp2Visitor::OnEndHeadersForStream(Http2StreamId stream_id) {
@@ -112,11 +118,32 @@ void RecordingHttp2Visitor::OnWindowUpdate(Http2StreamId stream_id,
absl::StrFormat("OnWindowUpdate %d %d", stream_id, window_increment));
}
-void RecordingHttp2Visitor::OnReadyToSendDataForStream(Http2StreamId stream_id,
- char* destination_buffer,
- size_t length,
- ssize_t* written,
- bool* end_stream) {
+int RecordingHttp2Visitor::OnBeforeFrameSent(uint8_t frame_type,
+ Http2StreamId stream_id,
+ size_t length, uint8_t flags) {
+ events_.push_back(absl::StrFormat("OnBeforeFrameSent %d %d %d %d", frame_type,
+ stream_id, length, flags));
+ return 0;
+}
+
+int RecordingHttp2Visitor::OnFrameSent(uint8_t frame_type,
+ Http2StreamId stream_id, size_t length,
+ uint8_t flags, uint32_t error_code) {
+ events_.push_back(absl::StrFormat("OnFrameSent %d %d %d %d %d", frame_type,
+ stream_id, length, flags, error_code));
+ return 0;
+}
+
+bool RecordingHttp2Visitor::OnInvalidFrame(Http2StreamId stream_id,
+ int error_code) {
+ events_.push_back(
+ absl::StrFormat("OnInvalidFrame %d %d", stream_id, error_code));
+ return true;
+}
+
+void RecordingHttp2Visitor::OnReadyToSendDataForStream(
+ Http2StreamId stream_id, char* /*destination_buffer*/, size_t length,
+ ssize_t* /*written*/, bool* /*end_stream*/) {
// TODO(b/181586191): Revisit this. The visitor is expected to write to the
// |destination_buffer| and set the other pointer values appropriately.
events_.push_back(
@@ -124,10 +151,8 @@ void RecordingHttp2Visitor::OnReadyToSendDataForStream(Http2StreamId stream_id,
}
void RecordingHttp2Visitor::OnReadyToSendMetadataForStream(
- Http2StreamId stream_id,
- char* buffer,
- size_t length,
- ssize_t* written) {
+ Http2StreamId stream_id, char* /*buffer*/, size_t length,
+ ssize_t* /*written*/) {
// TODO(b/181586191): Revisit this. The visitor is expected to write to the
// |buffer| and set *written appropriately.
events_.push_back(absl::StrFormat("OnReadyToSendMetadataForStream %d %d",
@@ -146,8 +171,13 @@ void RecordingHttp2Visitor::OnMetadataForStream(Http2StreamId stream_id,
absl::StrFormat("OnMetadataForStream %d %s", stream_id, metadata));
}
-void RecordingHttp2Visitor::OnMetadataEndForStream(Http2StreamId stream_id) {
+bool RecordingHttp2Visitor::OnMetadataEndForStream(Http2StreamId stream_id) {
events_.push_back(absl::StrFormat("OnMetadataEndForStream %d", stream_id));
+ return true;
+}
+
+void RecordingHttp2Visitor::OnErrorDebug(absl::string_view message) {
+ events_.push_back(absl::StrFormat("OnErrorDebug %s", message));
}
} // namespace test
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.h b/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.h
index 452b45185cb..25b92935b35 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.h
@@ -5,6 +5,7 @@
#include <string>
#include "http2/adapter/http2_visitor_interface.h"
+#include "common/platform/api/quiche_export.h"
#include "common/platform/api/quiche_test.h"
namespace http2 {
@@ -12,12 +13,13 @@ namespace adapter {
namespace test {
// A visitor implementation that records the sequence of callbacks it receives.
-class RecordingHttp2Visitor : public Http2VisitorInterface {
+class QUICHE_NO_EXPORT RecordingHttp2Visitor : public Http2VisitorInterface {
public:
using Event = std::string;
using EventSequence = std::list<Event>;
// From Http2VisitorInterface
+ ssize_t OnReadyToSend(absl::string_view serialized) override;
void OnConnectionError() override;
void OnFrameHeader(Http2StreamId stream_id,
size_t length,
@@ -27,10 +29,10 @@ class RecordingHttp2Visitor : public Http2VisitorInterface {
void OnSetting(Http2Setting setting) override;
void OnSettingsEnd() override;
void OnSettingsAck() override;
- void OnBeginHeadersForStream(Http2StreamId stream_id) override;
- void OnHeaderForStream(Http2StreamId stream_id,
- absl::string_view name,
- absl::string_view value) override;
+ bool OnBeginHeadersForStream(Http2StreamId stream_id) override;
+ OnHeaderResult OnHeaderForStream(Http2StreamId stream_id,
+ absl::string_view name,
+ absl::string_view value) override;
void OnEndHeadersForStream(Http2StreamId stream_id) override;
void OnBeginDataForStream(Http2StreamId stream_id,
size_t payload_length) override;
@@ -51,6 +53,11 @@ class RecordingHttp2Visitor : public Http2VisitorInterface {
Http2ErrorCode error_code,
absl::string_view opaque_data) override;
void OnWindowUpdate(Http2StreamId stream_id, int window_increment) override;
+ int OnBeforeFrameSent(uint8_t frame_type, Http2StreamId stream_id,
+ size_t length, uint8_t flags) override;
+ int OnFrameSent(uint8_t frame_type, Http2StreamId stream_id, size_t length,
+ uint8_t flags, uint32_t error_code) override;
+ bool OnInvalidFrame(Http2StreamId stream_id, int error_code) override;
void OnReadyToSendDataForStream(Http2StreamId stream_id,
char* destination_buffer,
size_t length,
@@ -64,7 +71,8 @@ class RecordingHttp2Visitor : public Http2VisitorInterface {
size_t payload_length) override;
void OnMetadataForStream(Http2StreamId stream_id,
absl::string_view metadata) override;
- void OnMetadataEndForStream(Http2StreamId stream_id) override;
+ bool OnMetadataEndForStream(Http2StreamId stream_id) override;
+ void OnErrorDebug(absl::string_view message) override;
const EventSequence& GetEventSequence() const { return events_; }
void Clear() { events_.clear(); }
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.cc b/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.cc
index 7be368ea6a5..bb1517d3d73 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.cc
@@ -2,6 +2,7 @@
#include "http2/adapter/http2_util.h"
#include "http2/adapter/oghttp2_util.h"
+#include "spdy/core/hpack/hpack_encoder.h"
#include "spdy/core/spdy_framer.h"
namespace http2 {
@@ -11,8 +12,9 @@ namespace test {
std::vector<const Header> ToHeaders(
absl::Span<const std::pair<absl::string_view, absl::string_view>> headers) {
std::vector<const Header> out;
- for (auto [name, value] : headers) {
- out.push_back(std::make_pair(HeaderRep(name), HeaderRep(value)));
+ for (const auto& header : headers) {
+ out.push_back(
+ std::make_pair(HeaderRep(header.first), HeaderRep(header.second)));
}
return out;
}
@@ -88,24 +90,45 @@ TestFrameSequence& TestFrameSequence::GoAway(Http2StreamId last_good_stream_id,
TestFrameSequence& TestFrameSequence::Headers(
Http2StreamId stream_id,
absl::Span<const std::pair<absl::string_view, absl::string_view>> headers,
- bool fin) {
- return Headers(stream_id, ToHeaders(headers), fin);
+ bool fin, bool add_continuation) {
+ return Headers(stream_id, ToHeaders(headers), fin, add_continuation);
}
TestFrameSequence& TestFrameSequence::Headers(Http2StreamId stream_id,
spdy::Http2HeaderBlock block,
- bool fin) {
- auto headers =
- absl::make_unique<spdy::SpdyHeadersIR>(stream_id, std::move(block));
- headers->set_fin(fin);
- frames_.push_back(std::move(headers));
+ bool fin, bool add_continuation) {
+ if (add_continuation) {
+ // The normal intermediate representations don't allow you to represent a
+ // nonterminal HEADERS frame explicitly, so we'll need to use
+ // SpdyUnknownIRs. For simplicity, and in order not to mess up HPACK state,
+ // the payload will be uncompressed.
+ std::string encoded_block;
+ spdy::HpackEncoder encoder;
+ encoder.DisableCompression();
+ encoder.EncodeHeaderSet(block, &encoded_block);
+ const size_t pos = encoded_block.size() / 2;
+ const uint8_t flags = fin ? 0x1 : 0x0;
+ frames_.push_back(absl::make_unique<spdy::SpdyUnknownIR>(
+ stream_id, static_cast<uint8_t>(spdy::SpdyFrameType::HEADERS), flags,
+ encoded_block.substr(0, pos)));
+
+ auto continuation = absl::make_unique<spdy::SpdyContinuationIR>(stream_id);
+ continuation->set_end_headers(true);
+ continuation->take_encoding(encoded_block.substr(pos));
+ frames_.push_back(std::move(continuation));
+ } else {
+ auto headers =
+ absl::make_unique<spdy::SpdyHeadersIR>(stream_id, std::move(block));
+ headers->set_fin(fin);
+ frames_.push_back(std::move(headers));
+ }
return *this;
}
TestFrameSequence& TestFrameSequence::Headers(Http2StreamId stream_id,
absl::Span<const Header> headers,
- bool fin) {
- return Headers(stream_id, ToHeaderBlock(headers), fin);
+ bool fin, bool add_continuation) {
+ return Headers(stream_id, ToHeaderBlock(headers), fin, add_continuation);
}
TestFrameSequence& TestFrameSequence::WindowUpdate(Http2StreamId stream_id,
@@ -124,6 +147,25 @@ TestFrameSequence& TestFrameSequence::Priority(Http2StreamId stream_id,
return *this;
}
+TestFrameSequence& TestFrameSequence::Metadata(Http2StreamId stream_id,
+ absl::string_view payload,
+ bool multiple_frames) {
+ const std::string encoded_payload = MetadataBlockForPayload(payload);
+ if (multiple_frames) {
+ const size_t pos = encoded_payload.size() / 2;
+ frames_.push_back(absl::make_unique<spdy::SpdyUnknownIR>(
+ stream_id, kMetadataFrameType, 0, encoded_payload.substr(0, pos)));
+ frames_.push_back(absl::make_unique<spdy::SpdyUnknownIR>(
+ stream_id, kMetadataFrameType, kMetadataEndFlag,
+ encoded_payload.substr(pos)));
+ } else {
+ frames_.push_back(absl::make_unique<spdy::SpdyUnknownIR>(
+ stream_id, kMetadataFrameType, kMetadataEndFlag,
+ std::move(encoded_payload)));
+ }
+ return *this;
+}
+
std::string TestFrameSequence::Serialize() {
std::string result;
if (!preface_.empty()) {
@@ -137,6 +179,18 @@ std::string TestFrameSequence::Serialize() {
return result;
}
+std::string TestFrameSequence::MetadataBlockForPayload(
+ absl::string_view payload) {
+ // Encode the payload using a header block.
+ spdy::SpdyHeaderBlock block;
+ block["example-payload"] = payload;
+ spdy::HpackEncoder encoder;
+ encoder.DisableCompression();
+ std::string encoded_payload;
+ encoder.EncodeHeaderSet(block, &encoded_payload);
+ return encoded_payload;
+}
+
} // namespace test
} // namespace adapter
} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.h b/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.h
index cc5e8b5ff57..99740d38f9e 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.h
@@ -6,16 +6,17 @@
#include <vector>
#include "http2/adapter/http2_protocol.h"
+#include "common/platform/api/quiche_export.h"
#include "spdy/core/spdy_protocol.h"
namespace http2 {
namespace adapter {
namespace test {
-std::vector<const Header> ToHeaders(
+std::vector<const Header> QUICHE_NO_EXPORT ToHeaders(
absl::Span<const std::pair<absl::string_view, absl::string_view>> headers);
-class TestFrameSequence {
+class QUICHE_NO_EXPORT TestFrameSequence {
public:
TestFrameSequence() = default;
@@ -36,21 +37,26 @@ class TestFrameSequence {
TestFrameSequence& Headers(
Http2StreamId stream_id,
absl::Span<const std::pair<absl::string_view, absl::string_view>> headers,
- bool fin = false);
+ bool fin = false, bool add_continuation = false);
TestFrameSequence& Headers(Http2StreamId stream_id,
- spdy::Http2HeaderBlock block,
- bool fin = false);
+ spdy::Http2HeaderBlock block, bool fin = false,
+ bool add_continuation = false);
TestFrameSequence& Headers(Http2StreamId stream_id,
- absl::Span<const Header> headers,
- bool fin = false);
+ absl::Span<const Header> headers, bool fin = false,
+ bool add_continuation = false);
TestFrameSequence& WindowUpdate(Http2StreamId stream_id, int32_t delta);
TestFrameSequence& Priority(Http2StreamId stream_id,
Http2StreamId parent_stream_id,
int weight,
bool exclusive);
+ TestFrameSequence& Metadata(Http2StreamId stream_id,
+ absl::string_view payload,
+ bool multiple_frames = false);
std::string Serialize();
+ static std::string MetadataBlockForPayload(absl::string_view);
+
private:
std::string preface_;
std::vector<std::unique_ptr<spdy::SpdyFrameIR>> frames_;
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/test_utils.cc b/chromium/net/third_party/quiche/src/http2/adapter/test_utils.cc
index 315a28f0717..b8acddbb87f 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/test_utils.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/test_utils.cc
@@ -1,28 +1,123 @@
#include "http2/adapter/test_utils.h"
-#include "http2/adapter/nghttp2_util.h"
+#include <ostream>
+
+#include "absl/strings/str_format.h"
#include "common/quiche_endian.h"
+#include "spdy/core/hpack/hpack_encoder.h"
#include "spdy/core/spdy_frame_reader.h"
namespace http2 {
namespace adapter {
namespace test {
+
+TestDataFrameSource::TestDataFrameSource(Http2VisitorInterface& visitor,
+ bool has_fin)
+ : visitor_(visitor), has_fin_(has_fin) {}
+
+void TestDataFrameSource::AppendPayload(absl::string_view payload) {
+ QUICHE_CHECK(!end_data_);
+ if (!payload.empty()) {
+ payload_fragments_.push_back(std::string(payload));
+ current_fragment_ = payload_fragments_.front();
+ }
+}
+
+void TestDataFrameSource::EndData() { end_data_ = true; }
+
+std::pair<ssize_t, bool> TestDataFrameSource::SelectPayloadLength(
+ size_t max_length) {
+ // The stream is done if there's no more data, or if |max_length| is at least
+ // as large as the remaining data.
+ const bool end_data = end_data_ && (current_fragment_.empty() ||
+ (payload_fragments_.size() == 1 &&
+ max_length >= current_fragment_.size()));
+ const ssize_t length = std::min(max_length, current_fragment_.size());
+ return {length, end_data};
+}
+
+bool TestDataFrameSource::Send(absl::string_view frame_header,
+ size_t payload_length) {
+ QUICHE_LOG_IF(DFATAL, payload_length > current_fragment_.size())
+ << "payload_length: " << payload_length
+ << " current_fragment_size: " << current_fragment_.size();
+ const std::string concatenated =
+ absl::StrCat(frame_header, current_fragment_.substr(0, payload_length));
+ const ssize_t result = visitor_.OnReadyToSend(concatenated);
+ if (result < 0) {
+ // Write encountered error.
+ visitor_.OnConnectionError();
+ current_fragment_ = {};
+ payload_fragments_.clear();
+ return false;
+ } else if (result == 0) {
+ // Write blocked.
+ return false;
+ } else if (static_cast<const size_t>(result) < concatenated.size()) {
+ // Probably need to handle this better within this test class.
+ QUICHE_LOG(DFATAL)
+ << "DATA frame not fully flushed. Connection will be corrupt!";
+ visitor_.OnConnectionError();
+ current_fragment_ = {};
+ payload_fragments_.clear();
+ return false;
+ }
+ if (payload_length > 0) {
+ current_fragment_.remove_prefix(payload_length);
+ }
+ if (current_fragment_.empty() && !payload_fragments_.empty()) {
+ payload_fragments_.erase(payload_fragments_.begin());
+ if (!payload_fragments_.empty()) {
+ current_fragment_ = payload_fragments_.front();
+ }
+ }
+ return true;
+}
+
+std::string EncodeHeaders(const spdy::SpdyHeaderBlock& entries) {
+ spdy::HpackEncoder encoder;
+ encoder.DisableCompression();
+ std::string result;
+ QUICHE_CHECK(encoder.EncodeHeaderSet(entries, &result));
+ return result;
+}
+
+TestMetadataSource::TestMetadataSource(const spdy::SpdyHeaderBlock& entries)
+ : encoded_entries_(EncodeHeaders(entries)) {
+ remaining_ = encoded_entries_;
+}
+
+std::pair<ssize_t, bool> TestMetadataSource::Pack(uint8_t* dest,
+ size_t dest_len) {
+ const size_t copied = std::min(dest_len, remaining_.size());
+ std::memcpy(dest, remaining_.data(), copied);
+ remaining_.remove_prefix(copied);
+ return std::make_pair(copied, remaining_.empty());
+}
+
namespace {
using TypeAndOptionalLength =
std::pair<spdy::SpdyFrameType, absl::optional<size_t>>;
-std::vector<std::pair<const char*, std::string>> LogFriendly(
+std::ostream& operator<<(
+ std::ostream& os,
const std::vector<TypeAndOptionalLength>& types_and_lengths) {
- std::vector<std::pair<const char*, std::string>> out;
- out.reserve(types_and_lengths.size());
- for (const auto type_and_length : types_and_lengths) {
- out.push_back({spdy::FrameTypeToString(type_and_length.first),
- type_and_length.second
- ? absl::StrCat(type_and_length.second.value())
- : "<unspecified>"});
+ for (const auto& type_and_length : types_and_lengths) {
+ os << "(" << spdy::FrameTypeToString(type_and_length.first) << ", "
+ << (type_and_length.second ? absl::StrCat(type_and_length.second.value())
+ : "<unspecified>")
+ << ") ";
+ }
+ return os;
+}
+
+std::string FrameTypeToString(uint8_t frame_type) {
+ if (spdy::IsDefinedFrameType(frame_type)) {
+ return spdy::FrameTypeToString(spdy::ParseFrameType(frame_type));
+ } else {
+ return absl::StrFormat("0x%x", static_cast<int>(frame_type));
}
- return out;
}
// Custom gMock matcher, used to implement EqualsFrames().
@@ -75,16 +170,8 @@ class SpdyControlFrameMatcher
return false;
}
- if (!spdy::IsDefinedFrameType(raw_type)) {
- *listener << "; expected type " << FrameTypeToString(expected_type)
- << " but raw type " << static_cast<int>(raw_type)
- << " is not a defined frame type!";
- return false;
- }
-
- spdy::SpdyFrameType actual_type = spdy::ParseFrameType(raw_type);
- if (actual_type != expected_type) {
- *listener << "; actual type: " << FrameTypeToString(actual_type)
+ if (raw_type != static_cast<uint8_t>(expected_type)) {
+ *listener << "; actual type: " << FrameTypeToString(raw_type)
<< " but expected type: " << FrameTypeToString(expected_type);
return false;
}
@@ -96,358 +183,18 @@ class SpdyControlFrameMatcher
void DescribeTo(std::ostream* os) const override {
*os << "Data contains frames of types in sequence "
- << LogFriendly(expected_types_and_lengths_);
+ << expected_types_and_lengths_;
}
void DescribeNegationTo(std::ostream* os) const override {
*os << "Data does not contain frames of types in sequence "
- << LogFriendly(expected_types_and_lengths_);
+ << expected_types_and_lengths_;
}
private:
const std::vector<TypeAndOptionalLength> expected_types_and_lengths_;
};
-// Custom gMock matcher, used to implement HasFrameHeader().
-class FrameHeaderMatcher
- : public testing::MatcherInterface<const nghttp2_frame_hd*> {
- public:
- FrameHeaderMatcher(int32_t streamid,
- uint8_t type,
- const testing::Matcher<int> flags)
- : stream_id_(streamid), type_(type), flags_(flags) {}
-
- bool MatchAndExplain(const nghttp2_frame_hd* frame,
- testing::MatchResultListener* listener) const override {
- bool matched = true;
- if (stream_id_ != frame->stream_id) {
- *listener << "; expected stream " << stream_id_ << ", saw "
- << frame->stream_id;
- matched = false;
- }
- if (type_ != frame->type) {
- *listener << "; expected frame type " << type_ << ", saw "
- << static_cast<int>(frame->type);
- matched = false;
- }
- if (!flags_.MatchAndExplain(frame->flags, listener)) {
- matched = false;
- }
- return matched;
- }
-
- void DescribeTo(std::ostream* os) const override {
- *os << "contains a frame header with stream " << stream_id_ << ", type "
- << type_ << ", ";
- flags_.DescribeTo(os);
- }
-
- void DescribeNegationTo(std::ostream* os) const override {
- *os << "does not contain a frame header with stream " << stream_id_
- << ", type " << type_ << ", ";
- flags_.DescribeNegationTo(os);
- }
-
- private:
- const int32_t stream_id_;
- const int type_;
- const testing::Matcher<int> flags_;
-};
-
-class DataMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
- public:
- DataMatcher(const testing::Matcher<uint32_t> stream_id,
- const testing::Matcher<size_t> length,
- const testing::Matcher<int> flags)
- : stream_id_(stream_id), length_(length), flags_(flags) {}
-
- bool MatchAndExplain(const nghttp2_frame* frame,
- testing::MatchResultListener* listener) const override {
- if (frame->hd.type != NGHTTP2_DATA) {
- *listener << "; expected DATA frame, saw frame of type "
- << static_cast<int>(frame->hd.type);
- return false;
- }
- bool matched = true;
- if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) {
- matched = false;
- }
- if (!length_.MatchAndExplain(frame->hd.length, listener)) {
- matched = false;
- }
- if (!flags_.MatchAndExplain(frame->hd.flags, listener)) {
- matched = false;
- }
- return matched;
- }
-
- void DescribeTo(std::ostream* os) const override {
- *os << "contains a DATA frame, ";
- stream_id_.DescribeTo(os);
- length_.DescribeTo(os);
- flags_.DescribeTo(os);
- }
-
- void DescribeNegationTo(std::ostream* os) const override {
- *os << "does not contain a DATA frame, ";
- stream_id_.DescribeNegationTo(os);
- length_.DescribeNegationTo(os);
- flags_.DescribeNegationTo(os);
- }
-
- private:
- const testing::Matcher<uint32_t> stream_id_;
- const testing::Matcher<size_t> length_;
- const testing::Matcher<int> flags_;
-};
-
-class HeadersMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
- public:
- HeadersMatcher(const testing::Matcher<uint32_t> stream_id,
- const testing::Matcher<int> flags,
- const testing::Matcher<int> category)
- : stream_id_(stream_id), flags_(flags), category_(category) {}
-
- bool MatchAndExplain(const nghttp2_frame* frame,
- testing::MatchResultListener* listener) const override {
- if (frame->hd.type != NGHTTP2_HEADERS) {
- *listener << "; expected HEADERS frame, saw frame of type "
- << static_cast<int>(frame->hd.type);
- return false;
- }
- bool matched = true;
- if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) {
- matched = false;
- }
- if (!flags_.MatchAndExplain(frame->hd.flags, listener)) {
- matched = false;
- }
- if (!category_.MatchAndExplain(frame->headers.cat, listener)) {
- matched = false;
- }
- return matched;
- }
-
- void DescribeTo(std::ostream* os) const override {
- *os << "contains a HEADERS frame, ";
- stream_id_.DescribeTo(os);
- flags_.DescribeTo(os);
- category_.DescribeTo(os);
- }
-
- void DescribeNegationTo(std::ostream* os) const override {
- *os << "does not contain a HEADERS frame, ";
- stream_id_.DescribeNegationTo(os);
- flags_.DescribeNegationTo(os);
- category_.DescribeNegationTo(os);
- }
-
- private:
- const testing::Matcher<uint32_t> stream_id_;
- const testing::Matcher<int> flags_;
- const testing::Matcher<int> category_;
-};
-
-class RstStreamMatcher
- : public testing::MatcherInterface<const nghttp2_frame*> {
- public:
- RstStreamMatcher(const testing::Matcher<uint32_t> stream_id,
- const testing::Matcher<uint32_t> error_code)
- : stream_id_(stream_id), error_code_(error_code) {}
-
- bool MatchAndExplain(const nghttp2_frame* frame,
- testing::MatchResultListener* listener) const override {
- if (frame->hd.type != NGHTTP2_RST_STREAM) {
- *listener << "; expected RST_STREAM frame, saw frame of type "
- << static_cast<int>(frame->hd.type);
- return false;
- }
- bool matched = true;
- if (!stream_id_.MatchAndExplain(frame->hd.stream_id, listener)) {
- matched = false;
- }
- if (!error_code_.MatchAndExplain(frame->rst_stream.error_code, listener)) {
- matched = false;
- }
- return matched;
- }
-
- void DescribeTo(std::ostream* os) const override {
- *os << "contains a RST_STREAM frame, ";
- stream_id_.DescribeTo(os);
- error_code_.DescribeTo(os);
- }
-
- void DescribeNegationTo(std::ostream* os) const override {
- *os << "does not contain a RST_STREAM frame, ";
- stream_id_.DescribeNegationTo(os);
- error_code_.DescribeNegationTo(os);
- }
-
- private:
- const testing::Matcher<uint32_t> stream_id_;
- const testing::Matcher<uint32_t> error_code_;
-};
-
-class SettingsMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
- public:
- SettingsMatcher(const testing::Matcher<std::vector<Http2Setting>> values)
- : values_(values) {}
-
- bool MatchAndExplain(const nghttp2_frame* frame,
- testing::MatchResultListener* listener) const override {
- if (frame->hd.type != NGHTTP2_SETTINGS) {
- *listener << "; expected SETTINGS frame, saw frame of type "
- << static_cast<int>(frame->hd.type);
- return false;
- }
- std::vector<Http2Setting> settings;
- settings.reserve(frame->settings.niv);
- for (int i = 0; i < frame->settings.niv; ++i) {
- const auto& p = frame->settings.iv[i];
- settings.push_back({static_cast<uint16_t>(p.settings_id), p.value});
- }
- return values_.MatchAndExplain(settings, listener);
- }
-
- void DescribeTo(std::ostream* os) const override {
- *os << "contains a SETTINGS frame, ";
- values_.DescribeTo(os);
- }
-
- void DescribeNegationTo(std::ostream* os) const override {
- *os << "does not contain a SETTINGS frame, ";
- values_.DescribeNegationTo(os);
- }
-
- private:
- const testing::Matcher<std::vector<Http2Setting>> values_;
-};
-
-class PingMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
- public:
- PingMatcher(const testing::Matcher<uint64_t> id, bool is_ack)
- : id_(id), is_ack_(is_ack) {}
-
- bool MatchAndExplain(const nghttp2_frame* frame,
- testing::MatchResultListener* listener) const override {
- if (frame->hd.type != NGHTTP2_PING) {
- *listener << "; expected PING frame, saw frame of type "
- << static_cast<int>(frame->hd.type);
- return false;
- }
- bool matched = true;
- bool frame_ack = frame->hd.flags & NGHTTP2_FLAG_ACK;
- if (is_ack_ != frame_ack) {
- *listener << "; expected is_ack=" << is_ack_ << ", saw " << frame_ack;
- matched = false;
- }
- uint64_t data;
- std::memcpy(&data, frame->ping.opaque_data, sizeof(data));
- data = quiche::QuicheEndian::HostToNet64(data);
- if (!id_.MatchAndExplain(data, listener)) {
- matched = false;
- }
- return matched;
- }
-
- void DescribeTo(std::ostream* os) const override {
- *os << "contains a PING frame, ";
- id_.DescribeTo(os);
- }
-
- void DescribeNegationTo(std::ostream* os) const override {
- *os << "does not contain a PING frame, ";
- id_.DescribeNegationTo(os);
- }
-
- private:
- const testing::Matcher<uint64_t> id_;
- const bool is_ack_;
-};
-
-class GoAwayMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
- public:
- GoAwayMatcher(const testing::Matcher<uint32_t> last_stream_id,
- const testing::Matcher<uint32_t> error_code,
- const testing::Matcher<absl::string_view> opaque_data)
- : last_stream_id_(last_stream_id),
- error_code_(error_code),
- opaque_data_(opaque_data) {}
-
- bool MatchAndExplain(const nghttp2_frame* frame,
- testing::MatchResultListener* listener) const override {
- if (frame->hd.type != NGHTTP2_GOAWAY) {
- *listener << "; expected GOAWAY frame, saw frame of type "
- << static_cast<int>(frame->hd.type);
- return false;
- }
- bool matched = true;
- if (!last_stream_id_.MatchAndExplain(frame->goaway.last_stream_id,
- listener)) {
- matched = false;
- }
- if (!error_code_.MatchAndExplain(frame->goaway.error_code, listener)) {
- matched = false;
- }
- auto opaque_data =
- ToStringView(frame->goaway.opaque_data, frame->goaway.opaque_data_len);
- if (!opaque_data_.MatchAndExplain(opaque_data, listener)) {
- matched = false;
- }
- return matched;
- }
-
- void DescribeTo(std::ostream* os) const override {
- *os << "contains a GOAWAY frame, ";
- last_stream_id_.DescribeTo(os);
- error_code_.DescribeTo(os);
- opaque_data_.DescribeTo(os);
- }
-
- void DescribeNegationTo(std::ostream* os) const override {
- *os << "does not contain a GOAWAY frame, ";
- last_stream_id_.DescribeNegationTo(os);
- error_code_.DescribeNegationTo(os);
- opaque_data_.DescribeNegationTo(os);
- }
-
- private:
- const testing::Matcher<uint32_t> last_stream_id_;
- const testing::Matcher<uint32_t> error_code_;
- const testing::Matcher<absl::string_view> opaque_data_;
-};
-
-class WindowUpdateMatcher
- : public testing::MatcherInterface<const nghttp2_frame*> {
- public:
- WindowUpdateMatcher(const testing::Matcher<uint32_t> delta) : delta_(delta) {}
-
- bool MatchAndExplain(const nghttp2_frame* frame,
- testing::MatchResultListener* listener) const override {
- if (frame->hd.type != NGHTTP2_WINDOW_UPDATE) {
- *listener << "; expected WINDOW_UPDATE frame, saw frame of type "
- << static_cast<int>(frame->hd.type);
- return false;
- }
- return delta_.MatchAndExplain(frame->window_update.window_size_increment,
- listener);
- }
-
- void DescribeTo(std::ostream* os) const override {
- *os << "contains a WINDOW_UPDATE frame, ";
- delta_.DescribeTo(os);
- }
-
- void DescribeNegationTo(std::ostream* os) const override {
- *os << "does not contain a WINDOW_UPDATE frame, ";
- delta_.DescribeNegationTo(os);
- }
-
- private:
- const testing::Matcher<uint32_t> delta_;
-};
-
} // namespace
testing::Matcher<absl::string_view> EqualsFrames(
@@ -467,61 +214,6 @@ testing::Matcher<absl::string_view> EqualsFrames(
return MakeMatcher(new SpdyControlFrameMatcher(std::move(types_and_lengths)));
}
-testing::Matcher<const nghttp2_frame_hd*> HasFrameHeader(
- uint32_t streamid,
- uint8_t type,
- const testing::Matcher<int> flags) {
- return MakeMatcher(new FrameHeaderMatcher(streamid, type, flags));
-}
-
-testing::Matcher<const nghttp2_frame*> IsData(
- const testing::Matcher<uint32_t> stream_id,
- const testing::Matcher<size_t> length,
- const testing::Matcher<int> flags) {
- return MakeMatcher(new DataMatcher(stream_id, length, flags));
-}
-
-testing::Matcher<const nghttp2_frame*> IsHeaders(
- const testing::Matcher<uint32_t> stream_id,
- const testing::Matcher<int> flags,
- const testing::Matcher<int> category) {
- return MakeMatcher(new HeadersMatcher(stream_id, flags, category));
-}
-
-testing::Matcher<const nghttp2_frame*> IsRstStream(
- const testing::Matcher<uint32_t> stream_id,
- const testing::Matcher<uint32_t> error_code) {
- return MakeMatcher(new RstStreamMatcher(stream_id, error_code));
-}
-
-testing::Matcher<const nghttp2_frame*> IsSettings(
- const testing::Matcher<std::vector<Http2Setting>> values) {
- return MakeMatcher(new SettingsMatcher(values));
-}
-
-testing::Matcher<const nghttp2_frame*> IsPing(
- const testing::Matcher<uint64_t> id) {
- return MakeMatcher(new PingMatcher(id, false));
-}
-
-testing::Matcher<const nghttp2_frame*> IsPingAck(
- const testing::Matcher<uint64_t> id) {
- return MakeMatcher(new PingMatcher(id, true));
-}
-
-testing::Matcher<const nghttp2_frame*> IsGoAway(
- const testing::Matcher<uint32_t> last_stream_id,
- const testing::Matcher<uint32_t> error_code,
- const testing::Matcher<absl::string_view> opaque_data) {
- return MakeMatcher(
- new GoAwayMatcher(last_stream_id, error_code, opaque_data));
-}
-
-testing::Matcher<const nghttp2_frame*> IsWindowUpdate(
- const testing::Matcher<uint32_t> delta) {
- return MakeMatcher(new WindowUpdateMatcher(delta));
-}
-
} // namespace test
} // namespace adapter
} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/test_utils.h b/chromium/net/third_party/quiche/src/http2/adapter/test_utils.h
index ef1ae29a650..d92617fa3c9 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/test_utils.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/test_utils.h
@@ -4,26 +4,99 @@
#include <string>
#include <vector>
+#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
+#include "http2/adapter/data_source.h"
#include "http2/adapter/http2_protocol.h"
#include "http2/adapter/mock_http2_visitor.h"
-#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+#include "common/platform/api/quiche_export.h"
#include "common/platform/api/quiche_test.h"
+#include "spdy/core/spdy_header_block.h"
#include "spdy/core/spdy_protocol.h"
namespace http2 {
namespace adapter {
namespace test {
-class DataSavingVisitor : public testing::StrictMock<MockHttp2Visitor> {
+class QUICHE_NO_EXPORT DataSavingVisitor
+ : public testing::StrictMock<MockHttp2Visitor> {
public:
- void Save(absl::string_view data) { absl::StrAppend(&data_, data); }
+ ssize_t OnReadyToSend(absl::string_view data) override {
+ if (is_write_blocked_) {
+ return kSendBlocked;
+ }
+ const size_t to_accept = std::min(send_limit_, data.size());
+ if (to_accept == 0) {
+ return kSendBlocked;
+ }
+ absl::StrAppend(&data_, data.substr(0, to_accept));
+ return to_accept;
+ }
+
+ void OnMetadataForStream(Http2StreamId stream_id,
+ absl::string_view metadata) override {
+ testing::StrictMock<MockHttp2Visitor>::OnMetadataForStream(stream_id,
+ metadata);
+ auto result =
+ metadata_map_.try_emplace(stream_id, std::vector<std::string>());
+ result.first->second.push_back(std::string(metadata));
+ }
+
+ const std::vector<std::string> GetMetadata(Http2StreamId stream_id) {
+ auto it = metadata_map_.find(stream_id);
+ if (it == metadata_map_.end()) {
+ return {};
+ } else {
+ return it->second;
+ }
+ }
const std::string& data() { return data_; }
void Clear() { data_.clear(); }
+ void set_send_limit(size_t limit) { send_limit_ = limit; }
+
+ bool is_write_blocked() const { return is_write_blocked_; }
+ void set_is_write_blocked(bool value) { is_write_blocked_ = value; }
+
private:
std::string data_;
+ absl::flat_hash_map<Http2StreamId, std::vector<std::string>> metadata_map_;
+ size_t send_limit_ = std::numeric_limits<size_t>::max();
+ bool is_write_blocked_ = false;
+};
+
+// A test DataFrameSource. Starts out in the empty, blocked state.
+class QUICHE_NO_EXPORT TestDataFrameSource : public DataFrameSource {
+ public:
+ TestDataFrameSource(Http2VisitorInterface& visitor, bool has_fin);
+
+ void AppendPayload(absl::string_view payload);
+ void EndData();
+
+ std::pair<ssize_t, bool> SelectPayloadLength(size_t max_length) override;
+ bool Send(absl::string_view frame_header, size_t payload_length) override;
+ bool send_fin() const override { return has_fin_; }
+
+ private:
+ Http2VisitorInterface& visitor_;
+ std::vector<std::string> payload_fragments_;
+ absl::string_view current_fragment_;
+ // Whether the stream should end with the final frame of data.
+ const bool has_fin_;
+ // Whether |payload_fragments_| contains the final segment of data.
+ bool end_data_ = false;
+};
+
+class QUICHE_NO_EXPORT TestMetadataSource : public MetadataSource {
+ public:
+ explicit TestMetadataSource(const spdy::SpdyHeaderBlock& entries);
+
+ std::pair<ssize_t, bool> Pack(uint8_t* dest, size_t dest_len) override;
+
+ private:
+ const std::string encoded_entries_;
+ absl::string_view remaining_;
};
// These matchers check whether a string consists entirely of HTTP/2 frames of
@@ -41,42 +114,6 @@ testing::Matcher<absl::string_view> EqualsFrames(
testing::Matcher<absl::string_view> EqualsFrames(
std::vector<spdy::SpdyFrameType> types);
-testing::Matcher<const nghttp2_frame_hd*> HasFrameHeader(
- uint32_t streamid,
- uint8_t type,
- const testing::Matcher<int> flags);
-
-testing::Matcher<const nghttp2_frame*> IsData(
- const testing::Matcher<uint32_t> stream_id,
- const testing::Matcher<size_t> length,
- const testing::Matcher<int> flags);
-
-testing::Matcher<const nghttp2_frame*> IsHeaders(
- const testing::Matcher<uint32_t> stream_id,
- const testing::Matcher<int> flags,
- const testing::Matcher<int> category);
-
-testing::Matcher<const nghttp2_frame*> IsRstStream(
- const testing::Matcher<uint32_t> stream_id,
- const testing::Matcher<uint32_t> error_code);
-
-testing::Matcher<const nghttp2_frame*> IsSettings(
- const testing::Matcher<std::vector<Http2Setting>> values);
-
-testing::Matcher<const nghttp2_frame*> IsPing(
- const testing::Matcher<uint64_t> id);
-
-testing::Matcher<const nghttp2_frame*> IsPingAck(
- const testing::Matcher<uint64_t> id);
-
-testing::Matcher<const nghttp2_frame*> IsGoAway(
- const testing::Matcher<uint32_t> last_stream_id,
- const testing::Matcher<uint32_t> error_code,
- const testing::Matcher<absl::string_view> opaque_data);
-
-testing::Matcher<const nghttp2_frame*> IsWindowUpdate(
- const testing::Matcher<uint32_t> delta);
-
} // namespace test
} // namespace adapter
} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/window_manager.h b/chromium/net/third_party/quiche/src/http2/adapter/window_manager.h
index 277c24f960e..f15982d466a 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/window_manager.h
+++ b/chromium/net/third_party/quiche/src/http2/adapter/window_manager.h
@@ -3,6 +3,8 @@
#include <functional>
+#include "common/platform/api/quiche_export.h"
+
namespace http2 {
namespace adapter {
@@ -12,7 +14,7 @@ class WindowManagerPeer;
// This class keeps track of a HTTP/2 flow control window, notifying a listener
// when a window update needs to be sent. This class is not thread-safe.
-class WindowManager {
+class QUICHE_EXPORT_PRIVATE WindowManager {
public:
// A WindowUpdateListener is invoked when it is time to send a window update.
typedef std::function<void(size_t)> WindowUpdateListener;
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/window_manager_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/window_manager_test.cc
index 5e0645365ec..e706156db69 100644
--- a/chromium/net/third_party/quiche/src/http2/adapter/window_manager_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/adapter/window_manager_test.cc
@@ -90,21 +90,21 @@ TEST_F(WindowManagerTest, AvoidWindowUnderflow) {
EXPECT_EQ(wm_.CurrentWindowSize(), wm_.WindowSizeLimit());
// Don't buffer more than the total window!
wm_.MarkDataBuffered(wm_.WindowSizeLimit() + 1);
- EXPECT_EQ(wm_.CurrentWindowSize(), 0);
+ EXPECT_EQ(wm_.CurrentWindowSize(), 0u);
}
// Window manager should GFE_BUG and avoid buffered underflow.
TEST_F(WindowManagerTest, AvoidBufferedUnderflow) {
- EXPECT_EQ(peer_.buffered(), 0);
+ EXPECT_EQ(peer_.buffered(), 0u);
// Don't flush more than has been buffered!
EXPECT_QUICHE_BUG(wm_.MarkDataFlushed(1), "buffered underflow");
- EXPECT_EQ(peer_.buffered(), 0);
+ EXPECT_EQ(peer_.buffered(), 0u);
wm_.MarkDataBuffered(42);
- EXPECT_EQ(peer_.buffered(), 42);
+ EXPECT_EQ(peer_.buffered(), 42u);
// Don't flush more than has been buffered!
EXPECT_QUICHE_BUG(wm_.MarkDataFlushed(43), "buffered underflow");
- EXPECT_EQ(peer_.buffered(), 0);
+ EXPECT_EQ(peer_.buffered(), 0u);
}
// This test verifies that WindowManager notifies its listener when window is
diff --git a/chromium/net/third_party/quiche/src/http2/core/http2_priority_write_scheduler.h b/chromium/net/third_party/quiche/src/http2/core/http2_priority_write_scheduler.h
index b7046e7023a..4d64d75a961 100644
--- a/chromium/net/third_party/quiche/src/http2/core/http2_priority_write_scheduler.h
+++ b/chromium/net/third_party/quiche/src/http2/core/http2_priority_write_scheduler.h
@@ -24,7 +24,6 @@
#include "common/platform/api/quiche_logging.h"
#include "spdy/core/spdy_intrusive_list.h"
#include "spdy/core/spdy_protocol.h"
-#include "spdy/platform/api/spdy_string_utils.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.cc
index 0f1336ed29e..37933f19630 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.cc
@@ -5,7 +5,6 @@
#include "http2/hpack/decoder/hpack_decoder.h"
#include "http2/decoder/decode_status.h"
-#include "http2/platform/api/http2_estimate_memory_usage.h"
#include "http2/platform/api/http2_flag_utils.h"
#include "http2/platform/api/http2_flags.h"
#include "http2/platform/api/http2_logging.h"
@@ -110,10 +109,6 @@ bool HpackDecoder::DetectError() {
return error_ != HpackDecodingError::kOk;
}
-size_t HpackDecoder::EstimateMemoryUsage() const {
- return Http2EstimateMemoryUsage(entry_buffer_);
-}
-
void HpackDecoder::ReportError(HpackDecodingError error,
std::string detailed_error) {
HTTP2_DVLOG(3) << "HpackDecoder::ReportError is new="
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h
index 01d32a38ac1..92177070acc 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h
@@ -92,12 +92,13 @@ class QUICHE_EXPORT_PRIVATE HpackDecoder {
// detected.
bool DetectError();
+ size_t GetDynamicTableSize() const {
+ return decoder_state_.GetDynamicTableSize();
+ }
+
// Error code if an error has occurred, HpackDecodingError::kOk otherwise.
HpackDecodingError error() const { return error_; }
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
-
std::string detailed_error() const { return detailed_error_; }
private:
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h
index 14b4184bd5a..5dcdc40bf99 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h
@@ -83,6 +83,10 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderState : public HpackWholeEntryListener {
// No further callbacks will be made to the listener.
HpackDecodingError error() const { return error_; }
+ size_t GetDynamicTableSize() const {
+ return decoder_tables_.current_header_table_size();
+ }
+
const HpackDecoderTables& decoder_tables_for_test() const {
return decoder_tables_;
}
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc
index 8f2243d7f6b..d03590377e3 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc
@@ -7,7 +7,6 @@
#include <utility>
#include "http2/platform/api/http2_bug_tracker.h"
-#include "http2/platform/api/http2_estimate_memory_usage.h"
#include "http2/platform/api/http2_logging.h"
namespace http2 {
@@ -232,10 +231,6 @@ void HpackDecoderStringBuffer::OutputDebugStringTo(std::ostream& out) const {
out << "}";
}
-size_t HpackDecoderStringBuffer::EstimateMemoryUsage() const {
- return Http2EstimateMemoryUsage(buffer_);
-}
-
std::ostream& operator<<(std::ostream& out, const HpackDecoderStringBuffer& v) {
v.OutputDebugStringTo(out);
return out;
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.h
index 9397330fd96..83f59eda593 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.h
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.h
@@ -67,9 +67,6 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderStringBuffer {
Backing backing_for_testing() const { return backing_; }
void OutputDebugStringTo(std::ostream& out) const;
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
-
private:
// Storage for the string being buffered, if buffering is necessary
// (e.g. if Huffman encoded, buffer_ is storage for the decoded string).
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc
index b7a8b39d051..82244efe4eb 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc
@@ -10,7 +10,6 @@
#include "absl/strings/escaping.h"
#include "http2/platform/api/http2_logging.h"
-#include "http2/platform/api/http2_string_utils.h"
#include "http2/platform/api/http2_test_helpers.h"
#include "common/platform/api/quiche_test.h"
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.cc
index 76d1f31354e..5f3e63e2315 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.cc
@@ -5,12 +5,11 @@
#include "http2/hpack/decoder/hpack_whole_entry_buffer.h"
#include "absl/strings/str_cat.h"
-#include "http2/platform/api/http2_estimate_memory_usage.h"
#include "http2/platform/api/http2_flag_utils.h"
#include "http2/platform/api/http2_flags.h"
#include "http2/platform/api/http2_logging.h"
#include "http2/platform/api/http2_macros.h"
-#include "http2/platform/api/http2_string_utils.h"
+#include "common/quiche_text_utils.h"
namespace http2 {
@@ -35,10 +34,6 @@ void HpackWholeEntryBuffer::BufferStringsIfUnbuffered() {
value_.BufferStringIfUnbuffered();
}
-size_t HpackWholeEntryBuffer::EstimateMemoryUsage() const {
- return Http2EstimateMemoryUsage(name_) + Http2EstimateMemoryUsage(value_);
-}
-
void HpackWholeEntryBuffer::OnIndexedHeader(size_t index) {
HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnIndexedHeader: index=" << index;
listener_->OnIndexedHeader(index);
@@ -71,7 +66,8 @@ void HpackWholeEntryBuffer::OnNameStart(bool huffman_encoded, size_t len) {
void HpackWholeEntryBuffer::OnNameData(const char* data, size_t len) {
HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnNameData: len=" << len
<< " data:\n"
- << Http2HexDump(absl::string_view(data, len));
+ << quiche::QuicheTextUtils::HexDump(
+ absl::string_view(data, len));
QUICHE_DCHECK_EQ(maybe_name_index_, 0u);
if (!error_detected_ && !name_.OnData(data, len)) {
ReportError(HpackDecodingError::kNameHuffmanError, "");
@@ -108,7 +104,8 @@ void HpackWholeEntryBuffer::OnValueStart(bool huffman_encoded, size_t len) {
void HpackWholeEntryBuffer::OnValueData(const char* data, size_t len) {
HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnValueData: len=" << len
<< " data:\n"
- << Http2HexDump(absl::string_view(data, len));
+ << quiche::QuicheTextUtils::HexDump(
+ absl::string_view(data, len));
if (!error_detected_ && !value_.OnData(data, len)) {
ReportError(HpackDecodingError::kValueHuffmanError, "");
HTTP2_CODE_COUNT_N(decompress_failure_3, 22, 23);
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.h
index eb4edbe53f6..0cce0bafd72 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.h
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.h
@@ -63,9 +63,6 @@ class QUICHE_EXPORT_PRIVATE HpackWholeEntryBuffer
// no further callbacks will be made to the listener.
bool error_detected() const { return error_detected_; }
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
-
// Implement the HpackEntryDecoderListener methods.
void OnIndexedHeader(size_t index) override;
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc
index e7a346e7719..395706f219c 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc
@@ -12,7 +12,6 @@
#include "absl/strings/escaping.h"
#include "http2/decoder/decode_buffer.h"
#include "http2/decoder/decode_status.h"
-#include "http2/platform/api/http2_string_utils.h"
#include "http2/platform/api/http2_test_helpers.h"
#include "http2/tools/random_decoder_test.h"
#include "common/platform/api/quiche_test.h"
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc
index 0d321876149..84439c16973 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc
@@ -6,7 +6,6 @@
#include "absl/base/macros.h"
#include "absl/strings/escaping.h"
-#include "http2/platform/api/http2_string_utils.h"
#include "common/platform/api/quiche_test.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc
index bca12c50ebb..3b0047d49d4 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc
@@ -11,9 +11,9 @@
#include "http2/decoder/decode_status.h"
#include "http2/hpack/huffman/hpack_huffman_decoder.h"
#include "http2/hpack/huffman/hpack_huffman_encoder.h"
-#include "http2/platform/api/http2_string_utils.h"
#include "http2/tools/random_decoder_test.h"
#include "common/platform/api/quiche_test.h"
+#include "common/quiche_text_utils.h"
using ::testing::AssertionResult;
using ::testing::AssertionSuccess;
@@ -114,8 +114,8 @@ TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomAsciiNonControlString) {
const std::string s = RandomAsciiNonControlString(length);
ASSERT_TRUE(TranscodeAndValidateSeveralWays(s))
<< "Unable to decode:\n\n"
- << Http2HexDump(s) << "\n\noutput_buffer_:\n"
- << Http2HexDump(output_buffer_);
+ << quiche::QuicheTextUtils::HexDump(s) << "\n\noutput_buffer_:\n"
+ << quiche::QuicheTextUtils::HexDump(output_buffer_);
}
}
@@ -124,8 +124,8 @@ TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomBytes) {
const std::string s = RandomBytes(length);
ASSERT_TRUE(TranscodeAndValidateSeveralWays(s))
<< "Unable to decode:\n\n"
- << Http2HexDump(s) << "\n\noutput_buffer_:\n"
- << Http2HexDump(output_buffer_);
+ << quiche::QuicheTextUtils::HexDump(s) << "\n\noutput_buffer_:\n"
+ << quiche::QuicheTextUtils::HexDump(output_buffer_);
}
}
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc
index 3821127bd08..b292c4b3767 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc
@@ -5,7 +5,6 @@
#include "http2/hpack/tools/hpack_block_builder.h"
#include "absl/strings/escaping.h"
-#include "http2/platform/api/http2_string_utils.h"
#include "common/platform/api/quiche_test.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc
index b2783e3b277..64255b1ef2d 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc
@@ -10,7 +10,6 @@
#include "absl/strings/str_cat.h"
#include "http2/platform/api/http2_bug_tracker.h"
#include "http2/platform/api/http2_logging.h"
-#include "http2/platform/api/http2_string_utils.h"
namespace http2 {
namespace test {
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc
index a67cca6c729..aff6af3730e 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc
@@ -12,7 +12,6 @@
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "http2/platform/api/http2_logging.h"
-#include "http2/platform/api/http2_string_utils.h"
#include "http2/tools/random_decoder_test.h"
#include "common/platform/api/quiche_test.h"
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc
index 7f049e8eabf..5daf88b99d7 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc
@@ -6,7 +6,6 @@
#include "absl/base/macros.h"
#include "absl/strings/escaping.h"
-#include "http2/platform/api/http2_string_utils.h"
#include "common/platform/api/quiche_test.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc
index d8152593547..0c832d9e83a 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc
@@ -18,9 +18,9 @@
#include "absl/strings/string_view.h"
#include "http2/hpack/tools/hpack_block_builder.h"
#include "http2/platform/api/http2_logging.h"
-#include "http2/platform/api/http2_string_utils.h"
#include "http2/tools/random_decoder_test.h"
#include "common/platform/api/quiche_test.h"
+#include "common/quiche_text_utils.h"
using ::testing::AssertionFailure;
using ::testing::AssertionSuccess;
@@ -166,7 +166,7 @@ class HpackVarintRoundTripTest : public RandomDecoderTest {
std::string msg = absl::StrCat("value=", value, " (0x", absl::Hex(value),
"), prefix_length=", prefix_length,
", expected_bytes=", expected_bytes, "\n",
- Http2HexDump(buffer_));
+ quiche::QuicheTextUtils::HexDump(buffer_));
if (value == minimum) {
HTTP2_LOG(INFO) << "Checking minimum; " << msg;
@@ -221,7 +221,8 @@ class HpackVarintRoundTripTest : public RandomDecoderTest {
if (expected_bytes < 11) {
// Confirm the claim that beyond requires more bytes.
Encode(beyond, prefix_length);
- EXPECT_EQ(expected_bytes + 1, buffer_.size()) << Http2HexDump(buffer_);
+ EXPECT_EQ(expected_bytes + 1, buffer_.size())
+ << quiche::QuicheTextUtils::HexDump(buffer_);
}
std::set<uint64_t> values;
@@ -285,9 +286,9 @@ TEST_F(HpackVarintRoundTripTest, Encode) {
for (uint64_t value : values) {
EncodeNoRandom(value, prefix_length);
- std::string dump = Http2HexDump(buffer_);
+ std::string dump = quiche::QuicheTextUtils::HexDump(buffer_);
HTTP2_LOG(INFO) << absl::StrFormat("%10llu %0#18x ", value, value)
- << Http2HexDump(buffer_).substr(7);
+ << quiche::QuicheTextUtils::HexDump(buffer_).substr(7);
}
}
}
diff --git a/chromium/net/third_party/quiche/src/http2/http2_constants.cc b/chromium/net/third_party/quiche/src/http2/http2_constants.cc
index bebe8803652..43d89bcc221 100644
--- a/chromium/net/third_party/quiche/src/http2/http2_constants.cc
+++ b/chromium/net/third_party/quiche/src/http2/http2_constants.cc
@@ -8,7 +8,6 @@
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "http2/platform/api/http2_logging.h"
-#include "http2/platform/api/http2_string_utils.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/http2_structures.cc b/chromium/net/third_party/quiche/src/http2/http2_structures.cc
index a96edccdd71..f8a8fadadbf 100644
--- a/chromium/net/third_party/quiche/src/http2/http2_structures.cc
+++ b/chromium/net/third_party/quiche/src/http2/http2_structures.cc
@@ -9,7 +9,6 @@
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
-#include "http2/platform/api/http2_string_utils.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc b/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc
index 44712bc13b9..d74b74cc6f3 100644
--- a/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc
@@ -9,7 +9,7 @@
// Note that EXPECT.*DEATH tests are slow (a fork is probably involved).
// And in case you're wondering, yes, these are ridiculously thorough tests,
-// but believe it or not, I've found stupid bugs this way.
+// but believe it or not, I've found silly bugs this way.
#include <memory>
#include <ostream>
diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_containers.h b/chromium/net/third_party/quiche/src/http2/platform/api/http2_containers.h
deleted file mode 100644
index 7bd3f8eb4a3..00000000000
--- a/chromium/net/third_party/quiche/src/http2/platform/api/http2_containers.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef QUICHE_HTTP2_PLATFORM_API_HTTP2_CONTAINERS_H_
-#define QUICHE_HTTP2_PLATFORM_API_HTTP2_CONTAINERS_H_
-
-#include "quiche_platform_impl/quiche_containers_impl.h"
-
-namespace http2 {
-
-// Represents a double-ended queue which may be backed by a list or a flat
-// circular buffer.
-//
-// DOES NOT GUARANTEE POINTER OR ITERATOR STABILITY!
-template <typename T>
-using Http2Deque = quiche::QuicheDequeImpl<T>;
-
-} // namespace http2
-
-#endif // QUICHE_HTTP2_PLATFORM_API_HTTP2_CONTAINERS_H_
diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_estimate_memory_usage.h b/chromium/net/third_party/quiche/src/http2/platform/api/http2_estimate_memory_usage.h
deleted file mode 100644
index 39b3af59528..00000000000
--- a/chromium/net/third_party/quiche/src/http2/platform/api/http2_estimate_memory_usage.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The Chromium 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 QUICHE_HTTP2_PLATFORM_API_HTTP2_ESTIMATE_MEMORY_USAGE_H_
-#define QUICHE_HTTP2_PLATFORM_API_HTTP2_ESTIMATE_MEMORY_USAGE_H_
-
-#include <cstddef>
-
-#include "quiche_platform_impl/quiche_estimate_memory_usage_impl.h"
-
-namespace http2 {
-
-template <class T>
-size_t Http2EstimateMemoryUsage(const T& object) {
- return quiche::QuicheEstimateMemoryUsageImpl(object);
-}
-
-} // namespace http2
-
-#endif // QUICHE_HTTP2_PLATFORM_API_HTTP2_ESTIMATE_MEMORY_USAGE_H_
diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h b/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h
deleted file mode 100644
index cf39515ad4c..00000000000
--- a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 The Chromium 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 QUICHE_HTTP2_PLATFORM_API_HTTP2_STRING_UTILS_H_
-#define QUICHE_HTTP2_PLATFORM_API_HTTP2_STRING_UTILS_H_
-
-#include <string>
-#include <type_traits>
-#include <utility>
-
-#include "absl/strings/string_view.h"
-#include "net/http2/platform/impl/http2_string_utils_impl.h"
-
-namespace http2 {
-
-inline std::string Http2HexDump(absl::string_view data) {
- return Http2HexDumpImpl(data);
-}
-
-} // namespace http2
-
-#endif // QUICHE_HTTP2_PLATFORM_API_HTTP2_STRING_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc
index a053beb10df..47135233ffd 100644
--- a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc
+++ b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc
@@ -126,7 +126,7 @@ void FrameParts::SetAltSvcExpected(absl::string_view origin,
opt_altsvc_value_length_ = value.size();
}
-bool FrameParts::OnFrameHeader(const Http2FrameHeader& header) {
+bool FrameParts::OnFrameHeader(const Http2FrameHeader& /*header*/) {
ADD_FAILURE() << "OnFrameHeader: " << *this;
return true;
}
diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.cc b/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.cc
index 8f9014efd17..71c08b78faf 100644
--- a/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.cc
+++ b/chromium/net/third_party/quiche/src/http2/test_tools/http2_random.cc
@@ -2,7 +2,6 @@
#include "absl/strings/escaping.h"
#include "http2/platform/api/http2_logging.h"
-#include "http2/platform/api/http2_string_utils.h"
#include "third_party/boringssl/src/include/openssl/chacha.h"
#include "third_party/boringssl/src/include/openssl/rand.h"
diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc
index 173c8d30488..9cdff7e2b6c 100644
--- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc
@@ -24,20 +24,10 @@ WriteResult QuicBatchWriterBase::WritePacket(
const WriteResult result =
InternalWritePacket(buffer, buf_len, self_address, peer_address, options);
- if (GetQuicReloadableFlag(quic_batch_writer_fix_write_blocked)) {
- if (IsWriteBlockedStatus(result.status)) {
- if (result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED) {
- QUIC_CODE_COUNT(quic_batch_writer_fix_write_blocked_data_buffered);
- } else {
- QUIC_CODE_COUNT(quic_batch_writer_fix_write_blocked_data_not_buffered);
- }
- write_blocked_ = true;
- }
- } else {
- if (result.status == WRITE_STATUS_BLOCKED) {
- write_blocked_ = true;
- }
+ if (IsWriteBlockedStatus(result.status)) {
+ write_blocked_ = true;
}
+
return result;
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.h b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.h
index 820100adabd..e14040fb39c 100644
--- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.h
+++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.h
@@ -12,6 +12,7 @@
#include <iostream>
#include <utility>
+#include "absl/base/optimization.h"
#include "quic/core/batch_writer/quic_batch_writer_base.h"
#include "quic/core/quic_udp_socket.h"
#include "quic/platform/api/quic_test.h"
@@ -259,8 +260,9 @@ class QUIC_EXPORT_PRIVATE QuicUdpBatchWriterIOTest
QuicSocketAddress self_address_;
QuicSocketAddress peer_address_;
- char packet_buffer_[1500];
- char control_buffer_[kDefaultUdpPacketControlBufferSize];
+ ABSL_CACHELINE_ALIGNED char packet_buffer_[1500];
+ ABSL_CACHELINE_ALIGNED char
+ control_buffer_[kDefaultUdpPacketControlBufferSize];
int address_family_;
const size_t data_size_;
const size_t packet_size_;
diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer_test.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer_test.cc
index 8a1b93fc723..4aab1982f55 100644
--- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer_test.cc
@@ -322,11 +322,9 @@ TEST_F(QuicGsoBatchWriterTest, WriteBlockDataBuffered) {
}));
ASSERT_EQ(WriteResult(WRITE_STATUS_BLOCKED_DATA_BUFFERED, EWOULDBLOCK),
WritePacket(&writer, 50));
- if (GetQuicReloadableFlag(quic_batch_writer_fix_write_blocked)) {
- EXPECT_TRUE(writer.IsWriteBlocked());
- } else {
- EXPECT_FALSE(writer.IsWriteBlocked());
- }
+
+ EXPECT_TRUE(writer.IsWriteBlocked());
+
ASSERT_EQ(250u, writer.batch_buffer().SizeInUse());
ASSERT_EQ(3u, writer.buffered_writes().size());
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h
index 6738f49ab05..2d5924b06e1 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h
@@ -47,6 +47,7 @@ struct QUIC_EXPORT_PRIVATE SendTimeState {
bytes_in_flight(bytes_in_flight) {}
SendTimeState(const SendTimeState& other) = default;
+ SendTimeState& operator=(const SendTimeState& other) = default;
friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
const SendTimeState& s);
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc
index 8d4a49350b5..6eb9b2ec940 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc
@@ -264,9 +264,7 @@ void Bbr2NetworkModel::AdaptLowerBounds(
// sample_max_bandwidth will be Zero() if the loss is triggered by a timer
// expiring. Ideally we'd use the most recent bandwidth sample,
// but bandwidth_latest is safer than Zero().
- if (GetQuicReloadableFlag(quic_bbr2_fix_bw_lo_mode2) &&
- !congestion_event.sample_max_bandwidth.IsZero()) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fix_bw_lo_mode2, 1, 2);
+ if (!congestion_event.sample_max_bandwidth.IsZero()) {
// bandwidth_latest_ is the max bandwidth for the round, but to allow
// fast, conservation style response to loss, use the last sample.
last_bandwidth = congestion_event.sample_max_bandwidth;
@@ -286,9 +284,7 @@ void Bbr2NetworkModel::AdaptLowerBounds(
}
// If it's the end of the round, ensure bandwidth_lo doesn't decrease more
// than beta.
- if (GetQuicReloadableFlag(quic_bbr2_fix_bw_lo_mode) &&
- congestion_event.end_of_round_trip) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fix_bw_lo_mode, 2, 2);
+ if (congestion_event.end_of_round_trip) {
bandwidth_lo_ =
std::max(bandwidth_lo_, prior_bandwidth_lo_ * (1.0 - Params().beta));
prior_bandwidth_lo_ = QuicBandwidth::Zero();
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h
index b7007f55705..d28036df543 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h
@@ -176,10 +176,6 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params {
// Can be disabled by connection option 'B2RP'.
bool avoid_unnecessary_probe_rtt = true;
- // Can be disabled by connection option 'B2CL'.
- bool avoid_too_low_probe_bw_cwnd =
- GetQuicReloadableFlag(quic_bbr2_avoid_too_low_probe_bw_cwnd);
-
// Can be enabled by connection option 'B2LO'.
bool ignore_inflight_lo = false;
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.cc
index af0f00fee80..ce9443f7d1d 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.cc
@@ -80,36 +80,12 @@ Bbr2Mode Bbr2ProbeBwMode::OnCongestionEvent(
}
Limits<QuicByteCount> Bbr2ProbeBwMode::GetCwndLimits() const {
- if (!GetQuicReloadableFlag(quic_bbr2_avoid_too_low_probe_bw_cwnd)) {
- if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
- return NoGreaterThan(
- std::min(model_->inflight_lo(), model_->inflight_hi_with_headroom()));
- }
-
+ if (cycle_.phase == CyclePhase::PROBE_CRUISE) {
return NoGreaterThan(
- std::min(model_->inflight_lo(), model_->inflight_hi()));
- }
-
- QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_avoid_too_low_probe_bw_cwnd);
-
- QuicByteCount upper_limit =
- std::min(model_->inflight_lo(), cycle_.phase == CyclePhase::PROBE_CRUISE
- ? model_->inflight_hi_with_headroom()
- : model_->inflight_hi());
-
- if (Params().avoid_too_low_probe_bw_cwnd) {
- // Ensure upper_limit is at least BDP + AckHeight.
- QuicByteCount bdp_with_ack_height =
- model_->BDP(model_->MaxBandwidth()) + model_->MaxAckHeight();
- if (upper_limit < bdp_with_ack_height) {
- QUIC_DVLOG(3) << sender_ << " Rasing upper_limit from " << upper_limit
- << " to " << bdp_with_ack_height;
- QUIC_CODE_COUNT(quic_bbr2_avoid_too_low_probe_bw_cwnd_in_effect);
- upper_limit = bdp_with_ack_height;
- }
+ std::min(model_->inflight_lo(), model_->inflight_hi_with_headroom()));
}
- return NoGreaterThan(upper_limit);
+ return NoGreaterThan(std::min(model_->inflight_lo(), model_->inflight_hi()));
}
bool Bbr2ProbeBwMode::IsProbingForBandwidth() const {
@@ -500,9 +476,7 @@ void Bbr2ProbeBwMode::EnterProbeDown(bool probed_too_high,
cycle_.rounds_in_phase = 0;
cycle_.phase_start_time = now;
++sender_->connection_stats_->bbr_num_cycles;
- if (GetQuicReloadableFlag(quic_bbr2_fix_bw_lo_mode2) &&
- Params().bw_lo_mode_ != Bbr2Params::QuicBandwidthLoMode::DEFAULT) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fix_bw_lo_mode2, 2, 2);
+ if (Params().bw_lo_mode_ != Bbr2Params::QuicBandwidthLoMode::DEFAULT) {
// Clear bandwidth lo if it was set in PROBE_UP, because losses in PROBE_UP
// should not permanently change bandwidth_lo.
// It's possible for bandwidth_lo to be set during REFILL, but if that was
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc
index b69a7952afa..09b920e5759 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc
@@ -16,6 +16,7 @@
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
+#include "common/print_elements.h"
namespace quic {
@@ -101,10 +102,6 @@ void Bbr2Sender::SetFromConfig(const QuicConfig& config,
if (config.HasClientRequestedIndependentOption(kB2RP, perspective)) {
params_.avoid_unnecessary_probe_rtt = false;
}
- if (GetQuicReloadableFlag(quic_bbr2_avoid_too_low_probe_bw_cwnd) &&
- config.HasClientRequestedIndependentOption(kB2CL, perspective)) {
- params_.avoid_too_low_probe_bw_cwnd = false;
- }
if (config.HasClientRequestedIndependentOption(k1RTT, perspective)) {
params_.startup_full_bw_rounds = 1;
}
@@ -287,7 +284,8 @@ void Bbr2Sender::OnCongestionEvent(bool /*rtt_updated*/,
}
QUIC_DVLOG(3)
- << this << " END CongestionEvent(acked:" << acked_packets
+ << this
+ << " END CongestionEvent(acked:" << quiche::PrintElements(acked_packets)
<< ", lost:" << lost_packets.size() << ") "
<< ", Mode:" << mode_ << ", RttCount:" << model_.RoundTripCount()
<< ", BytesInFlight:" << congestion_event.bytes_in_flight
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc
index 99acf2c3e21..ae0c8842c8e 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -477,7 +477,11 @@ TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer2RTTAggregationBytes) {
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
- EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
+ if (GetQuicReloadableFlag(quic_fix_pacing_sender_bursts)) {
+ EXPECT_EQ(sender_loss_rate_in_packets(), 0);
+ } else {
+ EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
+ }
// The margin here is high, because both link level aggregation and ack
// decimation can greatly increase smoothed rtt.
EXPECT_GE(params.RTT() * 5, rtt_stats()->smoothed_rtt());
@@ -558,6 +562,9 @@ TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncrease)) {
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
+ // Ensure the full bandwidth is discovered.
+ EXPECT_APPROX_EQ(params.test_link.bandwidth,
+ sender_->ExportDebugState().bandwidth_hi, 0.02f);
}
// Test the number of losses incurred by the startup phase in a situation when
@@ -1075,7 +1082,6 @@ TEST_F(Bbr2DefaultTopologyTest, ProbeBwAfterQuiescencePostponeMinRttTimestamp) {
min_rtt_timestamp_after_idle);
}
-// Regression test for http://go/switchtobbr2midconnection.
TEST_F(Bbr2DefaultTopologyTest, SwitchToBbr2MidConnection) {
QuicTime now = QuicTime::Zero();
BbrSender old_sender(sender_connection()->clock()->Now(),
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc
index a43601eae0d..265a422817d 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc
@@ -102,6 +102,12 @@ void PacingSender::OnPacketSent(
// is about 10ms of queueing.
lumpy_tokens_ = 1u;
}
+ if (GetQuicReloadableFlag(quic_fix_pacing_sender_bursts) &&
+ (bytes_in_flight + bytes) >= sender_->GetCongestionWindow()) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_fix_pacing_sender_bursts);
+ // Don't add lumpy_tokens if the congestion controller is CWND limited.
+ lumpy_tokens_ = 1u;
+ }
}
--lumpy_tokens_;
if (pacing_limited_) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender_test.cc
index b7513576cca..ec3119d2407 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender_test.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <utility>
+#include "quic/core/quic_constants.h"
#include "quic/core/quic_packets.h"
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
@@ -75,42 +76,41 @@ class PacingSenderTest : public QuicTest {
}
void CheckPacketIsSentImmediately(HasRetransmittableData retransmittable_data,
- QuicByteCount bytes_in_flight,
+ QuicByteCount prior_in_flight,
bool in_recovery,
- bool cwnd_limited,
QuicPacketCount cwnd) {
// In order for the packet to be sendable, the underlying sender must
// permit it to be sent immediately.
for (int i = 0; i < 2; ++i) {
- EXPECT_CALL(*mock_sender_, CanSend(bytes_in_flight))
+ EXPECT_CALL(*mock_sender_, CanSend(prior_in_flight))
.WillOnce(Return(true));
// Verify that the packet can be sent immediately.
EXPECT_EQ(zero_time_,
- pacing_sender_->TimeUntilSend(clock_.Now(), bytes_in_flight));
+ pacing_sender_->TimeUntilSend(clock_.Now(), prior_in_flight));
}
// Actually send the packet.
- if (bytes_in_flight == 0) {
+ if (prior_in_flight == 0) {
EXPECT_CALL(*mock_sender_, InRecovery()).WillOnce(Return(in_recovery));
}
EXPECT_CALL(*mock_sender_,
- OnPacketSent(clock_.Now(), bytes_in_flight, packet_number_,
+ OnPacketSent(clock_.Now(), prior_in_flight, packet_number_,
kMaxOutgoingPacketSize, retransmittable_data));
EXPECT_CALL(*mock_sender_, GetCongestionWindow())
- .Times(AtMost(1))
.WillRepeatedly(Return(cwnd * kDefaultTCPMSS));
EXPECT_CALL(*mock_sender_,
- CanSend(bytes_in_flight + kMaxOutgoingPacketSize))
+ CanSend(prior_in_flight + kMaxOutgoingPacketSize))
.Times(AtMost(1))
- .WillRepeatedly(Return(!cwnd_limited));
- pacing_sender_->OnPacketSent(clock_.Now(), bytes_in_flight,
+ .WillRepeatedly(Return((prior_in_flight + kMaxOutgoingPacketSize) <
+ (cwnd * kDefaultTCPMSS)));
+ pacing_sender_->OnPacketSent(clock_.Now(), prior_in_flight,
packet_number_++, kMaxOutgoingPacketSize,
retransmittable_data);
}
void CheckPacketIsSentImmediately() {
CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, kBytesInFlight,
- false, false, 10);
+ false, 10);
}
void CheckPacketIsDelayed(QuicTime::Delta delay) {
@@ -242,7 +242,7 @@ TEST_F(PacingSenderTest, InitialBurst) {
// Next time TimeUntilSend is called with no bytes in flight, pacing should
// allow a packet to be sent, and when it's sent, the tokens are refilled.
- CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, false, false, 10);
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, false, 10);
for (int i = 0; i < kInitialBurstPackets - 1; ++i) {
CheckPacketIsSentImmediately();
}
@@ -276,7 +276,7 @@ TEST_F(PacingSenderTest, InitialBurstNoRttMeasurement) {
// Next time TimeUntilSend is called with no bytes in flight, the tokens
// should be refilled and there should be no delay.
- CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, false, false, 10);
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, false, 10);
// Send 10 packets, and verify that they are not paced.
for (int i = 0; i < kInitialBurstPackets - 1; ++i) {
CheckPacketIsSentImmediately();
@@ -314,7 +314,7 @@ TEST_F(PacingSenderTest, FastSending) {
// Next time TimeUntilSend is called with no bytes in flight, the tokens
// should be refilled and there should be no delay.
- CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, false, false, 10);
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, false, 10);
for (int i = 0; i < kInitialBurstPackets - 1; ++i) {
CheckPacketIsSentImmediately();
}
@@ -348,10 +348,12 @@ TEST_F(PacingSenderTest, NoBurstEnteringRecovery) {
// One packet is sent immediately, because of 1ms pacing granularity.
CheckPacketIsSentImmediately();
// Ensure packets are immediately paced.
- EXPECT_CALL(*mock_sender_, CanSend(kDefaultTCPMSS)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_sender_, CanSend(kMaxOutgoingPacketSize))
+ .WillOnce(Return(true));
// Verify the next packet is paced and delayed 2ms due to granularity.
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2),
- pacing_sender_->TimeUntilSend(clock_.Now(), kDefaultTCPMSS));
+ EXPECT_EQ(
+ QuicTime::Delta::FromMilliseconds(2),
+ pacing_sender_->TimeUntilSend(clock_.Now(), kMaxOutgoingPacketSize));
CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
}
@@ -364,7 +366,7 @@ TEST_F(PacingSenderTest, NoBurstInRecovery) {
UpdateRtt();
// Ensure only one packet is sent immediately and the rest are paced.
- CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, true, false, 10);
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, true, 10);
CheckPacketIsSentImmediately();
CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
}
@@ -385,8 +387,11 @@ TEST_F(PacingSenderTest, CwndLimited) {
// Wake up on time.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2));
// After sending packet 3, cwnd is limited.
- CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, kBytesInFlight, false,
- true, 10);
+ // This test is slightly odd because bytes_in_flight is calculated using
+ // kMaxOutgoingPacketSize and CWND is calculated using kDefaultTCPMSS,
+ // which is 8 bytes larger, so 3 packets can be sent for a CWND of 2.
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA,
+ 2 * kMaxOutgoingPacketSize, false, 2);
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
// Verify pacing sender stops making up for lost time after sending packet 3.
@@ -438,20 +443,23 @@ TEST_F(PacingSenderTest, LumpyPacingWithInitialBurstToken) {
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3));
CheckPacketIsSentImmediately();
// After sending packet 21, cwnd is limited.
- CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, kBytesInFlight, false,
- true, 10);
+ // This test is slightly odd because bytes_in_flight is calculated using
+ // kMaxOutgoingPacketSize and CWND is calculated using kDefaultTCPMSS,
+ // which is 8 bytes larger, so 21 packets can be sent for a CWND of 20.
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA,
+ 20 * kMaxOutgoingPacketSize, false, 20);
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
// Suppose cwnd size is 5, so that lumpy size becomes 2.
CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, kBytesInFlight, false,
- false, 5);
+ 5);
CheckPacketIsSentImmediately();
// Packet 24 will be delayed 2ms.
CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
}
TEST_F(PacingSenderTest, NoLumpyPacingForLowBandwidthFlows) {
- // Set lumpy size to be 3, and cwnd faction to 0.5
+ // Set lumpy size to be 3, and cwnd fraction to 0.5
SetQuicFlag(FLAGS_quic_lumpy_pacing_size, 3);
SetQuicFlag(FLAGS_quic_lumpy_pacing_cwnd_fraction, 0.5f);
@@ -476,6 +484,43 @@ TEST_F(PacingSenderTest, NoLumpyPacingForLowBandwidthFlows) {
}
}
+// Regression test for b/184471302 to ensure that ACKs received back-to-back
+// don't cause bursts in sending.
+TEST_F(PacingSenderTest, NoBurstsForLumpyPacingWithAckAggregation) {
+ // Configure pacing rate of 1 packet per millisecond.
+ QuicTime::Delta inter_packet_delay = QuicTime::Delta::FromMilliseconds(1);
+ InitPacingRate(kInitialBurstPackets,
+ QuicBandwidth::FromBytesAndTimeDelta(kMaxOutgoingPacketSize,
+ inter_packet_delay));
+ UpdateRtt();
+
+ // Send kInitialBurstPackets packets, and verify that they are not paced.
+ for (int i = 0; i < kInitialBurstPackets; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+ // The last packet of the burst causes the sender to be CWND limited.
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA,
+ 10 * kMaxOutgoingPacketSize, false, 10);
+
+ if (GetQuicReloadableFlag(quic_fix_pacing_sender_bursts)) {
+ // The last sent packet made the connection CWND limited, so no lumpy tokens
+ // should be available.
+ EXPECT_EQ(0u, pacing_sender_->lumpy_tokens());
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA,
+ 10 * kMaxOutgoingPacketSize, false, 10);
+ EXPECT_EQ(0u, pacing_sender_->lumpy_tokens());
+ CheckPacketIsDelayed(2 * inter_packet_delay);
+ } else {
+ EXPECT_EQ(1u, pacing_sender_->lumpy_tokens());
+ // Repeatedly send single packets to make the sender CWND limited and
+ // observe that there's no pacing without the fix.
+ for (int i = 0; i < 10; ++i) {
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA,
+ 10 * kMaxOutgoingPacketSize, false, 10);
+ }
+ }
+}
+
TEST_F(PacingSenderTest, IdealNextPacketSendTimeWithLumpyPacing) {
// Set lumpy size to be 3, and cwnd faction to 0.5
SetQuicFlag(FLAGS_quic_lumpy_pacing_size, 3);
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.cc
index 2be6fa44b2a..e167dec9cf1 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.cc
@@ -29,8 +29,7 @@ RttStats::RttStats()
mean_deviation_(QuicTime::Delta::Zero()),
calculate_standard_deviation_(false),
initial_rtt_(QuicTime::Delta::FromMilliseconds(kInitialRttMs)),
- last_update_time_(QuicTime::Zero()),
- ignore_max_ack_delay_(false) {}
+ last_update_time_(QuicTime::Zero()) {}
void RttStats::ExpireSmoothedMetrics() {
mean_deviation_ = std::max(
@@ -63,17 +62,19 @@ void RttStats::UpdateRtt(QuicTime::Delta send_delta,
QuicTime::Delta rtt_sample(send_delta);
previous_srtt_ = smoothed_rtt_;
-
- if (ignore_max_ack_delay_) {
- ack_delay = QuicTime::Delta::Zero();
- }
// Correct for ack_delay if information received from the peer results in a
// an RTT sample at least as large as min_rtt. Otherwise, only use the
// send_delta.
+ // TODO(fayang): consider to ignore rtt_sample if rtt_sample < ack_delay and
+ // ack_delay is relatively large.
if (rtt_sample > ack_delay) {
if (rtt_sample - min_rtt_ >= ack_delay) {
rtt_sample = rtt_sample - ack_delay;
+ } else {
+ QUIC_CODE_COUNT(quic_ack_delay_makes_rtt_sample_smaller_than_min_rtt);
}
+ } else {
+ QUIC_CODE_COUNT(quic_ack_delay_greater_than_rtt_sample);
}
latest_rtt_ = rtt_sample;
if (calculate_standard_deviation_) {
@@ -138,7 +139,6 @@ void RttStats::CloneFrom(const RttStats& stats) {
calculate_standard_deviation_ = stats.calculate_standard_deviation_;
initial_rtt_ = stats.initial_rtt_;
last_update_time_ = stats.last_update_time_;
- ignore_max_ack_delay_ = stats.ignore_max_ack_delay_;
}
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h
index 0047b0f0448..cb591dbf2e1 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h
@@ -101,12 +101,6 @@ class QUIC_EXPORT_PRIVATE RttStats {
QuicTime last_update_time() const { return last_update_time_; }
- bool ignore_max_ack_delay() const { return ignore_max_ack_delay_; }
-
- void set_ignore_max_ack_delay(bool ignore_max_ack_delay) {
- ignore_max_ack_delay_ = ignore_max_ack_delay;
- }
-
void EnableStandardDeviationCalculation() {
calculate_standard_deviation_ = true;
}
@@ -130,8 +124,6 @@ class QUIC_EXPORT_PRIVATE RttStats {
bool calculate_standard_deviation_;
QuicTime::Delta initial_rtt_;
QuicTime last_update_time_;
- // Whether to ignore the peer's max ack delay.
- bool ignore_max_ack_delay_;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc
index bdf54c611fc..1df509f0f7c 100644
--- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc
@@ -56,34 +56,6 @@ TEST_F(RttStatsTest, SmoothedRtt) {
rtt_stats_.smoothed_rtt());
}
-TEST_F(RttStatsTest, SmoothedRttIgnoreAckDelay) {
- rtt_stats_.set_ignore_max_ack_delay(true);
- // Verify that ack_delay is ignored in the first measurement.
- rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300),
- QuicTime::Delta::FromMilliseconds(100),
- QuicTime::Zero());
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt());
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt());
- // Verify that a plausible ack delay increases the max ack delay.
- rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300),
- QuicTime::Delta::FromMilliseconds(100),
- QuicTime::Zero());
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt());
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt());
- // Verify that Smoothed RTT includes max ack delay if it's reasonable.
- rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300),
- QuicTime::Delta::FromMilliseconds(50), QuicTime::Zero());
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.latest_rtt());
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300), rtt_stats_.smoothed_rtt());
- // Verify that large erroneous ack_delay does not change Smoothed RTT.
- rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(200),
- QuicTime::Delta::FromMilliseconds(300),
- QuicTime::Zero());
- EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt());
- EXPECT_EQ(QuicTime::Delta::FromMicroseconds(287500),
- rtt_stats_.smoothed_rtt());
-}
-
// Ensure that the potential rounding artifacts in EWMA calculation do not cause
// the SRTT to drift too far from the exact value.
TEST_F(RttStatsTest, SmoothedRttStability) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc
index 6dcb989b9e8..b591becf4f9 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc
@@ -16,7 +16,6 @@
#include "quic/core/crypto/crypto_utils.h"
#include "quic/core/quic_socket_address_coder.h"
#include "quic/core/quic_utils.h"
-#include "quic/platform/api/quic_map_util.h"
#include "common/quiche_endian.h"
namespace quic {
@@ -171,7 +170,7 @@ bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag,
}
bool CryptoHandshakeMessage::HasStringPiece(QuicTag tag) const {
- return QuicContainsKey(tag_value_map_, tag);
+ return tag_value_map_.find(tag) != tag_value_map_.end();
}
QuicErrorCode CryptoHandshakeMessage::GetNthValue24(
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h
index af62865b653..d594f487549 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h
@@ -28,7 +28,7 @@ using ServerConfigID = std::string;
// The following tags have been deprecated and should not be reused:
// "1CON", "BBQ4", "NCON", "RCID", "SREJ", "TBKP", "TB10", "SCLS", "SMHL",
-// "QNZR", "B2HI", "H2PR", "FIFO", "LIFO", "RRWS", "QNSP"
+// "QNZR", "B2HI", "H2PR", "FIFO", "LIFO", "RRWS", "QNSP", "B2CL"
// clang-format off
const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello
@@ -133,9 +133,6 @@ const QuicTag kB2NE = TAG('B', '2', 'N', 'E'); // For BBRv2, always exit
// threshold.
const QuicTag kB2RP = TAG('B', '2', 'R', 'P'); // For BBRv2, run PROBE_RTT on
// the regular schedule
-const QuicTag kB2CL = TAG('B', '2', 'C', 'L'); // For BBRv2, allow PROBE_BW
- // cwnd to be below BDP + ack
- // height.
const QuicTag kB2LO = TAG('B', '2', 'L', 'O'); // Ignore inflight_lo in BBR2
const QuicTag kB2HR = TAG('B', '2', 'H', 'R'); // 15% inflight_hi headroom.
const QuicTag kB2SL = TAG('B', '2', 'S', 'L'); // When exiting STARTUP due to
@@ -344,6 +341,8 @@ const QuicTag kMTUL = TAG('M', 'T', 'U', 'L'); // Low-target MTU discovery.
const QuicTag kNSLC = TAG('N', 'S', 'L', 'C'); // Always send connection close
// for idle timeout.
const QuicTag kCHSP = TAG('C', 'H', 'S', 'P'); // Chaos protection.
+const QuicTag kBPTE = TAG('B', 'P', 'T', 'E'); // BoringSSL Permutes
+ // TLS Extensions.
// Proof types (i.e. certificate types)
// NOTE: although it would be silly to do so, specifying both kX509 and kX59R
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc
index 7fadbbdcef7..753bd603f07 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc
@@ -59,21 +59,19 @@ const char kOldConfigId[] = "old-config-id";
struct TestParams {
friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
os << " versions: "
- << ParsedQuicVersionVectorToString(p.supported_versions)
- << " } allow_sni_without_dots: " << p.allow_sni_without_dots;
+ << ParsedQuicVersionVectorToString(p.supported_versions) << " }";
return os;
}
// Versions supported by client and server.
ParsedQuicVersionVector supported_versions;
- bool allow_sni_without_dots;
};
// Used by ::testing::PrintToStringParamName().
std::string PrintToString(const TestParams& p) {
std::string rv = ParsedQuicVersionVectorToString(p.supported_versions);
std::replace(rv.begin(), rv.end(), ',', '_');
- return absl::StrCat(rv, "_allow_sni_without_dots_", p.allow_sni_without_dots);
+ return rv;
}
// Constructs various test permutations.
@@ -83,9 +81,7 @@ std::vector<TestParams> GetTestParams() {
// Start with all versions, remove highest on each iteration.
ParsedQuicVersionVector supported_versions = AllSupportedVersions();
while (!supported_versions.empty()) {
- for (bool allow_sni_without_dots : {false, true}) {
- params.push_back({supported_versions, allow_sni_without_dots});
- }
+ params.push_back({supported_versions});
supported_versions.erase(supported_versions.begin());
}
@@ -109,8 +105,6 @@ class CryptoServerTest : public QuicTestWithParam<TestParams> {
signed_config_(new QuicSignedServerConfig),
chlo_packet_size_(kDefaultMaxPacketSize) {
supported_versions_ = GetParam().supported_versions;
- SetQuicReloadableFlag(quic_and_tls_allow_sni_without_dots,
- GetParam().allow_sni_without_dots);
config_.set_enable_serving_sct(true);
client_version_ = supported_versions_.front();
@@ -387,9 +381,6 @@ TEST_P(CryptoServerTest, BadSNI) {
"127.0.0.1",
"ffee::1",
};
- if (!GetParam().allow_sni_without_dots) {
- badSNIs.push_back("foo");
- }
// clang-format on
for (const std::string& bad_sni : badSNIs) {
@@ -402,12 +393,11 @@ TEST_P(CryptoServerTest, BadSNI) {
CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
}
- if (GetParam().allow_sni_without_dots) {
- CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
- {{"PDMD", "X509"}, {"SNI", "foo"}, {"VER\0", client_version_string_}},
- kClientHelloMinimumSize);
- ShouldSucceed(msg);
- }
+ // Check that SNIs without dots are allowed
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"SNI", "foo"}, {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+ ShouldSucceed(msg);
}
TEST_P(CryptoServerTest, DefaultCert) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc
index 67527d45826..9c12d8cf309 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc
@@ -713,35 +713,15 @@ const char* CryptoUtils::HandshakeFailureReasonToString(
return "INVALID_HANDSHAKE_FAILURE_REASON";
}
+#undef RETURN_STRING_LITERAL // undef for jumbo builds
+
// static
std::string CryptoUtils::EarlyDataReasonToString(
ssl_early_data_reason_t reason) {
-#if BORINGSSL_API_VERSION >= 12
const char* reason_string = SSL_early_data_reason_string(reason);
if (reason_string != nullptr) {
return std::string("ssl_early_data_") + reason_string;
}
-#else
- // TODO(davidben): Remove this logic once
- // https://boringssl-review.googlesource.com/c/boringssl/+/43724 has landed in
- // downstream repositories.
- switch (reason) {
- RETURN_STRING_LITERAL(ssl_early_data_unknown);
- RETURN_STRING_LITERAL(ssl_early_data_disabled);
- RETURN_STRING_LITERAL(ssl_early_data_accepted);
- RETURN_STRING_LITERAL(ssl_early_data_protocol_version);
- RETURN_STRING_LITERAL(ssl_early_data_peer_declined);
- RETURN_STRING_LITERAL(ssl_early_data_no_session_offered);
- RETURN_STRING_LITERAL(ssl_early_data_session_not_resumed);
- RETURN_STRING_LITERAL(ssl_early_data_unsupported_for_session);
- RETURN_STRING_LITERAL(ssl_early_data_hello_retry_request);
- RETURN_STRING_LITERAL(ssl_early_data_alpn_mismatch);
- RETURN_STRING_LITERAL(ssl_early_data_channel_id);
- RETURN_STRING_LITERAL(ssl_early_data_token_binding);
- RETURN_STRING_LITERAL(ssl_early_data_ticket_age_skew);
- RETURN_STRING_LITERAL(ssl_early_data_quic_parameter_mismatch);
- }
-#endif
QUIC_BUG_IF(quic_bug_12871_3,
reason < 0 || reason > ssl_early_data_reason_max_value)
<< "Unknown ssl_early_data_reason_t " << reason;
@@ -761,8 +741,6 @@ std::string CryptoUtils::HashHandshakeMessage(
return output;
}
-#undef RETURN_STRING_LITERAL // undef for jumbo builds
-
// static
bool CryptoUtils::GetSSLCapabilities(const SSL* ssl,
bssl::UniquePtr<uint8_t>* capabilities,
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc
index c4e66c80e73..c97d5ddb512 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc
@@ -31,10 +31,8 @@ std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange(
switch (type) {
case kC255:
return Curve25519KeyExchange::New(rand);
- break;
case kP256:
return P256KeyExchange::New();
- break;
default:
QUIC_BUG(quic_bug_10712_2)
<< "Unknown key exchange method: " << QuicTagToString(type);
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h
index 31cbfd66114..b0a24468a23 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h
@@ -166,6 +166,15 @@ class QUIC_EXPORT_PRIVATE ProofSource {
absl::string_view in,
std::unique_ptr<SignatureCallback> callback) = 0;
+ // Return the list of TLS signature algorithms that is acceptable by the
+ // ComputeTlsSignature method. If the entire BoringSSL's default list of
+ // supported signature algorithms are acceptable, return an empty list.
+ //
+ // If returns a non-empty list, ComputeTlsSignature will only be called with a
+ // algorithm in the list.
+ virtual absl::InlinedVector<uint16_t, 8> SupportedTlsSignatureAlgorithms()
+ const = 0;
+
class QUIC_EXPORT_PRIVATE DecryptCallback {
public:
DecryptCallback() = default;
@@ -197,7 +206,12 @@ class QUIC_EXPORT_PRIVATE ProofSource {
// returns the encrypted ticket. The resulting value must not be larger than
// MaxOverhead bytes larger than |in|. If encryption fails, this method
// returns an empty vector.
- virtual std::vector<uint8_t> Encrypt(absl::string_view in) = 0;
+ //
+ // If |encryption_key| is nonempty, this method should use it for minting
+ // TLS resumption tickets. If it is empty, this method may use an
+ // internally cached encryption key, if available.
+ virtual std::vector<uint8_t> Encrypt(absl::string_view in,
+ absl::string_view encryption_key) = 0;
// Decrypt takes an encrypted ticket |in|, decrypts it, and calls
// |callback->Run| with the decrypted ticket, which must not be larger than
@@ -231,13 +245,15 @@ class QUIC_EXPORT_PRIVATE ProofSourceHandleCallback {
// |chain| the certificate chain in leaf-first order.
// |handshake_hints| (optional) handshake hints that can be used by
// SSL_set_handshake_hints.
+ // |ticket_encryption_key| (optional) encryption key to be used for minting
+ // TLS resumption tickets.
//
// When called asynchronously(is_sync=false), this method will be responsible
// to continue the handshake from where it left off.
- virtual void OnSelectCertificateDone(bool ok,
- bool is_sync,
- const ProofSource::Chain* chain,
- absl::string_view handshake_hints) = 0;
+ virtual void OnSelectCertificateDone(
+ bool ok, bool is_sync, const ProofSource::Chain* chain,
+ absl::string_view handshake_hints,
+ absl::string_view ticket_encryption_key) = 0;
// Called when a ProofSourceHandle::ComputeSignature operation completes.
virtual void OnComputeSignatureDone(
@@ -245,6 +261,10 @@ class QUIC_EXPORT_PRIVATE ProofSourceHandleCallback {
bool is_sync,
std::string signature,
std::unique_ptr<ProofSource::Details> details) = 0;
+
+ // Return true iff ProofSourceHandle::ComputeSignature won't be called later.
+ // The handle can use this function to release resources promptly.
+ virtual bool WillNotCallComputeSignature() const = 0;
};
// ProofSourceHandle is an interface by which a TlsServerHandshaker can obtain
@@ -264,13 +284,17 @@ class QUIC_EXPORT_PRIVATE ProofSourceHandle {
public:
virtual ~ProofSourceHandle() = default;
- // Cancel the pending operation, if any.
- // Once called, any completion method on |callback()| won't be invoked.
- virtual void CancelPendingOperation() = 0;
+ // Close the handle. Cancel the pending operation, if any.
+ // Once called, any completion method on |callback()| won't be invoked, and
+ // future SelectCertificate and ComputeSignature calls should return failure.
+ virtual void CloseHandle() = 0;
// Starts a select certificate operation. If the operation is not cancelled
// when it completes, callback()->OnSelectCertificateDone will be invoked.
//
+ // server_address and client_address should be normalized by the caller before
+ // sending down to this function.
+ //
// If the operation is handled synchronously:
// - QUIC_SUCCESS or QUIC_FAILURE will be returned.
// - callback()->OnSelectCertificateDone should be invoked before the function
@@ -289,7 +313,8 @@ class QUIC_EXPORT_PRIVATE ProofSourceHandle {
const std::string& alpn,
absl::optional<std::string> alps,
const std::vector<uint8_t>& quic_transport_params,
- const absl::optional<std::vector<uint8_t>>& early_data_context) = 0;
+ const absl::optional<std::vector<uint8_t>>& early_data_context,
+ const QuicSSLConfig& ssl_config) = 0;
// Starts a compute signature operation. If the operation is not cancelled
// when it completes, callback()->OnComputeSignatureDone will be invoked.
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.cc
index d61df716c3a..c7acd0056bc 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.cc
@@ -80,6 +80,13 @@ void ProofSourceX509::ComputeTlsSignature(
callback->Run(/*ok=*/!signature.empty(), signature, nullptr);
}
+absl::InlinedVector<uint16_t, 8>
+ProofSourceX509::SupportedTlsSignatureAlgorithms() const {
+ // Let ComputeTlsSignature() report an error if a bad signature algorithm is
+ // requested.
+ return {};
+}
+
ProofSource::TicketCrypter* ProofSourceX509::GetTicketCrypter() {
return nullptr;
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.h b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.h
index 83de1de204e..9ac676939a3 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.h
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.h
@@ -41,11 +41,11 @@ class QUIC_EXPORT_PRIVATE ProofSourceX509 : public ProofSource {
const std::string& hostname) override;
void ComputeTlsSignature(
const QuicSocketAddress& server_address,
- const QuicSocketAddress& client_address,
- const std::string& hostname,
- uint16_t signature_algorithm,
- absl::string_view in,
+ const QuicSocketAddress& client_address, const std::string& hostname,
+ uint16_t signature_algorithm, absl::string_view in,
std::unique_ptr<SignatureCallback> callback) override;
+ absl::InlinedVector<uint16_t, 8> SupportedTlsSignatureAlgorithms()
+ const override;
TicketCrypter* GetTicketCrypter() override;
// Adds a certificate chain to the verifier. Returns false if the chain is
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc
index 3f94e7cda60..99f92793c0e 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc
@@ -33,7 +33,6 @@
#include "quic/platform/api/quic_client_stats.h"
#include "quic/platform/api/quic_hostname_utils.h"
#include "quic/platform/api/quic_logging.h"
-#include "quic/platform/api/quic_map_util.h"
namespace quic {
@@ -848,22 +847,23 @@ bool QuicCryptoClientConfig::PopulateFromCanonicalConfig(
QuicServerId suffix_server_id(canonical_suffixes_[i], server_id.port(),
server_id.privacy_mode_enabled());
- if (!QuicContainsKey(canonical_server_map_, suffix_server_id)) {
+ auto it = canonical_server_map_.lower_bound(suffix_server_id);
+ if (it == canonical_server_map_.end() || it->first != 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;
+ // canonical. Use |it| as position hint for faster insertion.
+ canonical_server_map_.insert(
+ it, std::make_pair(std::move(suffix_server_id), std::move(server_id)));
return false;
}
- const QuicServerId& canonical_server_id =
- canonical_server_map_[suffix_server_id];
+ const QuicServerId& canonical_server_id = it->second;
CachedState* canonical_state = cached_states_[canonical_server_id].get();
if (!canonical_state->proof_valid()) {
return false;
}
// Update canonical version to point at the "most recent" entry.
- canonical_server_map_[suffix_server_id] = server_id;
+ it->second = server_id;
server_state->InitializeFrom(*canonical_state);
return true;
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h
index bb0626094db..847fc96ebe6 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h
@@ -362,6 +362,14 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// handshake message.
const std::string& user_agent_id() const { return user_agent_id_; }
+ void set_tls_signature_algorithms(std::string signature_algorithms) {
+ tls_signature_algorithms_ = std::move(signature_algorithms);
+ }
+
+ const absl::optional<std::string>& tls_signature_algorithms() const {
+ return tls_signature_algorithms_;
+ }
+
// Saves the |alpn| that will be passed in QUIC's CHLO message.
void set_alpn(const std::string& alpn) { alpn_ = alpn; }
@@ -435,6 +443,10 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
// incorporating |pre_shared_key_| into the key schedule.
std::string pre_shared_key_;
+ // If set, configure the client to use the specified signature algorithms, via
+ // SSL_set1_sigalgs_list. TLS only.
+ absl::optional<std::string> tls_signature_algorithms_;
+
// In QUIC, technically, client hello should be fully padded.
// However, fully padding on slow network connection (e.g. 50kbps) can add
// 150ms latency to one roundtrip. Therefore, you can disable padding of
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc
index 6dd09677fb9..5bcf01cd074 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc
@@ -179,6 +179,7 @@ TEST_F(QuicCryptoClientConfigTest, InchoateChloSecureWithSCIDNoEXPY) {
QuicWallTime expiry = QuicWallTime::FromUNIXSeconds(2);
state.SetServerConfig(scfg.GetSerialized().AsStringPiece(), now, expiry,
&details);
+ EXPECT_FALSE(state.IsEmpty());
QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
@@ -205,6 +206,7 @@ TEST_F(QuicCryptoClientConfigTest, InchoateChloSecureWithSCID) {
state.SetServerConfig(scfg.GetSerialized().AsStringPiece(),
QuicWallTime::FromUNIXSeconds(1),
QuicWallTime::FromUNIXSeconds(0), &details);
+ EXPECT_FALSE(state.IsEmpty());
QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
@@ -503,5 +505,46 @@ TEST_F(QuicCryptoClientConfigTest, ServerNonceinSHLO) {
EXPECT_EQ("server hello missing server nonce", error_details);
}
+// Test that PopulateFromCanonicalConfig() handles the case of multiple entries
+// in |canonical_server_map_|.
+TEST_F(QuicCryptoClientConfigTest, MultipleCanonicalEntries) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting());
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_server_id1("www.google.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state1 =
+ config.LookupOrCreate(canonical_server_id1);
+
+ CryptoHandshakeMessage scfg;
+ scfg.set_tag(kSCFG);
+ scfg.SetStringPiece(kSCID, "12345678");
+ std::string details;
+ QuicWallTime now = QuicWallTime::FromUNIXSeconds(1);
+ QuicWallTime expiry = QuicWallTime::FromUNIXSeconds(2);
+ state1->SetServerConfig(scfg.GetSerialized().AsStringPiece(), now, expiry,
+ &details);
+ state1->set_source_address_token("TOKEN");
+ state1->SetProofValid();
+ EXPECT_FALSE(state1->IsEmpty());
+
+ // This will have the same |suffix_server_id| as |canonical_server_id1|,
+ // therefore |*state2| will be initialized from |*state1|.
+ QuicServerId canonical_server_id2("mail.google.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state2 =
+ config.LookupOrCreate(canonical_server_id2);
+ EXPECT_FALSE(state2->IsEmpty());
+ const CryptoHandshakeMessage* const scfg2 = state2->GetServerConfig();
+ ASSERT_TRUE(scfg2);
+ EXPECT_EQ(kSCFG, scfg2->tag());
+
+ // With a different |suffix_server_id|, this will return an empty CachedState.
+ config.AddCanonicalSuffix(".example.com");
+ QuicServerId canonical_server_id3("www.example.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state3 =
+ config.LookupOrCreate(canonical_server_id3);
+ EXPECT_TRUE(state3->IsEmpty());
+ const CryptoHandshakeMessage* const scfg3 = state3->GetServerConfig();
+ EXPECT_FALSE(scfg3);
+}
+
} // namespace test
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h
index 4f3ad0819e4..15ce0f276d7 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h
@@ -507,9 +507,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig {
// one-to-one, with the tags in |kexs| from the parent class.
std::vector<std::unique_ptr<AsynchronousKeyExchange>> key_exchanges;
- // tag_value_map contains the raw key/value pairs for the config.
- QuicTagValueMap tag_value_map;
-
// channel_id_enabled is true if the config in |serialized| specifies that
// ChannelIDs are supported.
bool channel_id_enabled;
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.cc
index 56657b507f9..d3190c626e7 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.cc
@@ -19,17 +19,22 @@ namespace {
// xoshiro256++ 1.0 based on code in the public domain from
// <http://prng.di.unimi.it/xoshiro256plusplus.c>.
+inline uint64_t Xoshiro256InitializeRngStateMember() {
+ uint64_t result;
+ RAND_bytes(reinterpret_cast<uint8_t*>(&result), sizeof(result));
+ return result;
+}
+
inline uint64_t Xoshiro256PlusPlusRotLeft(uint64_t x, int k) {
return (x << k) | (x >> (64 - k));
}
uint64_t Xoshiro256PlusPlus() {
- static thread_local uint64_t rng_state[4];
- static thread_local bool rng_state_initialized = false;
- if (QUIC_PREDICT_FALSE(!rng_state_initialized)) {
- RAND_bytes(reinterpret_cast<uint8_t*>(&rng_state), sizeof(rng_state));
- rng_state_initialized = true;
- }
+ static thread_local uint64_t rng_state[4] = {
+ Xoshiro256InitializeRngStateMember(),
+ Xoshiro256InitializeRngStateMember(),
+ Xoshiro256InitializeRngStateMember(),
+ Xoshiro256InitializeRngStateMember()};
const uint64_t result =
Xoshiro256PlusPlusRotLeft(rng_state[0] + rng_state[3], 23) + rng_state[0];
const uint64_t t = rng_state[1] << 17;
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.cc
index c6a45c4b834..dd53ee7037c 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.cc
@@ -6,8 +6,12 @@
namespace quic {
-TlsClientConnection::TlsClientConnection(SSL_CTX* ssl_ctx, Delegate* delegate)
- : TlsConnection(ssl_ctx, delegate->ConnectionDelegate()),
+TlsClientConnection::TlsClientConnection(SSL_CTX* ssl_ctx,
+ Delegate* delegate,
+ QuicSSLConfig ssl_config)
+ : TlsConnection(ssl_ctx,
+ delegate->ConnectionDelegate(),
+ std::move(ssl_config)),
delegate_(delegate) {}
// static
@@ -24,6 +28,8 @@ bssl::UniquePtr<SSL_CTX> TlsClientConnection::CreateSslCtx(
ssl_ctx.get(), SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
+ // TODO(wub): Always enable early data on the SSL_CTX, but allow it to be
+ // overridden on the SSL object, via QuicSSLConfig.
SSL_CTX_set_early_data_enabled(ssl_ctx.get(), enable_early_data);
return ssl_ctx;
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.h b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.h
index c9471536b67..ce4b94804a6 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.h
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.h
@@ -30,7 +30,9 @@ class QUIC_EXPORT_PRIVATE TlsClientConnection : public TlsConnection {
friend class TlsClientConnection;
};
- TlsClientConnection(SSL_CTX* ssl_ctx, Delegate* delegate);
+ TlsClientConnection(SSL_CTX* ssl_ctx,
+ Delegate* delegate,
+ QuicSSLConfig ssl_config);
// Creates and configures an SSL_CTX that is appropriate for clients to use.
static bssl::UniquePtr<SSL_CTX> CreateSslCtx(bool enable_early_data);
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.cc
index 381de7eeafb..7a66e2f8f8d 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.cc
@@ -5,6 +5,7 @@
#include "quic/core/crypto/tls_connection.h"
#include "absl/strings/string_view.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
#include "quic/platform/api/quic_bug_tracker.h"
namespace quic {
@@ -88,12 +89,32 @@ enum ssl_encryption_level_t TlsConnection::BoringEncryptionLevel(
}
TlsConnection::TlsConnection(SSL_CTX* ssl_ctx,
- TlsConnection::Delegate* delegate)
- : delegate_(delegate), ssl_(SSL_new(ssl_ctx)) {
+ TlsConnection::Delegate* delegate,
+ QuicSSLConfig ssl_config)
+ : delegate_(delegate),
+ ssl_(SSL_new(ssl_ctx)),
+ ssl_config_(std::move(ssl_config)) {
SSL_set_ex_data(
ssl(), SslIndexSingleton::GetInstance()->ssl_ex_data_index_connection(),
this);
+ if (ssl_config_.early_data_enabled.has_value()) {
+ const int early_data_enabled = *ssl_config_.early_data_enabled ? 1 : 0;
+ SSL_set_early_data_enabled(ssl(), early_data_enabled);
+ }
+ if (ssl_config_.signing_algorithm_prefs.has_value()) {
+ SSL_set_signing_algorithm_prefs(
+ ssl(), ssl_config_.signing_algorithm_prefs->data(),
+ ssl_config_.signing_algorithm_prefs->size());
+ }
}
+
+void TlsConnection::EnableInfoCallback() {
+ SSL_set_info_callback(
+ ssl(), +[](const SSL* ssl, int type, int value) {
+ ConnectionFromSsl(ssl)->delegate_->InfoCallback(type, value);
+ });
+}
+
// static
bssl::UniquePtr<SSL_CTX> TlsConnection::CreateSslCtx(int cert_verify_mode) {
CRYPTO_library_init();
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h
index 28b5684ad4a..f59eaa13150 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h
@@ -75,12 +75,21 @@ class QUIC_EXPORT_PRIVATE TlsConnection {
// level |level|.
virtual void SendAlert(EncryptionLevel level, uint8_t desc) = 0;
+ // Informational callback from BoringSSL. This callback is disabled by
+ // default, but can be enabled by TlsConnection::EnableInfoCallback.
+ //
+ // See |SSL_CTX_set_info_callback| for the meaning of |type| and |value|.
+ virtual void InfoCallback(int type, int value) = 0;
+
friend class TlsConnection;
};
TlsConnection(const TlsConnection&) = delete;
TlsConnection& operator=(const TlsConnection&) = delete;
+ // Configure the SSL such that delegate_->InfoCallback will be called.
+ void EnableInfoCallback();
+
// Functions to convert between BoringSSL's enum ssl_encryption_level_t and
// QUIC's EncryptionLevel.
static EncryptionLevel QuicEncryptionLevel(enum ssl_encryption_level_t level);
@@ -89,10 +98,12 @@ class QUIC_EXPORT_PRIVATE TlsConnection {
SSL* ssl() const { return ssl_.get(); }
+ const QuicSSLConfig& ssl_config() const { return ssl_config_; }
+
protected:
- // TlsConnection does not take ownership of any of its arguments; they must
+ // TlsConnection does not take ownership of |ssl_ctx| or |delegate|; they must
// outlive the TlsConnection object.
- TlsConnection(SSL_CTX* ssl_ctx, Delegate* delegate);
+ TlsConnection(SSL_CTX* ssl_ctx, Delegate* delegate, QuicSSLConfig ssl_config);
// Creates an SSL_CTX and configures it with the options that are appropriate
// for both client and server. The caller is responsible for ownership of the
@@ -141,6 +152,7 @@ class QUIC_EXPORT_PRIVATE TlsConnection {
Delegate* delegate_;
bssl::UniquePtr<SSL> ssl_;
+ const QuicSSLConfig ssl_config_;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc
index 6e9901b995e..2042c15aaf2 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc
@@ -12,8 +12,12 @@
namespace quic {
-TlsServerConnection::TlsServerConnection(SSL_CTX* ssl_ctx, Delegate* delegate)
- : TlsConnection(ssl_ctx, delegate->ConnectionDelegate()),
+TlsServerConnection::TlsServerConnection(SSL_CTX* ssl_ctx,
+ Delegate* delegate,
+ QuicSSLConfig ssl_config)
+ : TlsConnection(ssl_ctx,
+ delegate->ConnectionDelegate(),
+ std::move(ssl_config)),
delegate_(delegate) {}
// static
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.h b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.h
index 774bb440f64..6c775b86cd7 100644
--- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.h
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.h
@@ -120,7 +120,9 @@ class QUIC_EXPORT_PRIVATE TlsServerConnection : public TlsConnection {
friend class TlsServerConnection;
};
- TlsServerConnection(SSL_CTX* ssl_ctx, Delegate* delegate);
+ TlsServerConnection(SSL_CTX* ssl_ctx,
+ Delegate* delegate,
+ QuicSSLConfig ssl_config);
// Creates and configures an SSL_CTX that is appropriate for servers to use.
static bssl::UniquePtr<SSL_CTX> CreateSslCtx(ProofSource* proof_source);
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc
index ad10d601c31..c31b45e322e 100644
--- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc
@@ -391,11 +391,9 @@ QuicFrame CopyQuicFrame(QuicBufferAllocator* allocator,
copy.message_frame->data = frame.message_frame->data;
copy.message_frame->message_length = frame.message_frame->message_length;
for (const auto& slice : frame.message_frame->message_data) {
- QuicUniqueBufferPtr buffer =
- MakeUniqueBuffer(allocator, slice.length());
- memcpy(buffer.get(), slice.data(), slice.length());
+ QuicBuffer buffer = QuicBuffer::Copy(allocator, slice.AsStringView());
copy.message_frame->message_data.push_back(
- QuicMemSlice(std::move(buffer), slice.length()));
+ QuicMemSlice(std::move(buffer)));
}
break;
case NEW_TOKEN_FRAME:
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h
index 1efe1e662d7..9ee45689117 100644
--- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h
@@ -9,6 +9,7 @@
#include <type_traits>
#include <vector>
+#include "absl/container/inlined_vector.h"
#include "quic/core/frames/quic_ack_frame.h"
#include "quic/core/frames/quic_ack_frequency_frame.h"
#include "quic/core/frames/quic_blocked_frame.h"
@@ -133,7 +134,7 @@ static_assert(offsetof(QuicStreamFrame, type) == offsetof(QuicFrame, type),
// A inline size of 1 is chosen to optimize the typical use case of
// 1-stream-frame in QuicTransmissionInfo.retransmittable_frames.
-using QuicFrames = QuicInlinedVector<QuicFrame, 1>;
+using QuicFrames = absl::InlinedVector<QuicFrame, 1>;
// Deletes all the sub-frames contained in |frames|.
QUIC_EXPORT_PRIVATE void DeleteFrames(QuicFrames* frames);
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc
index c11be9eb016..038924e2165 100644
--- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc
@@ -543,10 +543,8 @@ TEST_F(QuicFramesTest, RemoveSmallestInterval) {
TEST_F(QuicFramesTest, CopyQuicFrames) {
QuicFrames frames;
- SimpleBufferAllocator allocator;
- QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
QuicMessageFrame* message_frame =
- new QuicMessageFrame(1, MakeSpan(&allocator, "message", &storage));
+ new QuicMessageFrame(1, MemSliceFromString("message"));
// Construct a frame list.
for (uint8_t i = 0; i < NUM_FRAME_TYPES; ++i) {
switch (i) {
@@ -626,7 +624,7 @@ TEST_F(QuicFramesTest, CopyQuicFrames) {
}
}
- QuicFrames copy = CopyQuicFrames(&allocator, frames);
+ QuicFrames copy = CopyQuicFrames(SimpleBufferAllocator::Get(), frames);
ASSERT_EQ(NUM_FRAME_TYPES, copy.size());
for (uint8_t i = 0; i < NUM_FRAME_TYPES; ++i) {
EXPECT_EQ(i, copy[i].type);
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.cc
index 054303ac1ee..1ac0de67035 100644
--- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.cc
@@ -14,13 +14,18 @@ QuicMessageFrame::QuicMessageFrame(QuicMessageId message_id)
: message_id(message_id), data(nullptr), message_length(0) {}
QuicMessageFrame::QuicMessageFrame(QuicMessageId message_id,
- QuicMemSliceSpan span)
+ absl::Span<QuicMemSlice> span)
: message_id(message_id), data(nullptr), message_length(0) {
- span.ConsumeAll([&](QuicMemSlice slice) {
+ for (QuicMemSlice& slice : span) {
+ if (slice.empty()) {
+ continue;
+ }
message_length += slice.length();
message_data.push_back(std::move(slice));
- });
+ }
}
+QuicMessageFrame::QuicMessageFrame(QuicMessageId message_id, QuicMemSlice slice)
+ : QuicMessageFrame(message_id, absl::MakeSpan(&slice, 1)) {}
QuicMessageFrame::QuicMessageFrame(const char* data, QuicPacketLength length)
: message_id(0), data(data), message_length(length) {}
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.h
index ef1e070deb5..1d090c902e1 100644
--- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.h
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.h
@@ -5,20 +5,22 @@
#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_MESSAGE_FRAME_H_
#define QUICHE_QUIC_CORE_FRAMES_QUIC_MESSAGE_FRAME_H_
+#include "absl/container/inlined_vector.h"
+#include "absl/types/span.h"
#include "quic/core/quic_types.h"
#include "quic/platform/api/quic_containers.h"
#include "quic/platform/api/quic_export.h"
#include "quic/platform/api/quic_mem_slice.h"
-#include "quic/platform/api/quic_mem_slice_span.h"
namespace quic {
-using QuicMessageData = QuicInlinedVector<QuicMemSlice, 1>;
+using QuicMessageData = absl::InlinedVector<QuicMemSlice, 1>;
struct QUIC_EXPORT_PRIVATE QuicMessageFrame {
QuicMessageFrame() = default;
explicit QuicMessageFrame(QuicMessageId message_id);
- QuicMessageFrame(QuicMessageId message_id, QuicMemSliceSpan span);
+ QuicMessageFrame(QuicMessageId message_id, absl::Span<QuicMemSlice> span);
+ QuicMessageFrame(QuicMessageId message_id, QuicMemSlice slice);
QuicMessageFrame(const char* data, QuicPacketLength length);
QuicMessageFrame(const QuicMessageFrame& other) = delete;
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h
index 9c3d2053d34..235d2af792b 100644
--- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h
@@ -32,7 +32,7 @@ struct QUIC_EXPORT_PRIVATE QuicNewConnectionIdFrame {
QuicConnectionId connection_id = EmptyQuicConnectionId();
QuicConnectionIdSequenceNumber sequence_number = 0;
StatelessResetToken stateless_reset_token;
- uint64_t retire_prior_to;
+ uint64_t retire_prior_to = 0;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h
index 7c27a4258b3..6966775a30d 100644
--- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h
@@ -27,7 +27,7 @@ struct QUIC_EXPORT_PRIVATE QuicPathChallengeFrame {
// and non-zero when sent.
QuicControlFrameId control_frame_id = kInvalidControlFrameId;
- QuicPathFrameBuffer data_buffer;
+ QuicPathFrameBuffer data_buffer{};
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.h
index 6b43bda16ae..a4a75be208b 100644
--- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.h
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.h
@@ -27,7 +27,7 @@ struct QUIC_EXPORT_PRIVATE QuicPathResponseFrame {
// and non-zero when sent.
QuicControlFrameId control_frame_id = kInvalidControlFrameId;
- QuicPathFrameBuffer data_buffer;
+ QuicPathFrameBuffer data_buffer{};
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_streams_blocked_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_streams_blocked_frame.h
index 94676fb00da..2d738901776 100644
--- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_streams_blocked_frame.h
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_streams_blocked_frame.h
@@ -35,7 +35,7 @@ struct QUIC_EXPORT_PRIVATE QuicStreamsBlockedFrame
QuicControlFrameId control_frame_id = kInvalidControlFrameId;
// The number of streams that the sender wishes to exceed
- QuicStreamCount stream_count;
+ QuicStreamCount stream_count = 0;
// Whether uni- or bi-directional streams
bool unidirectional = false;
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h
index e4f2a86eac7..372a4ea598e 100644
--- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h
@@ -31,11 +31,11 @@ struct QUIC_EXPORT_PRIVATE QuicWindowUpdateFrame {
// The stream this frame applies to. 0 is a special case meaning the overall
// connection rather than a specific stream.
- QuicStreamId stream_id;
+ QuicStreamId stream_id = 0;
// Maximum data allowed in the stream or connection. The receiver of this
// frame must not send data which would exceedes this restriction.
- QuicByteCount max_data;
+ QuicByteCount max_data = 0;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc
index 8efba8869bf..e64d8e62581 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc
@@ -363,7 +363,6 @@ class EndToEndTest : public QuicTestWithParam<TestParams> {
bool Initialize() {
if (enable_web_transport_) {
- SetQuicReloadableFlag(quic_h3_datagram, true);
memory_cache_backend_.set_enable_webtransport(true);
}
@@ -1883,11 +1882,6 @@ TEST_P(EndToEndTest, RetransmissionAfterZeroRTTRejectBeforeOneRtt) {
ON_CALL(visitor, OnZeroRttRejected(_)).WillByDefault(Invoke([this]() {
EXPECT_FALSE(GetClientSession()->IsEncryptionEstablished());
- if (!GetQuicReloadableFlag(quic_donot_write_mid_packet_processing)) {
- // Trigger an OnCanWrite() to make sure no unencrypted data will be
- // written.
- GetClientSession()->OnCanWrite();
- }
}));
// The 0-RTT handshake should fail.
@@ -3882,7 +3876,7 @@ TEST_P(EndToEndTest, BadPacketHeaderFlags) {
SendSynchronousFooRequestAndCheckResponse();
// Packet with invalid public flags.
- char packet[] = {
+ uint8_t packet[] = {
// invalid public flags
0xFF,
// connection_id
@@ -3905,7 +3899,7 @@ TEST_P(EndToEndTest, BadPacketHeaderFlags) {
0x00,
};
client_writer_->WritePacket(
- &packet[0], sizeof(packet),
+ reinterpret_cast<const char*>(packet), sizeof(packet),
client_->client()->network_helper()->GetLatestClientAddress().host(),
server_address_, nullptr);
@@ -4739,20 +4733,15 @@ TEST_P(EndToEndTest, SendMessages) {
ASSERT_LT(0, client_session->GetCurrentLargestMessagePayload());
std::string message_string(kMaxOutgoingPacketSize, 'a');
- absl::string_view message_buffer(message_string);
QuicRandom* random =
QuicConnectionPeer::GetHelper(client_connection)->GetRandomGenerator();
- QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
{
QuicConnection::ScopedPacketFlusher flusher(client_session->connection());
// Verify the largest message gets successfully sent.
EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 1),
- client_session->SendMessage(MakeSpan(
- client_connection->helper()->GetStreamSendBufferAllocator(),
- absl::string_view(
- message_buffer.data(),
- client_session->GetCurrentLargestMessagePayload()),
- &storage)));
+ client_session->SendMessage(MemSliceFromString(absl::string_view(
+ message_string.data(),
+ client_session->GetCurrentLargestMessagePayload()))));
// Send more messages with size (0, largest_payload] until connection is
// write blocked.
const int kTestMaxNumberOfMessages = 100;
@@ -4761,9 +4750,8 @@ TEST_P(EndToEndTest, SendMessages) {
random->RandUint64() %
client_session->GetGuaranteedLargestMessagePayload() +
1;
- MessageResult result = client_session->SendMessage(MakeSpan(
- client_connection->helper()->GetStreamSendBufferAllocator(),
- absl::string_view(message_buffer.data(), message_length), &storage));
+ MessageResult result = client_session->SendMessage(MemSliceFromString(
+ absl::string_view(message_string.data(), message_length)));
if (result.status == MESSAGE_STATUS_BLOCKED) {
// Connection is write blocked.
break;
@@ -4775,12 +4763,9 @@ TEST_P(EndToEndTest, SendMessages) {
client_->WaitForDelayedAcks();
EXPECT_EQ(MESSAGE_STATUS_TOO_LARGE,
client_session
- ->SendMessage(MakeSpan(
- client_connection->helper()->GetStreamSendBufferAllocator(),
- absl::string_view(
- message_buffer.data(),
- client_session->GetCurrentLargestMessagePayload() + 1),
- &storage))
+ ->SendMessage(MemSliceFromString(absl::string_view(
+ message_string.data(),
+ client_session->GetCurrentLargestMessagePayload() + 1)))
.status);
EXPECT_THAT(client_->connection_error(), IsQuicNoError());
}
@@ -5698,6 +5683,18 @@ TEST_P(EndToEndTest, ChaosProtectionWithMultiPacketChlo) {
SendSynchronousFooRequestAndCheckResponse();
}
+TEST_P(EndToEndTest, PermuteTlsExtensions) {
+ if (!version_.UsesTls()) {
+ ASSERT_TRUE(Initialize());
+ return;
+ }
+ // Enable TLS extension permutation and perform an HTTP request.
+ client_config_.SetClientConnectionOptions(QuicTagVector{kBPTE});
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(GetClientSession()->permutes_tls_extensions());
+ SendSynchronousFooRequestAndCheckResponse();
+}
+
TEST_P(EndToEndTest, KeyUpdateInitiatedByClient) {
if (!version_.UsesTls()) {
// Key Update is only supported in TLS handshake.
@@ -6043,6 +6040,7 @@ TEST_P(EndToEndTest, WebTransportSessionSetup) {
WebTransportHttp3* web_transport =
CreateWebTransportSession("/echo", /*wait_for_server_response=*/true);
+ ASSERT_NE(web_transport, nullptr);
server_thread_->Pause();
QuicSpdySession* server_session = GetServerSession();
@@ -6064,6 +6062,7 @@ TEST_P(EndToEndTest, WebTransportSessionWithLoss) {
WebTransportHttp3* web_transport =
CreateWebTransportSession("/echo", /*wait_for_server_response=*/true);
+ ASSERT_NE(web_transport, nullptr);
server_thread_->Pause();
QuicSpdySession* server_session = GetServerSession();
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc
index 6d9290a12c1..de86955739b 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc
@@ -33,20 +33,9 @@ HttpDecoder::HttpDecoder(Visitor* visitor, Options options)
remaining_frame_length_(0),
current_type_field_length_(0),
remaining_type_field_length_(0),
- current_push_id_length_(0),
- remaining_push_id_length_(0),
error_(QUIC_NO_ERROR),
- error_detail_(""),
- ignore_old_priority_update_(
- GetQuicReloadableFlag(quic_ignore_old_priority_update_frame)),
- error_on_http3_push_(GetQuicReloadableFlag(quic_error_on_http3_push)) {
+ error_detail_("") {
QUICHE_DCHECK(visitor_);
- if (ignore_old_priority_update_) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_ignore_old_priority_update_frame);
- }
- if (error_on_http3_push_) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_error_on_http3_push);
- }
}
HttpDecoder::~HttpDecoder() {}
@@ -182,17 +171,15 @@ bool HttpDecoder::ReadFrameType(QuicDataReader* reader) {
return false;
}
- if (error_on_http3_push_) {
- if (current_frame_type_ ==
- static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH)) {
- RaiseError(QUIC_HTTP_FRAME_ERROR, "CANCEL_PUSH frame received.");
- return false;
- }
- if (current_frame_type_ ==
- static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE)) {
- RaiseError(QUIC_HTTP_FRAME_ERROR, "PUSH_PROMISE frame received.");
- return false;
- }
+ if (current_frame_type_ ==
+ static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH)) {
+ RaiseError(QUIC_HTTP_FRAME_ERROR, "CANCEL_PUSH frame received.");
+ return false;
+ }
+ if (current_frame_type_ ==
+ static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE)) {
+ RaiseError(QUIC_HTTP_FRAME_ERROR, "PUSH_PROMISE frame received.");
+ return false;
}
state_ = STATE_READING_FRAME_LENGTH;
@@ -241,6 +228,10 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) {
}
if (current_frame_length_ > MaxFrameLength(current_frame_type_)) {
+ // MaxFrameLength() returns numeric_limits::max()
+ // if IsFrameBuffered() is false.
+ QUICHE_DCHECK(IsFrameBuffered());
+
RaiseError(QUIC_HTTP_FRAME_TOO_LARGE, "Frame is too large.");
return false;
}
@@ -261,41 +252,18 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) {
visitor_->OnHeadersFrameStart(header_length, current_frame_length_);
break;
case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH):
- if (error_on_http3_push_) {
- QUICHE_NOTREACHED();
- break;
- }
+ QUICHE_NOTREACHED();
break;
case static_cast<uint64_t>(HttpFrameType::SETTINGS):
continue_processing = visitor_->OnSettingsFrameStart(header_length);
break;
case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE):
- if (error_on_http3_push_) {
- QUICHE_NOTREACHED();
- break;
- }
- // This edge case needs to be handled here, because ReadFramePayload()
- // does not get called if |current_frame_length_| is zero.
- if (current_frame_length_ == 0) {
- RaiseError(QUIC_HTTP_FRAME_ERROR,
- "PUSH_PROMISE frame with empty payload.");
- return false;
- }
- continue_processing = visitor_->OnPushPromiseFrameStart(header_length);
+ QUICHE_NOTREACHED();
break;
case static_cast<uint64_t>(HttpFrameType::GOAWAY):
break;
case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID):
break;
- case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE):
- if (ignore_old_priority_update_) {
- continue_processing = visitor_->OnUnknownFrameStart(
- current_frame_type_, header_length, current_frame_length_);
- } else {
- continue_processing =
- visitor_->OnPriorityUpdateFrameStart(header_length);
- }
- break;
case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM):
continue_processing = visitor_->OnPriorityUpdateFrameStart(header_length);
break;
@@ -314,6 +282,24 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) {
return continue_processing;
}
+bool HttpDecoder::IsFrameBuffered() {
+ switch (current_frame_type_) {
+ case static_cast<uint64_t>(HttpFrameType::SETTINGS):
+ return true;
+ case static_cast<uint64_t>(HttpFrameType::GOAWAY):
+ return true;
+ case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID):
+ return true;
+ case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM):
+ return true;
+ case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH):
+ return true;
+ }
+
+ // Other defined frame types as well as unknown frames are not buffered.
+ return false;
+}
+
bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) {
QUICHE_DCHECK_NE(0u, reader->BytesRemaining());
QUICHE_DCHECK_NE(0u, remaining_frame_length_);
@@ -344,11 +330,7 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) {
break;
}
case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): {
- if (error_on_http3_push_) {
- QUICHE_NOTREACHED();
- } else {
- continue_processing = BufferOrParsePayload(reader);
- }
+ QUICHE_NOTREACHED();
break;
}
case static_cast<uint64_t>(HttpFrameType::SETTINGS): {
@@ -356,72 +338,7 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) {
break;
}
case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): {
- if (error_on_http3_push_) {
- QUICHE_NOTREACHED();
- break;
- }
- PushId push_id;
- if (current_frame_length_ == remaining_frame_length_) {
- // A new Push Promise frame just arrived.
- QUICHE_DCHECK_EQ(0u, current_push_id_length_);
- current_push_id_length_ = reader->PeekVarInt62Length();
- if (current_push_id_length_ > remaining_frame_length_) {
- RaiseError(QUIC_HTTP_FRAME_ERROR,
- "Unable to read PUSH_PROMISE push_id.");
- return false;
- }
- if (current_push_id_length_ > reader->BytesRemaining()) {
- // Not all bytes of push id is present yet, buffer push id.
- QUICHE_DCHECK_EQ(0u, remaining_push_id_length_);
- remaining_push_id_length_ = current_push_id_length_;
- BufferPushId(reader);
- break;
- }
- bool success = reader->ReadVarInt62(&push_id);
- QUICHE_DCHECK(success);
- remaining_frame_length_ -= current_push_id_length_;
- if (!visitor_->OnPushPromiseFramePushId(
- push_id, current_push_id_length_,
- current_frame_length_ - current_push_id_length_)) {
- continue_processing = false;
- current_push_id_length_ = 0;
- break;
- }
- current_push_id_length_ = 0;
- } else if (remaining_push_id_length_ > 0) {
- // Waiting for more bytes on push id.
- BufferPushId(reader);
- if (remaining_push_id_length_ != 0) {
- break;
- }
- QuicDataReader push_id_reader(push_id_buffer_.data(),
- current_push_id_length_);
-
- bool success = push_id_reader.ReadVarInt62(&push_id);
- QUICHE_DCHECK(success);
- if (!visitor_->OnPushPromiseFramePushId(
- push_id, current_push_id_length_,
- current_frame_length_ - current_push_id_length_)) {
- continue_processing = false;
- current_push_id_length_ = 0;
- break;
- }
- current_push_id_length_ = 0;
- }
-
- // Read Push Promise headers.
- QUICHE_DCHECK_LT(remaining_frame_length_, current_frame_length_);
- QuicByteCount bytes_to_read = std::min<QuicByteCount>(
- remaining_frame_length_, reader->BytesRemaining());
- if (bytes_to_read == 0) {
- break;
- }
- absl::string_view payload;
- bool success = reader->ReadStringPiece(&payload, bytes_to_read);
- QUICHE_DCHECK(success);
- QUICHE_DCHECK(!payload.empty());
- continue_processing = visitor_->OnPushPromiseFramePayload(payload);
- remaining_frame_length_ -= payload.length();
+ QUICHE_NOTREACHED();
break;
}
case static_cast<uint64_t>(HttpFrameType::GOAWAY): {
@@ -432,14 +349,6 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) {
continue_processing = BufferOrParsePayload(reader);
break;
}
- case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): {
- if (ignore_old_priority_update_) {
- continue_processing = HandleUnknownFramePayload(reader);
- } else {
- continue_processing = BufferOrParsePayload(reader);
- }
- break;
- }
case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): {
continue_processing = BufferOrParsePayload(reader);
break;
@@ -454,6 +363,17 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) {
}
}
+ if (IsFrameBuffered()) {
+ if (state_ != STATE_READING_FRAME_PAYLOAD) {
+ // BufferOrParsePayload() has advanced |state_|.
+ // TODO(bnc): Simplify state transitions.
+ QUICHE_DCHECK_EQ(STATE_READING_FRAME_TYPE, state_);
+ QUICHE_DCHECK_EQ(0u, remaining_frame_length_);
+ }
+ } else {
+ QUICHE_DCHECK(state_ == STATE_READING_FRAME_PAYLOAD);
+ }
+
// BufferOrParsePayload() may have advanced |state_|.
if (state_ == STATE_READING_FRAME_PAYLOAD && remaining_frame_length_ == 0) {
state_ = STATE_FINISH_PARSING;
@@ -477,13 +397,7 @@ bool HttpDecoder::FinishParsing(QuicDataReader* reader) {
break;
}
case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): {
- if (error_on_http3_push_) {
- QUICHE_NOTREACHED();
- } else {
- // If frame payload is not empty, FinishParsing() is skipped.
- QUICHE_DCHECK_EQ(0u, current_frame_length_);
- continue_processing = BufferOrParsePayload(reader);
- }
+ QUICHE_NOTREACHED();
break;
}
case static_cast<uint64_t>(HttpFrameType::SETTINGS): {
@@ -493,11 +407,7 @@ bool HttpDecoder::FinishParsing(QuicDataReader* reader) {
break;
}
case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): {
- if (error_on_http3_push_) {
- QUICHE_NOTREACHED();
- } else {
- continue_processing = visitor_->OnPushPromiseFrameEnd();
- }
+ QUICHE_NOTREACHED();
break;
}
case static_cast<uint64_t>(HttpFrameType::GOAWAY): {
@@ -512,16 +422,6 @@ bool HttpDecoder::FinishParsing(QuicDataReader* reader) {
continue_processing = BufferOrParsePayload(reader);
break;
}
- case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): {
- if (ignore_old_priority_update_) {
- continue_processing = visitor_->OnUnknownFrameEnd();
- } else {
- // If frame payload is not empty, FinishParsing() is skipped.
- QUICHE_DCHECK_EQ(0u, current_frame_length_);
- continue_processing = BufferOrParsePayload(reader);
- }
- break;
- }
case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): {
// If frame payload is not empty, FinishParsing() is skipped.
QUICHE_DCHECK_EQ(0u, current_frame_length_);
@@ -555,21 +455,8 @@ bool HttpDecoder::HandleUnknownFramePayload(QuicDataReader* reader) {
return visitor_->OnUnknownFramePayload(payload);
}
-void HttpDecoder::DiscardFramePayload(QuicDataReader* reader) {
- QuicByteCount bytes_to_read = std::min<QuicByteCount>(
- remaining_frame_length_, reader->BytesRemaining());
- absl::string_view payload;
- bool success = reader->ReadStringPiece(&payload, bytes_to_read);
- QUICHE_DCHECK(success);
- remaining_frame_length_ -= payload.length();
- if (remaining_frame_length_ == 0) {
- state_ = STATE_READING_FRAME_TYPE;
- current_length_field_length_ = 0;
- current_type_field_length_ = 0;
- }
-}
-
bool HttpDecoder::BufferOrParsePayload(QuicDataReader* reader) {
+ QUICHE_DCHECK(IsFrameBuffered());
QUICHE_DCHECK_EQ(current_frame_length_,
buffer_.size() + remaining_frame_length_);
@@ -615,27 +502,14 @@ bool HttpDecoder::BufferOrParsePayload(QuicDataReader* reader) {
}
bool HttpDecoder::ParseEntirePayload(QuicDataReader* reader) {
+ QUICHE_DCHECK(IsFrameBuffered());
QUICHE_DCHECK_EQ(current_frame_length_, reader->BytesRemaining());
QUICHE_DCHECK_EQ(0u, remaining_frame_length_);
switch (current_frame_type_) {
case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): {
- if (error_on_http3_push_) {
- QUICHE_NOTREACHED();
- return false;
- }
- CancelPushFrame frame;
- if (!reader->ReadVarInt62(&frame.push_id)) {
- RaiseError(QUIC_HTTP_FRAME_ERROR,
- "Unable to read CANCEL_PUSH push_id.");
- return false;
- }
- if (!reader->IsDoneReading()) {
- RaiseError(QUIC_HTTP_FRAME_ERROR,
- "Superfluous data in CANCEL_PUSH frame.");
- return false;
- }
- return visitor_->OnCancelPushFrame(frame);
+ QUICHE_NOTREACHED();
+ return false;
}
case static_cast<uint64_t>(HttpFrameType::SETTINGS): {
SettingsFrame frame;
@@ -670,21 +544,9 @@ bool HttpDecoder::ParseEntirePayload(QuicDataReader* reader) {
}
return visitor_->OnMaxPushIdFrame(frame);
}
- case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): {
- if (ignore_old_priority_update_) {
- QUICHE_NOTREACHED();
- return false;
- } else {
- PriorityUpdateFrame frame;
- if (!ParsePriorityUpdateFrame(reader, &frame)) {
- return false;
- }
- return visitor_->OnPriorityUpdateFrame(frame);
- }
- }
case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): {
PriorityUpdateFrame frame;
- if (!ParseNewPriorityUpdateFrame(reader, &frame)) {
+ if (!ParsePriorityUpdateFrame(reader, &frame)) {
return false;
}
return visitor_->OnPriorityUpdateFrame(frame);
@@ -725,19 +587,6 @@ void HttpDecoder::BufferFrameType(QuicDataReader* reader) {
remaining_type_field_length_ -= bytes_to_read;
}
-void HttpDecoder::BufferPushId(QuicDataReader* reader) {
- QUICHE_DCHECK_LE(remaining_push_id_length_, current_frame_length_);
- QuicByteCount bytes_to_read = std::min<QuicByteCount>(
- reader->BytesRemaining(), remaining_push_id_length_);
- bool success =
- reader->ReadBytes(push_id_buffer_.data() + current_push_id_length_ -
- remaining_push_id_length_,
- bytes_to_read);
- QUICHE_DCHECK(success);
- remaining_push_id_length_ -= bytes_to_read;
- remaining_frame_length_ -= bytes_to_read;
-}
-
void HttpDecoder::RaiseError(QuicErrorCode error, std::string error_detail) {
state_ = STATE_ERROR;
error_ = error;
@@ -769,36 +618,6 @@ bool HttpDecoder::ParseSettingsFrame(QuicDataReader* reader,
}
bool HttpDecoder::ParsePriorityUpdateFrame(QuicDataReader* reader,
- PriorityUpdateFrame* frame) {
- uint8_t prioritized_element_type;
- if (!reader->ReadUInt8(&prioritized_element_type)) {
- RaiseError(QUIC_HTTP_FRAME_ERROR,
- "Unable to read prioritized element type.");
- return false;
- }
-
- if (prioritized_element_type != REQUEST_STREAM &&
- prioritized_element_type != PUSH_STREAM) {
- RaiseError(QUIC_HTTP_FRAME_ERROR, "Invalid prioritized element type.");
- return false;
- }
-
- frame->prioritized_element_type =
- static_cast<PrioritizedElementType>(prioritized_element_type);
-
- if (!reader->ReadVarInt62(&frame->prioritized_element_id)) {
- RaiseError(QUIC_HTTP_FRAME_ERROR, "Unable to read prioritized element id.");
- return false;
- }
-
- absl::string_view priority_field_value = reader->ReadRemainingPayload();
- frame->priority_field_value =
- std::string(priority_field_value.data(), priority_field_value.size());
-
- return true;
-}
-
-bool HttpDecoder::ParseNewPriorityUpdateFrame(QuicDataReader* reader,
PriorityUpdateFrame* frame) {
frame->prioritized_element_type = REQUEST_STREAM;
@@ -836,20 +655,13 @@ bool HttpDecoder::ParseAcceptChFrame(QuicDataReader* reader,
QuicByteCount HttpDecoder::MaxFrameLength(uint64_t frame_type) {
switch (frame_type) {
- case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH):
- // TODO(b/171463363): Remove.
- return sizeof(PushId);
case static_cast<uint64_t>(HttpFrameType::SETTINGS):
// This limit is arbitrary.
return 1024 * 1024;
case static_cast<uint64_t>(HttpFrameType::GOAWAY):
return VARIABLE_LENGTH_INTEGER_LENGTH_8;
case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID):
- // TODO(b/171463363): Remove.
- return sizeof(PushId);
- case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE):
- // This limit is arbitrary.
- return 1024 * 1024;
+ return VARIABLE_LENGTH_INTEGER_LENGTH_8;
case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM):
// This limit is arbitrary.
return 1024 * 1024;
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h
index 3a970fb63b1..002ff68f008 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h
@@ -44,10 +44,6 @@ class QUIC_EXPORT_PRIVATE HttpDecoder {
// On*FrameStart() methods are called after the frame header is completely
// processed. At that point it is safe to consume |header_length| bytes.
- // Called when a CANCEL_PUSH frame has been successfully parsed.
- // TODO(b/171463363): Remove.
- virtual bool OnCancelPushFrame(const CancelPushFrame& frame) = 0;
-
// Called when a MAX_PUSH_ID frame has been successfully parsed.
virtual bool OnMaxPushIdFrame(const MaxPushIdFrame& frame) = 0;
@@ -84,24 +80,6 @@ class QUIC_EXPORT_PRIVATE HttpDecoder {
// Called when a HEADERS frame has been completely processed.
virtual bool OnHeadersFrameEnd() = 0;
- // TODO(b/171463363): Remove all.
- // Called when a PUSH_PROMISE frame has been received.
- virtual bool OnPushPromiseFrameStart(QuicByteCount header_length) = 0;
- // Called when the Push ID field of a PUSH_PROMISE frame has been parsed.
- // Called exactly once for a valid PUSH_PROMISE frame.
- // |push_id_length| is the length of the push ID field.
- // |header_block_length| is the length of the compressed header block.
- virtual bool OnPushPromiseFramePushId(
- PushId push_id,
- QuicByteCount push_id_length,
- QuicByteCount header_block_length) = 0;
- // Called when part of the header block of a PUSH_PROMISE frame has been
- // read. May be called multiple times for a single frame. |payload| is
- // guaranteed to be non-empty.
- virtual bool OnPushPromiseFramePayload(absl::string_view payload) = 0;
- // Called when a PUSH_PROMISE frame has been completely processed.
- virtual bool OnPushPromiseFrameEnd() = 0;
-
// Called when a PRIORITY_UPDATE frame has been received.
// |header_length| contains PRIORITY_UPDATE frame length and payload length.
virtual bool OnPriorityUpdateFrameStart(QuicByteCount header_length) = 0;
@@ -193,16 +171,23 @@ class QUIC_EXPORT_PRIVATE HttpDecoder {
// if there are any errors. Returns whether processing should continue.
bool ReadFrameLength(QuicDataReader* reader);
- // Depending on the frame type, reads and processes the payload of the current
- // frame from |reader| and calls visitor methods, or calls
- // BufferOrParsePayload(). Returns whether processing should continue.
+ // Returns whether the current frame is of a buffered type.
+ // The payload of buffered frames is buffered by HttpDecoder, and parsed by
+ // HttpDecoder after the entire frame has been received. (Copying to the
+ // buffer is skipped if the ProcessInput() call covers the entire payload.)
+ // Frames that are not buffered have every payload fragment synchronously
+ // passed to the Visitor without buffering.
+ bool IsFrameBuffered();
+
+ // For buffered frame types, calls BufferOrParsePayload(). For other frame
+ // types, reads the payload of the current frame from |reader| and calls
+ // visitor methods. Returns whether processing should continue.
bool ReadFramePayload(QuicDataReader* reader);
- // For frame types parsed by BufferOrParsePayload(), this method is only
- // called if frame payload is empty, at it calls BufferOrParsePayload(). For
- // other frame types, this method directly calls visitor methods to signal
- // that frame had been parsed completely. Returns whether processing should
- // continue.
+ // For buffered frame types, this method is only called if frame payload is
+ // empty, and it calls BufferOrParsePayload(). For other frame types, this
+ // method directly calls visitor methods to signal that frame had been
+ // received completely. Returns whether processing should continue.
bool FinishParsing(QuicDataReader* reader);
// Read payload of unknown frame from |reader| and call
@@ -210,18 +195,17 @@ class QUIC_EXPORT_PRIVATE HttpDecoder {
// false if it should be paused.
bool HandleUnknownFramePayload(QuicDataReader* reader);
- // Discards any remaining frame payload from |reader|.
- void DiscardFramePayload(QuicDataReader* reader);
-
// Buffers any remaining frame payload from |*reader| into |buffer_| if
// necessary. Parses the frame payload if complete. Parses out of |*reader|
- // without unnecessary copy if |*reader| has entire payload.
+ // without unnecessary copy if |*reader| contains entire payload.
// Returns whether processing should continue.
+ // Must only be called when current frame type is buffered.
bool BufferOrParsePayload(QuicDataReader* reader);
// Parses the entire payload of certain kinds of frames that are parsed in a
// single pass. |reader| must have at least |current_frame_length_| bytes.
// Returns whether processing should continue.
+ // Must only be called when current frame type is buffered.
bool ParseEntirePayload(QuicDataReader* reader);
// Buffers any remaining frame length field from |reader| into
@@ -231,28 +215,17 @@ class QUIC_EXPORT_PRIVATE HttpDecoder {
// Buffers any remaining frame type field from |reader| into |type_buffer_|.
void BufferFrameType(QuicDataReader* reader);
- // Buffers at most |remaining_push_id_length_| from |reader| to
- // |push_id_buffer_|. TODO(b/171463363): Remove.
- void BufferPushId(QuicDataReader* reader);
-
// Sets |error_| and |error_detail_| accordingly.
void RaiseError(QuicErrorCode error, std::string error_detail);
// Parses the payload of a SETTINGS frame from |reader| into |frame|.
bool ParseSettingsFrame(QuicDataReader* reader, SettingsFrame* frame);
- // Parses the payload of a PRIORITY_UPDATE frame (draft-01, type 0x0f)
+ // Parses the payload of a PRIORITY_UPDATE frame (draft-02, type 0xf0700)
// from |reader| into |frame|.
- // TODO(b/147306124): Remove.
bool ParsePriorityUpdateFrame(QuicDataReader* reader,
PriorityUpdateFrame* frame);
- // Parses the payload of a PRIORITY_UPDATE frame (draft-02, type 0xf0700)
- // from |reader| into |frame|.
- // TODO(b/147306124): Rename to ParsePriorityUpdateFrame().
- bool ParseNewPriorityUpdateFrame(QuicDataReader* reader,
- PriorityUpdateFrame* frame);
-
// Parses the payload of an ACCEPT_CH frame from |reader| into |frame|.
bool ParseAcceptChFrame(QuicDataReader* reader, AcceptChFrame* frame);
@@ -279,12 +252,6 @@ class QUIC_EXPORT_PRIVATE HttpDecoder {
QuicByteCount current_type_field_length_;
// Remaining length that's needed for the frame's type field.
QuicByteCount remaining_type_field_length_;
- // Length of PUSH_PROMISE frame's push id.
- // TODO(b/171463363): Remove.
- QuicByteCount current_push_id_length_;
- // Remaining length that's needed for PUSH_PROMISE frame's push id field.
- // TODO(b/171463363): Remove.
- QuicByteCount remaining_push_id_length_;
// Last error.
QuicErrorCode error_;
// The issue which caused |error_|
@@ -295,17 +262,6 @@ class QUIC_EXPORT_PRIVATE HttpDecoder {
std::array<char, sizeof(uint64_t)> length_buffer_;
// Remaining unparsed type field data.
std::array<char, sizeof(uint64_t)> type_buffer_;
- // Remaining unparsed push id data.
- // TODO(b/171463363): Remove.
- std::array<char, sizeof(uint64_t)> push_id_buffer_;
-
- // Latched value of
- // gfe2_reloadable_flag_quic_ignore_old_priority_update_frame.
- const bool ignore_old_priority_update_;
-
- // Latched value of
- // gfe2_reloadable_flag_quic_error_on_http3_push.
- const bool error_on_http3_push_;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc
index 05d580e20e1..0266632aae7 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc
@@ -45,10 +45,6 @@ class MockVisitor : public HttpDecoder::Visitor {
MOCK_METHOD(void, OnError, (HttpDecoder*), (override));
MOCK_METHOD(bool,
- OnCancelPushFrame,
- (const CancelPushFrame& frame),
- (override));
- MOCK_METHOD(bool,
OnMaxPushIdFrame,
(const MaxPushIdFrame& frame),
(override));
@@ -80,22 +76,6 @@ class MockVisitor : public HttpDecoder::Visitor {
MOCK_METHOD(bool, OnHeadersFrameEnd, (), (override));
MOCK_METHOD(bool,
- OnPushPromiseFrameStart,
- (QuicByteCount header_length),
- (override));
- MOCK_METHOD(bool,
- OnPushPromiseFramePushId,
- (PushId push_id,
- QuicByteCount push_id_length,
- QuicByteCount header_block_length),
- (override));
- MOCK_METHOD(bool,
- OnPushPromiseFramePayload,
- (absl::string_view payload),
- (override));
- MOCK_METHOD(bool, OnPushPromiseFrameEnd, (), (override));
-
- MOCK_METHOD(bool,
OnPriorityUpdateFrameStart,
(QuicByteCount header_length),
(override));
@@ -130,7 +110,6 @@ class MockVisitor : public HttpDecoder::Visitor {
class HttpDecoderTest : public QuicTest {
public:
HttpDecoderTest() : decoder_(&visitor_) {
- ON_CALL(visitor_, OnCancelPushFrame(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnMaxPushIdFrame(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnGoAwayFrame(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnSettingsFrameStart(_)).WillByDefault(Return(true));
@@ -141,11 +120,6 @@ class HttpDecoderTest : public QuicTest {
ON_CALL(visitor_, OnHeadersFrameStart(_, _)).WillByDefault(Return(true));
ON_CALL(visitor_, OnHeadersFramePayload(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnHeadersFrameEnd()).WillByDefault(Return(true));
- ON_CALL(visitor_, OnPushPromiseFrameStart(_)).WillByDefault(Return(true));
- ON_CALL(visitor_, OnPushPromiseFramePushId(_, _, _))
- .WillByDefault(Return(true));
- ON_CALL(visitor_, OnPushPromiseFramePayload(_)).WillByDefault(Return(true));
- ON_CALL(visitor_, OnPushPromiseFrameEnd()).WillByDefault(Return(true));
ON_CALL(visitor_, OnPriorityUpdateFrameStart(_))
.WillByDefault(Return(true));
ON_CALL(visitor_, OnPriorityUpdateFrame(_)).WillByDefault(Return(true));
@@ -248,144 +222,24 @@ TEST_F(HttpDecoderTest, CancelPush) {
"01" // length
"01"); // Push Id
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- EXPECT_CALL(visitor_, OnError(&decoder_));
- EXPECT_EQ(1u, ProcessInput(input));
- EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ("CANCEL_PUSH frame received.", decoder_.error_detail());
- return;
- }
-
- // Visitor pauses processing.
- EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1})))
- .WillOnce(Return(false));
- EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process the full frame.
- EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1})));
- EXPECT_EQ(input.size(), ProcessInput(input));
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process the frame incrementally.
- EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1})));
- ProcessInputCharByChar(input);
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
+ EXPECT_CALL(visitor_, OnError(&decoder_));
+ EXPECT_EQ(1u, ProcessInput(input));
+ EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR));
+ EXPECT_EQ("CANCEL_PUSH frame received.", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, PushPromiseFrame) {
InSequence s;
std::string input =
- absl::StrCat(absl::HexStringToBytes("05" // type (PUSH PROMISE)
- "0f" // length
- "C000000000000101"), // push id 257
- "Headers"); // headers
-
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- EXPECT_CALL(visitor_, OnError(&decoder_));
- EXPECT_EQ(1u, ProcessInput(input));
- EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ("PUSH_PROMISE frame received.", decoder_.error_detail());
- return;
- }
-
- // Visitor pauses processing.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)).WillOnce(Return(false));
- EXPECT_CALL(visitor_, OnPushPromiseFramePushId(257, 8, 7))
- .WillOnce(Return(false));
- absl::string_view remaining_input(input);
- QuicByteCount processed_bytes =
- ProcessInputWithGarbageAppended(remaining_input);
- EXPECT_EQ(2u, processed_bytes);
- remaining_input = remaining_input.substr(processed_bytes);
- processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
- EXPECT_EQ(8u, processed_bytes);
- remaining_input = remaining_input.substr(processed_bytes);
-
- EXPECT_CALL(visitor_, OnPushPromiseFramePayload(absl::string_view("Headers")))
- .WillOnce(Return(false));
- processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
- EXPECT_EQ(remaining_input.size(), processed_bytes);
-
- EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()).WillOnce(Return(false));
- EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process the full frame.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2));
- EXPECT_CALL(visitor_, OnPushPromiseFramePushId(257, 8, 7));
- EXPECT_CALL(visitor_,
- OnPushPromiseFramePayload(absl::string_view("Headers")));
- EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
- EXPECT_EQ(input.size(), ProcessInput(input));
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process the frame incrementally.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2));
- EXPECT_CALL(visitor_, OnPushPromiseFramePushId(257, 8, 7));
- EXPECT_CALL(visitor_, OnPushPromiseFramePayload(absl::string_view("H")));
- EXPECT_CALL(visitor_, OnPushPromiseFramePayload(absl::string_view("e")));
- EXPECT_CALL(visitor_, OnPushPromiseFramePayload(absl::string_view("a")));
- EXPECT_CALL(visitor_, OnPushPromiseFramePayload(absl::string_view("d")));
- EXPECT_CALL(visitor_, OnPushPromiseFramePayload(absl::string_view("e")));
- EXPECT_CALL(visitor_, OnPushPromiseFramePayload(absl::string_view("r")));
- EXPECT_CALL(visitor_, OnPushPromiseFramePayload(absl::string_view("s")));
- EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
- ProcessInputCharByChar(input);
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process push id incrementally and append headers with last byte of push id.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2));
- EXPECT_CALL(visitor_, OnPushPromiseFramePushId(257, 8, 7));
- EXPECT_CALL(visitor_,
- OnPushPromiseFramePayload(absl::string_view("Headers")));
- EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
- ProcessInputCharByChar(input.substr(0, 9));
- EXPECT_EQ(8u, ProcessInput(input.substr(9)));
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-}
-
-TEST_F(HttpDecoderTest, CorruptPushPromiseFrame) {
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- return;
- }
-
- InSequence s;
-
- std::string input = absl::HexStringToBytes(
- "05" // type (PUSH_PROMISE)
- "01" // length
- "40"); // first byte of two-byte varint push id
-
- {
- HttpDecoder decoder(&visitor_);
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2));
- EXPECT_CALL(visitor_, OnError(&decoder));
-
- decoder.ProcessInput(input.data(), input.size());
-
- EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ("Unable to read PUSH_PROMISE push_id.", decoder.error_detail());
- }
- {
- HttpDecoder decoder(&visitor_);
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2));
- EXPECT_CALL(visitor_, OnError(&decoder));
-
- for (auto c : input) {
- decoder.ProcessInput(&c, 1);
- }
+ absl::StrCat(absl::HexStringToBytes("05" // type (PUSH PROMISE)
+ "08" // length
+ "1f"), // push id 31
+ "Headers"); // headers
- EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ("Unable to read PUSH_PROMISE push_id.", decoder.error_detail());
- }
+ EXPECT_CALL(visitor_, OnError(&decoder_));
+ EXPECT_EQ(1u, ProcessInput(input));
+ EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR));
+ EXPECT_EQ("PUSH_PROMISE frame received.", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, MaxPushId) {
@@ -564,10 +418,8 @@ TEST_F(HttpDecoderTest, FrameHeaderPartialDelivery) {
InSequence s;
// A large input that will occupy more than 1 byte in the length field.
std::string input(2048, 'x');
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(input.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ input.length(), SimpleBufferAllocator::Get());
// Partially send only 1 byte of the header to process.
EXPECT_EQ(1u, decoder_.ProcessInput(header.data(), 1));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
@@ -575,8 +427,8 @@ TEST_F(HttpDecoderTest, FrameHeaderPartialDelivery) {
// Send the rest of the header.
EXPECT_CALL(visitor_, OnDataFrameStart(3, input.length()));
- EXPECT_EQ(header_length - 1,
- decoder_.ProcessInput(header.data() + 1, header_length - 1));
+ EXPECT_EQ(header.size() - 1,
+ decoder_.ProcessInput(header.data() + 1, header.size() - 1));
EXPECT_THAT(decoder_.error(), IsQuicNoError());
EXPECT_EQ("", decoder_.error_detail());
@@ -751,55 +603,23 @@ TEST_F(HttpDecoderTest, EmptyHeadersFrame) {
EXPECT_EQ("", decoder_.error_detail());
}
-TEST_F(HttpDecoderTest, PushPromiseFrameNoHeaders) {
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- return;
- }
-
- InSequence s;
+TEST_F(HttpDecoderTest, GoawayWithOverlyLargePayload) {
std::string input = absl::HexStringToBytes(
- "05" // type (PUSH_PROMISE)
- "01" // length
- "01"); // Push Id
-
- // Visitor pauses processing.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2));
- EXPECT_CALL(visitor_, OnPushPromiseFramePushId(1, 1, 0))
- .WillOnce(Return(false));
- EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
-
- EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()).WillOnce(Return(false));
- EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process the full frame.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2));
- EXPECT_CALL(visitor_, OnPushPromiseFramePushId(1, 1, 0));
- EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
- EXPECT_EQ(input.size(), ProcessInput(input));
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process the frame incrementally.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2));
- EXPECT_CALL(visitor_, OnPushPromiseFramePushId(1, 1, 0));
- EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
- ProcessInputCharByChar(input);
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
+ "07" // type (GOAWAY)
+ "10"); // length exceeding the maximum possible length for GOAWAY frame
+ // Process all data at once.
+ EXPECT_CALL(visitor_, OnError(&decoder_));
+ EXPECT_EQ(2u, ProcessInput(input));
+ EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_TOO_LARGE));
+ EXPECT_EQ("Frame is too large.", decoder_.error_detail());
}
-TEST_F(HttpDecoderTest, MalformedFrameWithOverlyLargePayload) {
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- return;
- }
-
+TEST_F(HttpDecoderTest, MaxPushIdWithOverlyLargePayload) {
std::string input = absl::HexStringToBytes(
- "03" // type (CANCEL_PUSH)
- "10" // length
- "15"); // malformed payload
- // Process the full frame.
+ "0d" // type (MAX_PUSH_ID)
+ "10"); // length exceeding the maximum possible length for MAX_PUSH_ID
+ // frame
+ // Process all data at once.
EXPECT_CALL(visitor_, OnError(&decoder_));
EXPECT_EQ(2u, ProcessInput(input));
EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_TOO_LARGE));
@@ -868,193 +688,87 @@ TEST_F(HttpDecoderTest, HeadersPausedThenData) {
}
TEST_F(HttpDecoderTest, CorruptFrame) {
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- InSequence s;
-
- struct {
- const char* const input;
- const char* const error_message;
- } kTestData[] = {{"\x0D" // type (MAX_PUSH_ID)
- "\x01" // length
- "\x40", // first byte of two-byte varint push id
- "Unable to read MAX_PUSH_ID push_id."},
- {"\x0D" // type (MAX_PUSH_ID)
- "\x04" // length
- "\x05" // valid push id
- "foo", // superfluous data
- "Superfluous data in MAX_PUSH_ID frame."},
- {"\x07" // type (GOAWAY)
- "\x01" // length
- "\x40", // first byte of two-byte varint stream id
- "Unable to read GOAWAY ID."},
- {"\x07" // type (GOAWAY)
- "\x04" // length
- "\x05" // valid stream id
- "foo", // superfluous data
- "Superfluous data in GOAWAY frame."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x01" // length
- "\x40", // first byte of two-byte varint origin length
- "Unable to read ACCEPT_CH origin."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x01" // length
- "\x05", // valid origin length but no origin string
- "Unable to read ACCEPT_CH origin."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x04" // length
- "\x05" // valid origin length
- "foo", // payload ends before origin ends
- "Unable to read ACCEPT_CH origin."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x04" // length
- "\x03" // valid origin length
- "foo", // payload ends at end of origin: no value
- "Unable to read ACCEPT_CH value."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x05" // length
- "\x03" // valid origin length
- "foo" // payload ends at end of origin: no value
- "\x40", // first byte of two-byte varint value length
- "Unable to read ACCEPT_CH value."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x08" // length
- "\x03" // valid origin length
- "foo" // origin
- "\x05" // valid value length
- "bar", // payload ends before value ends
- "Unable to read ACCEPT_CH value."}};
-
- for (const auto& test_data : kTestData) {
- {
- HttpDecoder decoder(&visitor_);
- EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber());
- EXPECT_CALL(visitor_, OnError(&decoder));
-
- absl::string_view input(test_data.input);
- decoder.ProcessInput(input.data(), input.size());
- EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ(test_data.error_message, decoder.error_detail());
- }
- {
- HttpDecoder decoder(&visitor_);
- EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber());
- EXPECT_CALL(visitor_, OnError(&decoder));
-
- absl::string_view input(test_data.input);
- for (auto c : input) {
- decoder.ProcessInput(&c, 1);
- }
- EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ(test_data.error_message, decoder.error_detail());
- }
+ InSequence s;
+
+ struct {
+ const char* const input;
+ const char* const error_message;
+ } kTestData[] = {{"\x0D" // type (MAX_PUSH_ID)
+ "\x01" // length
+ "\x40", // first byte of two-byte varint push id
+ "Unable to read MAX_PUSH_ID push_id."},
+ {"\x0D" // type (MAX_PUSH_ID)
+ "\x04" // length
+ "\x05" // valid push id
+ "foo", // superfluous data
+ "Superfluous data in MAX_PUSH_ID frame."},
+ {"\x07" // type (GOAWAY)
+ "\x01" // length
+ "\x40", // first byte of two-byte varint stream id
+ "Unable to read GOAWAY ID."},
+ {"\x07" // type (GOAWAY)
+ "\x04" // length
+ "\x05" // valid stream id
+ "foo", // superfluous data
+ "Superfluous data in GOAWAY frame."},
+ {"\x40\x89" // type (ACCEPT_CH)
+ "\x01" // length
+ "\x40", // first byte of two-byte varint origin length
+ "Unable to read ACCEPT_CH origin."},
+ {"\x40\x89" // type (ACCEPT_CH)
+ "\x01" // length
+ "\x05", // valid origin length but no origin string
+ "Unable to read ACCEPT_CH origin."},
+ {"\x40\x89" // type (ACCEPT_CH)
+ "\x04" // length
+ "\x05" // valid origin length
+ "foo", // payload ends before origin ends
+ "Unable to read ACCEPT_CH origin."},
+ {"\x40\x89" // type (ACCEPT_CH)
+ "\x04" // length
+ "\x03" // valid origin length
+ "foo", // payload ends at end of origin: no value
+ "Unable to read ACCEPT_CH value."},
+ {"\x40\x89" // type (ACCEPT_CH)
+ "\x05" // length
+ "\x03" // valid origin length
+ "foo" // payload ends at end of origin: no value
+ "\x40", // first byte of two-byte varint value length
+ "Unable to read ACCEPT_CH value."},
+ {"\x40\x89" // type (ACCEPT_CH)
+ "\x08" // length
+ "\x03" // valid origin length
+ "foo" // origin
+ "\x05" // valid value length
+ "bar", // payload ends before value ends
+ "Unable to read ACCEPT_CH value."}};
+
+ for (const auto& test_data : kTestData) {
+ {
+ HttpDecoder decoder(&visitor_);
+ EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber());
+ EXPECT_CALL(visitor_, OnError(&decoder));
+
+ absl::string_view input(test_data.input);
+ decoder.ProcessInput(input.data(), input.size());
+ EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
+ EXPECT_EQ(test_data.error_message, decoder.error_detail());
}
- } else {
- InSequence s;
-
- struct {
- const char* const input;
- const char* const error_message;
- } kTestData[] = {{"\x03" // type (CANCEL_PUSH)
- "\x01" // length
- "\x40", // first byte of two-byte varint push id
- "Unable to read CANCEL_PUSH push_id."},
- {"\x03" // type (CANCEL_PUSH)
- "\x04" // length
- "\x05" // valid push id
- "foo", // superfluous data
- "Superfluous data in CANCEL_PUSH frame."},
- {"\x0D" // type (MAX_PUSH_ID)
- "\x01" // length
- "\x40", // first byte of two-byte varint push id
- "Unable to read MAX_PUSH_ID push_id."},
- {"\x0D" // type (MAX_PUSH_ID)
- "\x04" // length
- "\x05" // valid push id
- "foo", // superfluous data
- "Superfluous data in MAX_PUSH_ID frame."},
- {"\x07" // type (GOAWAY)
- "\x01" // length
- "\x40", // first byte of two-byte varint stream id
- "Unable to read GOAWAY ID."},
- {"\x07" // type (GOAWAY)
- "\x04" // length
- "\x05" // valid stream id
- "foo", // superfluous data
- "Superfluous data in GOAWAY frame."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x01" // length
- "\x40", // first byte of two-byte varint origin length
- "Unable to read ACCEPT_CH origin."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x01" // length
- "\x05", // valid origin length but no origin string
- "Unable to read ACCEPT_CH origin."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x04" // length
- "\x05" // valid origin length
- "foo", // payload ends before origin ends
- "Unable to read ACCEPT_CH origin."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x04" // length
- "\x03" // valid origin length
- "foo", // payload ends at end of origin: no value
- "Unable to read ACCEPT_CH value."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x05" // length
- "\x03" // valid origin length
- "foo" // payload ends at end of origin: no value
- "\x40", // first byte of two-byte varint value length
- "Unable to read ACCEPT_CH value."},
- {"\x40\x89" // type (ACCEPT_CH)
- "\x08" // length
- "\x03" // valid origin length
- "foo" // origin
- "\x05" // valid value length
- "bar", // payload ends before value ends
- "Unable to read ACCEPT_CH value."}};
-
- for (const auto& test_data : kTestData) {
- {
- HttpDecoder decoder(&visitor_);
- EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber());
- EXPECT_CALL(visitor_, OnError(&decoder));
-
- absl::string_view input(test_data.input);
- decoder.ProcessInput(input.data(), input.size());
- EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ(test_data.error_message, decoder.error_detail());
- }
- {
- HttpDecoder decoder(&visitor_);
- EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber());
- EXPECT_CALL(visitor_, OnError(&decoder));
-
- absl::string_view input(test_data.input);
- for (auto c : input) {
- decoder.ProcessInput(&c, 1);
- }
- EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ(test_data.error_message, decoder.error_detail());
+ {
+ HttpDecoder decoder(&visitor_);
+ EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber());
+ EXPECT_CALL(visitor_, OnError(&decoder));
+
+ absl::string_view input(test_data.input);
+ for (auto c : input) {
+ decoder.ProcessInput(&c, 1);
}
+ EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
+ EXPECT_EQ(test_data.error_message, decoder.error_detail());
}
}
}
-TEST_F(HttpDecoderTest, EmptyCancelPushFrame) {
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- return;
- }
-
- std::string input = absl::HexStringToBytes(
- "03" // type (CANCEL_PUSH)
- "00"); // frame length
-
- EXPECT_CALL(visitor_, OnError(&decoder_));
- EXPECT_EQ(input.size(), ProcessInput(input));
- EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ("Unable to read CANCEL_PUSH push_id.", decoder_.error_detail());
-}
-
TEST_F(HttpDecoderTest, EmptySettingsFrame) {
std::string input = absl::HexStringToBytes(
"04" // type (SETTINGS)
@@ -1070,22 +784,6 @@ TEST_F(HttpDecoderTest, EmptySettingsFrame) {
EXPECT_EQ("", decoder_.error_detail());
}
-// Regression test for https://crbug.com/1001823.
-TEST_F(HttpDecoderTest, EmptyPushPromiseFrame) {
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- return;
- }
-
- std::string input = absl::HexStringToBytes(
- "05" // type (PUSH_PROMISE)
- "00"); // frame length
-
- EXPECT_CALL(visitor_, OnError(&decoder_));
- EXPECT_EQ(input.size(), ProcessInput(input));
- EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ("PUSH_PROMISE frame with empty payload.", decoder_.error_detail());
-}
-
TEST_F(HttpDecoderTest, EmptyGoAwayFrame) {
std::string input = absl::HexStringToBytes(
"07" // type (GOAWAY)
@@ -1120,97 +818,8 @@ TEST_F(HttpDecoderTest, LargeStreamIdInGoAway) {
EXPECT_EQ("", decoder_.error_detail());
}
-TEST_F(HttpDecoderTest, OldPriorityUpdateFrame) {
- if (GetQuicReloadableFlag(quic_ignore_old_priority_update_frame)) {
- return;
- }
-
- InSequence s;
- std::string input1 = absl::HexStringToBytes(
- "0f" // type (PRIORITY_UPDATE)
- "02" // length
- "00" // prioritized element type: REQUEST_STREAM
- "03"); // prioritized element id
-
- PriorityUpdateFrame priority_update1;
- priority_update1.prioritized_element_type = REQUEST_STREAM;
- priority_update1.prioritized_element_id = 0x03;
-
- // Visitor pauses processing.
- EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(2)).WillOnce(Return(false));
- absl::string_view remaining_input(input1);
- QuicByteCount processed_bytes =
- ProcessInputWithGarbageAppended(remaining_input);
- EXPECT_EQ(2u, processed_bytes);
- remaining_input = remaining_input.substr(processed_bytes);
-
- EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update1))
- .WillOnce(Return(false));
- processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
- EXPECT_EQ(remaining_input.size(), processed_bytes);
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process the full frame.
- EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(2));
- EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update1));
- EXPECT_EQ(input1.size(), ProcessInput(input1));
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process the frame incrementally.
- EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(2));
- EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update1));
- ProcessInputCharByChar(input1);
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- std::string input2 = absl::HexStringToBytes(
- "0f" // type (PRIORITY_UPDATE)
- "05" // length
- "80" // prioritized element type: PUSH_STREAM
- "05" // prioritized element id
- "666f6f"); // priority field value: "foo"
-
- PriorityUpdateFrame priority_update2;
- priority_update2.prioritized_element_type = PUSH_STREAM;
- priority_update2.prioritized_element_id = 0x05;
- priority_update2.priority_field_value = "foo";
-
- // Visitor pauses processing.
- EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(2)).WillOnce(Return(false));
- remaining_input = input2;
- processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
- EXPECT_EQ(2u, processed_bytes);
- remaining_input = remaining_input.substr(processed_bytes);
-
- EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update2))
- .WillOnce(Return(false));
- processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
- EXPECT_EQ(remaining_input.size(), processed_bytes);
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process the full frame.
- EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(2));
- EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update2));
- EXPECT_EQ(input2.size(), ProcessInput(input2));
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-
- // Process the frame incrementally.
- EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(2));
- EXPECT_CALL(visitor_, OnPriorityUpdateFrame(priority_update2));
- ProcessInputCharByChar(input2);
- EXPECT_THAT(decoder_.error(), IsQuicNoError());
- EXPECT_EQ("", decoder_.error_detail());
-}
-
+// Old PRIORITY_UPDATE frame is parsed as unknown frame.
TEST_F(HttpDecoderTest, ObsoletePriorityUpdateFrame) {
- if (!GetQuicReloadableFlag(quic_ignore_old_priority_update_frame)) {
- return;
- }
-
const QuicByteCount header_length = 2;
const QuicByteCount payload_length = 3;
InSequence s;
@@ -1324,46 +933,6 @@ TEST_F(HttpDecoderTest, PriorityUpdateFrame) {
}
TEST_F(HttpDecoderTest, CorruptPriorityUpdateFrame) {
- if (GetQuicReloadableFlag(quic_ignore_old_priority_update_frame)) {
- return;
- }
-
- std::string payload1 = absl::HexStringToBytes(
- "80" // prioritized element type: PUSH_STREAM
- "4005"); // prioritized element id
- std::string payload2 =
- absl::HexStringToBytes("42"); // invalid prioritized element type
- struct {
- const char* const payload;
- size_t payload_length;
- const char* const error_message;
- } kTestData[] = {
- {payload1.data(), 0, "Unable to read prioritized element type."},
- {payload1.data(), 1, "Unable to read prioritized element id."},
- {payload1.data(), 2, "Unable to read prioritized element id."},
- {payload2.data(), 1, "Invalid prioritized element type."},
- };
-
- for (const auto& test_data : kTestData) {
- std::string input;
- input.push_back(15u); // type PRIORITY_UPDATE
- input.push_back(test_data.payload_length);
- size_t header_length = input.size();
- input.append(test_data.payload, test_data.payload_length);
-
- HttpDecoder decoder(&visitor_);
- EXPECT_CALL(visitor_, OnPriorityUpdateFrameStart(header_length));
- EXPECT_CALL(visitor_, OnError(&decoder));
-
- QuicByteCount processed_bytes =
- decoder.ProcessInput(input.data(), input.size());
- EXPECT_EQ(input.size(), processed_bytes);
- EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
- EXPECT_EQ(test_data.error_message, decoder.error_detail());
- }
-}
-
-TEST_F(HttpDecoderTest, CorruptNewPriorityUpdateFrame) {
std::string payload =
absl::HexStringToBytes("4005"); // prioritized element id
struct {
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc
index 2fbc2380cf7..25f6200e59b 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc
@@ -34,23 +34,30 @@ QuicByteCount GetTotalLength(QuicByteCount payload_length, HttpFrameType type) {
} // namespace
// static
-QuicByteCount HttpEncoder::SerializeDataFrameHeader(
+QuicByteCount HttpEncoder::GetDataFrameHeaderLength(
+ QuicByteCount payload_length) {
+ QUICHE_DCHECK_NE(0u, payload_length);
+ return QuicDataWriter::GetVarInt62Len(payload_length) +
+ QuicDataWriter::GetVarInt62Len(
+ static_cast<uint64_t>(HttpFrameType::DATA));
+}
+
+// static
+QuicBuffer HttpEncoder::SerializeDataFrameHeader(
QuicByteCount payload_length,
- std::unique_ptr<char[]>* output) {
+ QuicBufferAllocator* allocator) {
QUICHE_DCHECK_NE(0u, payload_length);
- QuicByteCount header_length = QuicDataWriter::GetVarInt62Len(payload_length) +
- QuicDataWriter::GetVarInt62Len(
- static_cast<uint64_t>(HttpFrameType::DATA));
+ QuicByteCount header_length = GetDataFrameHeaderLength(payload_length);
- output->reset(new char[header_length]);
- QuicDataWriter writer(header_length, output->get());
+ QuicBuffer header(allocator, header_length);
+ QuicDataWriter writer(header.size(), header.data());
if (WriteFrameHeader(payload_length, HttpFrameType::DATA, &writer)) {
- return header_length;
+ return header;
}
QUIC_DLOG(ERROR)
<< "Http encoder failed when attempting to serialize data frame header.";
- return 0;
+ return QuicBuffer();
}
// static
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h
index 0a8aa84e0b7..585e739ef53 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h
@@ -7,6 +7,7 @@
#include <memory>
#include "quic/core/http/http_frames.h"
+#include "quic/core/quic_buffer_allocator.h"
#include "quic/core/quic_error_codes.h"
#include "quic/core/quic_types.h"
#include "quic/platform/api/quic_export.h"
@@ -21,11 +22,13 @@ class QUIC_EXPORT_PRIVATE HttpEncoder {
public:
HttpEncoder() = delete;
- // Serializes a DATA frame header into a new buffer stored in |output|.
- // Returns the length of the buffer on success, or 0 otherwise.
- static QuicByteCount SerializeDataFrameHeader(
- QuicByteCount payload_length,
- std::unique_ptr<char[]>* output);
+ // Returns the length of the header for a DATA frame.
+ static QuicByteCount GetDataFrameHeaderLength(QuicByteCount payload_length);
+
+ // Serializes a DATA frame header into a QuicBuffer; returns said QuicBuffer
+ // on success, empty buffer otherwise.
+ static QuicBuffer SerializeDataFrameHeader(QuicByteCount payload_length,
+ QuicBufferAllocator* allocator);
// Serializes a HEADERS frame header into a new buffer stored in |output|.
// Returns the length of the buffer on success, or 0 otherwise.
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc
index 66e988facca..c2b0f36946e 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc
@@ -5,6 +5,7 @@
#include "quic/core/http/http_encoder.h"
#include "absl/base/macros.h"
+#include "quic/core/quic_simple_buffer_allocator.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/quic_test_utils.h"
@@ -14,16 +15,15 @@ namespace quic {
namespace test {
TEST(HttpEncoderTest, SerializeDataFrameHeader) {
- std::unique_ptr<char[]> buffer;
- uint64_t length =
- HttpEncoder::SerializeDataFrameHeader(/* payload_length = */ 5, &buffer);
+ QuicBuffer buffer = HttpEncoder::SerializeDataFrameHeader(
+ /* payload_length = */ 5, SimpleBufferAllocator::Get());
char output[] = {// type (DATA)
0x00,
// length
0x05};
- EXPECT_EQ(ABSL_ARRAYSIZE(output), length);
- quiche::test::CompareCharArraysWithHexError("DATA", buffer.get(), length,
- output, ABSL_ARRAYSIZE(output));
+ EXPECT_EQ(ABSL_ARRAYSIZE(output), buffer.size());
+ quiche::test::CompareCharArraysWithHexError(
+ "DATA", buffer.data(), buffer.size(), output, ABSL_ARRAYSIZE(output));
}
TEST(HttpEncoderTest, SerializeHeadersFrameHeader) {
@@ -87,40 +87,42 @@ TEST(HttpEncoderTest, SerializePriorityUpdateFrame) {
PriorityUpdateFrame priority_update1;
priority_update1.prioritized_element_type = REQUEST_STREAM;
priority_update1.prioritized_element_id = 0x03;
- char output1[] = {0x80, 0x0f, 0x07, 0x00, // type (PRIORITY_UPDATE)
- 0x01, // length
- 0x03}; // prioritized element id
+ uint8_t output1[] = {0x80, 0x0f, 0x07, 0x00, // type (PRIORITY_UPDATE)
+ 0x01, // length
+ 0x03}; // prioritized element id
std::unique_ptr<char[]> buffer;
uint64_t length =
HttpEncoder::SerializePriorityUpdateFrame(priority_update1, &buffer);
EXPECT_EQ(ABSL_ARRAYSIZE(output1), length);
- quiche::test::CompareCharArraysWithHexError("PRIORITY_UPDATE", buffer.get(),
- length, output1,
- ABSL_ARRAYSIZE(output1));
+ quiche::test::CompareCharArraysWithHexError(
+ "PRIORITY_UPDATE", buffer.get(), length, reinterpret_cast<char*>(output1),
+ ABSL_ARRAYSIZE(output1));
}
TEST(HttpEncoderTest, SerializeAcceptChFrame) {
AcceptChFrame accept_ch;
- char output1[] = {0x40, 0x89, // type (ACCEPT_CH)
- 0x00}; // length
+ uint8_t output1[] = {0x40, 0x89, // type (ACCEPT_CH)
+ 0x00}; // length
std::unique_ptr<char[]> buffer;
uint64_t length = HttpEncoder::SerializeAcceptChFrame(accept_ch, &buffer);
EXPECT_EQ(ABSL_ARRAYSIZE(output1), length);
quiche::test::CompareCharArraysWithHexError("ACCEPT_CH", buffer.get(), length,
- output1, ABSL_ARRAYSIZE(output1));
+ reinterpret_cast<char*>(output1),
+ ABSL_ARRAYSIZE(output1));
accept_ch.entries.push_back({"foo", "bar"});
- char output2[] = {0x40, 0x89, // type (ACCEPT_CH)
- 0x08, // payload length
- 0x03, 0x66, 0x6f, 0x6f, // length of "foo"; "foo"
- 0x03, 0x62, 0x61, 0x72}; // length of "bar"; "bar"
+ uint8_t output2[] = {0x40, 0x89, // type (ACCEPT_CH)
+ 0x08, // payload length
+ 0x03, 0x66, 0x6f, 0x6f, // length of "foo"; "foo"
+ 0x03, 0x62, 0x61, 0x72}; // length of "bar"; "bar"
length = HttpEncoder::SerializeAcceptChFrame(accept_ch, &buffer);
EXPECT_EQ(ABSL_ARRAYSIZE(output2), length);
quiche::test::CompareCharArraysWithHexError("ACCEPT_CH", buffer.get(), length,
- output2, ABSL_ARRAYSIZE(output2));
+ reinterpret_cast<char*>(output2),
+ ABSL_ARRAYSIZE(output2));
}
TEST(HttpEncoderTest, SerializeWebTransportStreamFrameHeader) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h b/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h
index b14ebe5a91b..6a00ad7bb53 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h
@@ -20,6 +20,9 @@
namespace quic {
+// TODO(b/171463363): Remove.
+using PushId = uint64_t;
+
enum class HttpFrameType {
DATA = 0x0,
HEADERS = 0x1,
@@ -28,12 +31,9 @@ enum class HttpFrameType {
PUSH_PROMISE = 0x5,
GOAWAY = 0x7,
MAX_PUSH_ID = 0xD,
- // https://tools.ietf.org/html/draft-ietf-httpbis-priority-01
- // TODO(b/147306124): Remove.
- PRIORITY_UPDATE = 0XF,
// https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02
ACCEPT_CH = 0x89,
- // https://tools.ietf.org/html/draft-ietf-httpbis-priority-02
+ // https://tools.ietf.org/html/draft-ietf-httpbis-priority-03
PRIORITY_UPDATE_REQUEST_STREAM = 0xF0700,
// https://www.ietf.org/archive/id/draft-ietf-webtrans-http3-00.html
WEBTRANSPORT_STREAM = 0x41,
@@ -55,20 +55,6 @@ struct QUIC_EXPORT_PRIVATE HeadersFrame {
absl::string_view headers;
};
-// 7.2.3. CANCEL_PUSH
-//
-// The CANCEL_PUSH frame (type=0x3) is used to request cancellation of
-// server push prior to the push stream being created.
-using PushId = uint64_t;
-
-struct QUIC_EXPORT_PRIVATE CancelPushFrame {
- PushId push_id;
-
- bool operator==(const CancelPushFrame& rhs) const {
- return push_id == rhs.push_id;
- }
-};
-
// 7.2.4. SETTINGS
//
// The SETTINGS frame (type=0x4) conveys configuration parameters that
@@ -102,20 +88,6 @@ struct QUIC_EXPORT_PRIVATE SettingsFrame {
}
};
-// 7.2.5. PUSH_PROMISE
-//
-// The PUSH_PROMISE frame (type=0x05) is used to carry a request header
-// set from server to client, as in HTTP/2.
-// TODO(b/171463363): Remove.
-struct QUIC_EXPORT_PRIVATE PushPromiseFrame {
- PushId push_id;
- absl::string_view headers;
-
- bool operator==(const PushPromiseFrame& rhs) const {
- return push_id == rhs.push_id && headers == rhs.headers;
- }
-};
-
// 7.2.6. GOAWAY
//
// The GOAWAY frame (type=0x7) is used to initiate shutdown of a connection by
@@ -144,10 +116,9 @@ struct QUIC_EXPORT_PRIVATE MaxPushIdFrame {
// https://httpwg.org/http-extensions/draft-ietf-httpbis-priority.html
//
// The PRIORITY_UPDATE frame specifies the sender-advised priority of a stream.
-// https://tools.ietf.org/html/draft-ietf-httpbis-priority-01 uses frame type
-// 0x0f, both for request streams and push streams.
-// https://tools.ietf.org/html/draft-ietf-httpbis-priority-02 uses frame types
-// 0xf0700 for request streams and 0xf0701 for push streams (not implemented).
+// Frame type 0xf0700 (called PRIORITY_UPDATE_REQUEST_STREAM in the
+// implementation) is used for for request streams.
+// Frame type 0xf0701 is used for push streams and is not implemented.
// Length of a priority frame's first byte.
const QuicByteCount kPriorityFirstByteLength = 1;
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_frames_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_frames_test.cc
index c9a285dc8d9..24fd5780af8 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/http_frames_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_frames_test.cc
@@ -12,17 +12,6 @@
namespace quic {
namespace test {
-TEST(HttpFramesTest, CancelPushFrame) {
- CancelPushFrame a{1};
- EXPECT_TRUE(a == a);
-
- CancelPushFrame b{1};
- EXPECT_TRUE(a == b);
-
- b.push_id = 2;
- EXPECT_FALSE(a == b);
-}
-
TEST(HttpFramesTest, SettingsFrame) {
SettingsFrame a;
EXPECT_TRUE(a == a);
@@ -44,21 +33,6 @@ TEST(HttpFramesTest, SettingsFrame) {
EXPECT_EQ("SETTINGS_QPACK_MAX_TABLE_CAPACITY = 1; ", s.str());
}
-TEST(HttpFramesTest, PushPromiseFrame) {
- PushPromiseFrame a{1, ""};
- EXPECT_TRUE(a == a);
-
- PushPromiseFrame b{2, ""};
- EXPECT_FALSE(a == b);
-
- b.push_id = 1;
- EXPECT_TRUE(a == b);
-
- b.headers = "foo";
- EXPECT_FALSE(a == b);
- EXPECT_TRUE(b == b);
-}
-
TEST(HttpFramesTest, GoAwayFrame) {
GoAwayFrame a{1};
EXPECT_TRUE(a == a);
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc
index 36011804056..1c951ba108b 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc
@@ -24,7 +24,11 @@ QuicClientPromisedInfo::QuicClientPromisedInfo(
url_(std::move(url)),
client_request_delegate_(nullptr) {}
-QuicClientPromisedInfo::~QuicClientPromisedInfo() {}
+QuicClientPromisedInfo::~QuicClientPromisedInfo() {
+ if (cleanup_alarm_ != nullptr) {
+ cleanup_alarm_->PermanentCancel();
+ }
+}
void QuicClientPromisedInfo::CleanupAlarm::OnAlarm() {
QUIC_DVLOG(1) << "self GC alarm for stream " << promised_->id_;
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc
index 39d8b42a5a2..9c164d0e448 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc
@@ -65,14 +65,6 @@ void QuicReceiveControlStream::OnError(HttpDecoder* decoder) {
stream_delegate()->OnStreamError(decoder->error(), decoder->error_detail());
}
-bool QuicReceiveControlStream::OnCancelPushFrame(const CancelPushFrame& frame) {
- if (spdy_session()->debug_visitor()) {
- spdy_session()->debug_visitor()->OnCancelPushFrameReceived(frame);
- }
-
- return ValidateFrameType(HttpFrameType::CANCEL_PUSH);
-}
-
bool QuicReceiveControlStream::OnMaxPushIdFrame(const MaxPushIdFrame& frame) {
if (spdy_session()->debug_visitor()) {
spdy_session()->debug_visitor()->OnMaxPushIdFrameReceived(frame);
@@ -144,33 +136,9 @@ bool QuicReceiveControlStream::OnHeadersFrameEnd() {
return false;
}
-bool QuicReceiveControlStream::OnPushPromiseFrameStart(
- QuicByteCount /*header_length*/) {
- return ValidateFrameType(HttpFrameType::PUSH_PROMISE);
-}
-
-bool QuicReceiveControlStream::OnPushPromiseFramePushId(
- PushId /*push_id*/,
- QuicByteCount /*push_id_length*/,
- QuicByteCount /*header_block_length*/) {
- QUICHE_NOTREACHED();
- return false;
-}
-
-bool QuicReceiveControlStream::OnPushPromiseFramePayload(
- absl::string_view /*payload*/) {
- QUICHE_NOTREACHED();
- return false;
-}
-
-bool QuicReceiveControlStream::OnPushPromiseFrameEnd() {
- QUICHE_NOTREACHED();
- return false;
-}
-
bool QuicReceiveControlStream::OnPriorityUpdateFrameStart(
QuicByteCount /*header_length*/) {
- return ValidateFrameType(HttpFrameType::PRIORITY_UPDATE);
+ return ValidateFrameType(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM);
}
bool QuicReceiveControlStream::OnPriorityUpdateFrame(
@@ -264,9 +232,8 @@ bool QuicReceiveControlStream::OnUnknownFrameEnd() {
bool QuicReceiveControlStream::ValidateFrameType(HttpFrameType frame_type) {
// Certain frame types are forbidden.
- if ((frame_type == HttpFrameType::DATA ||
- frame_type == HttpFrameType::HEADERS ||
- frame_type == HttpFrameType::PUSH_PROMISE) ||
+ if (frame_type == HttpFrameType::DATA ||
+ frame_type == HttpFrameType::HEADERS ||
(spdy_session()->perspective() == Perspective::IS_CLIENT &&
frame_type == HttpFrameType::MAX_PUSH_ID) ||
(spdy_session()->perspective() == Perspective::IS_SERVER &&
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h
index 4ed01043662..3c61d3cb621 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h
@@ -33,9 +33,8 @@ class QUIC_EXPORT_PRIVATE QuicReceiveControlStream
// Implementation of QuicStream.
void OnDataAvailable() override;
- // HttpDecoderVisitor implementation.
+ // HttpDecoder::Visitor implementation.
void OnError(HttpDecoder* decoder) override;
- bool OnCancelPushFrame(const CancelPushFrame& frame) override;
bool OnMaxPushIdFrame(const MaxPushIdFrame& frame) override;
bool OnGoAwayFrame(const GoAwayFrame& frame) override;
bool OnSettingsFrameStart(QuicByteCount header_length) override;
@@ -48,12 +47,6 @@ class QUIC_EXPORT_PRIVATE QuicReceiveControlStream
QuicByteCount payload_length) override;
bool OnHeadersFramePayload(absl::string_view payload) override;
bool OnHeadersFrameEnd() override;
- bool OnPushPromiseFrameStart(QuicByteCount header_length) override;
- bool OnPushPromiseFramePushId(PushId push_id,
- QuicByteCount push_id_length,
- QuicByteCount header_block_length) override;
- bool OnPushPromiseFramePayload(absl::string_view payload) override;
- bool OnPushPromiseFrameEnd() override;
bool OnPriorityUpdateFrameStart(QuicByteCount header_length) override;
bool OnPriorityUpdateFrame(const PriorityUpdateFrame& frame) override;
bool OnAcceptChFrameStart(QuicByteCount header_length) override;
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc
index 03f21d8a70f..ae7a0d818cc 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc
@@ -9,6 +9,7 @@
#include "absl/strings/string_view.h"
#include "quic/core/http/http_constants.h"
#include "quic/core/qpack/qpack_header_table.h"
+#include "quic/core/quic_simple_buffer_allocator.h"
#include "quic/core/quic_types.h"
#include "quic/core/quic_utils.h"
#include "quic/test_tools/qpack/qpack_encoder_peer.h"
@@ -239,12 +240,11 @@ TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsFragments) {
TEST_P(QuicReceiveControlStreamTest, ReceiveWrongFrame) {
// DATA frame header without payload.
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(/* payload_length = */ 2, &buffer);
- std::string data = std::string(buffer.get(), header_length);
+ QuicBuffer data = HttpEncoder::SerializeDataFrameHeader(
+ /* payload_length = */ 2, SimpleBufferAllocator::Get());
- QuicStreamFrame frame(receive_control_stream_->id(), false, 1, data);
+ QuicStreamFrame frame(receive_control_stream_->id(), false, 1,
+ data.AsStringView());
EXPECT_CALL(
*connection_,
CloseConnection(QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, _, _));
@@ -260,7 +260,7 @@ TEST_P(QuicReceiveControlStreamTest,
EXPECT_CALL(*connection_,
CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME,
"First frame received on control stream is type "
- "15, but it must be SETTINGS.",
+ "984832, but it must be SETTINGS.",
_))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
@@ -308,12 +308,7 @@ TEST_P(QuicReceiveControlStreamTest, PushPromiseOnControlStreamShouldClose) {
"00"); // push ID
QuicStreamFrame frame(receive_control_stream_->id(), false, 1,
push_promise_frame);
- EXPECT_CALL(
- *connection_,
- CloseConnection(GetQuicReloadableFlag(quic_error_on_http3_push)
- ? QUIC_HTTP_FRAME_ERROR
- : QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM,
- _, _))
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_FRAME_ERROR, _, _))
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
@@ -384,21 +379,10 @@ TEST_P(QuicReceiveControlStreamTest, CancelPushFrameBeforeSettings) {
"01" // payload length
"01"); // push ID
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_FRAME_ERROR,
- "CANCEL_PUSH frame received.", _))
- .WillOnce(
- Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- } else {
- EXPECT_CALL(
- *connection_,
- CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME,
- "First frame received on control stream is type "
- "3, but it must be SETTINGS.",
- _))
- .WillOnce(
- Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- }
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_FRAME_ERROR,
+ "CANCEL_PUSH frame received.", _))
+ .WillOnce(
+ Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
EXPECT_CALL(session_, OnConnectionClosed(_, _));
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc
index c5af1dc4a90..3066cb38871 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc
@@ -133,7 +133,7 @@ TEST_P(QuicSendControlStreamTest, WriteSettings) {
"4040" // 0x40 as the reserved frame type
"01" // 1 byte frame length
"61"); // payload "a"
- if (GetQuicReloadableFlag(quic_h3_datagram)) {
+ if (QuicSpdySessionPeer::ShouldNegotiateHttp3Datagram(&session_)) {
expected_write_data = absl::HexStringToBytes(
"00" // stream type: control stream
"04" // frame type: SETTINGS frame
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc
index c78df58e18a..69d6cb47a9d 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc
@@ -282,4 +282,23 @@ void QuicServerSessionBase::SendSettingsToCryptoStream() {
std::move(serialized_settings));
}
+QuicSSLConfig QuicServerSessionBase::GetSSLConfig() const {
+ QUICHE_DCHECK(crypto_config_ && crypto_config_->proof_source());
+
+ QuicSSLConfig ssl_config = QuicSpdySession::GetSSLConfig();
+ if (!GetQuicReloadableFlag(quic_tls_set_signature_algorithm_prefs) ||
+ !crypto_config_ || !crypto_config_->proof_source()) {
+ return ssl_config;
+ }
+
+ absl::InlinedVector<uint16_t, 8> signature_algorithms =
+ crypto_config_->proof_source()->SupportedTlsSignatureAlgorithms();
+ if (!signature_algorithms.empty()) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_tls_set_signature_algorithm_prefs, 1, 2);
+ ssl_config.signing_algorithm_prefs = std::move(signature_algorithms);
+ }
+
+ return ssl_config;
+}
+
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h
index 9bdaa30adf9..bd9f5e71f42 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h
@@ -68,6 +68,8 @@ class QUIC_EXPORT_PRIVATE QuicServerSessionBase : public QuicSpdySession {
serving_region_ = serving_region;
}
+ QuicSSLConfig GetSSLConfig() const override;
+
protected:
// QuicSession methods(override them with return type of QuicSpdyStream*):
QuicCryptoServerStreamBase* GetMutableCryptoStream() override;
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc
index b71cba9958f..45bdd1d31b1 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc
@@ -126,6 +126,10 @@ int QuicSpdyClientSession::GetNumSentClientHellos() const {
return crypto_stream_->num_sent_client_hellos();
}
+bool QuicSpdyClientSession::IsResumption() const {
+ return crypto_stream_->IsResumption();
+}
+
bool QuicSpdyClientSession::EarlyDataAccepted() const {
return crypto_stream_->EarlyDataAccepted();
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h
index 88df049e06d..a1c0883b8ad 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h
@@ -61,6 +61,10 @@ class QUIC_EXPORT_PRIVATE QuicSpdyClientSession
// than the number of round-trips needed for the handshake.
int GetNumSentClientHellos() const;
+ // Return true if the handshake performed is a TLS resumption.
+ // Always return false for QUIC Crypto.
+ bool IsResumption() const;
+
// Returns true if early data (0-RTT data) was sent and the server accepted
// it.
bool EarlyDataAccepted() const;
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc
index fa4943dcda7..9043d0b0e71 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc
@@ -12,6 +12,7 @@
#include "quic/core/crypto/null_encrypter.h"
#include "quic/core/http/quic_spdy_client_session.h"
#include "quic/core/http/spdy_utils.h"
+#include "quic/core/quic_simple_buffer_allocator.h"
#include "quic/core/quic_utils.h"
#include "quic/platform/api/quic_logging.h"
#include "quic/platform/api/quic_socket_address.h"
@@ -131,12 +132,10 @@ TEST_P(QuicSpdyClientStreamTest, TestFraming) {
auto headers = AsHeaderList(headers_);
stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
headers);
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
std::string data = VersionUsesHttp3(connection_->transport_version())
- ? header + body_
+ ? absl::StrCat(header.AsStringView(), body_)
: body_;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
@@ -160,12 +159,11 @@ TEST_P(QuicSpdyClientStreamTest, Test100ContinueBeforeSuccessful) {
headers = AsHeaderList(headers_);
stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
headers);
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
- std::string data =
- connection_->version().UsesHttp3() ? header + body_ : body_;
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
+ std::string data = VersionUsesHttp3(connection_->transport_version())
+ ? absl::StrCat(header.AsStringView(), body_)
+ : body_;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
// Make sure the 200 response got parsed correctly.
@@ -190,12 +188,11 @@ TEST_P(QuicSpdyClientStreamTest, TestUnknownInformationalBeforeSuccessful) {
headers = AsHeaderList(headers_);
stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
headers);
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
- std::string data =
- connection_->version().UsesHttp3() ? header + body_ : body_;
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
+ std::string data = VersionUsesHttp3(connection_->transport_version())
+ ? absl::StrCat(header.AsStringView(), body_)
+ : body_;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
// Make sure the 200 response got parsed correctly.
@@ -222,12 +219,10 @@ TEST_P(QuicSpdyClientStreamTest, TestFramingOnePacket) {
auto headers = AsHeaderList(headers_);
stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
headers);
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
std::string data = VersionUsesHttp3(connection_->transport_version())
- ? header + body_
+ ? absl::StrCat(header.AsStringView(), body_)
: body_;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
@@ -247,12 +242,10 @@ TEST_P(QuicSpdyClientStreamTest,
EXPECT_THAT(stream_->stream_error(), IsQuicStreamNoError());
EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
EXPECT_EQ(200, stream_->response_code());
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(large_body.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ large_body.length(), SimpleBufferAllocator::Get());
std::string data = VersionUsesHttp3(connection_->transport_version())
- ? header + large_body
+ ? absl::StrCat(header.AsStringView(), large_body)
: large_body;
EXPECT_CALL(session_, WriteControlFrame(_, _));
EXPECT_CALL(*connection_,
@@ -290,12 +283,10 @@ TEST_P(QuicSpdyClientStreamTest, ReceivingTrailers) {
// Now send the body, which should close the stream as the FIN has been
// received, as well as all data.
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
std::string data = VersionUsesHttp3(connection_->transport_version())
- ? header + body_
+ ? absl::StrCat(header.AsStringView(), body_)
: body_;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc
index 276a7aff6b6..56b0db3ff48 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc
@@ -72,10 +72,6 @@ class AlpsFrameDecoder : public HttpDecoder::Visitor {
// HttpDecoder::Visitor implementation.
void OnError(HttpDecoder* /*decoder*/) override {}
- bool OnCancelPushFrame(const CancelPushFrame& /*frame*/) override {
- error_detail_ = "CANCEL_PUSH frame forbidden";
- return false;
- }
bool OnMaxPushIdFrame(const MaxPushIdFrame& /*frame*/) override {
error_detail_ = "MAX_PUSH_ID frame forbidden";
return false;
@@ -124,26 +120,6 @@ class AlpsFrameDecoder : public HttpDecoder::Visitor {
QUICHE_NOTREACHED();
return false;
}
- bool OnPushPromiseFrameStart(QuicByteCount /*header_length*/) override {
- error_detail_ = "PUSH_PROMISE frame forbidden";
- return false;
- }
- bool OnPushPromiseFramePushId(
- PushId /*push_id*/,
- QuicByteCount
- /*push_id_length*/,
- QuicByteCount /*header_block_length*/) override {
- QUICHE_NOTREACHED();
- return false;
- }
- bool OnPushPromiseFramePayload(absl::string_view /*payload*/) override {
- QUICHE_NOTREACHED();
- return false;
- }
- bool OnPushPromiseFrameEnd() override {
- QUICHE_NOTREACHED();
- return false;
- }
bool OnPriorityUpdateFrameStart(QuicByteCount /*header_length*/) override {
error_detail_ = "PRIORITY_UPDATE frame forbidden";
return false;
@@ -461,14 +437,9 @@ Http3DebugVisitor::~Http3DebugVisitor() {}
// Expected unidirectional static streams Requirement can be found at
// https://tools.ietf.org/html/draft-ietf-quic-http-22#section-6.2.
QuicSpdySession::QuicSpdySession(
- QuicConnection* connection,
- QuicSession::Visitor* visitor,
- const QuicConfig& config,
- const ParsedQuicVersionVector& supported_versions)
- : QuicSession(connection,
- visitor,
- config,
- supported_versions,
+ QuicConnection* connection, QuicSession::Visitor* visitor,
+ const QuicConfig& config, const ParsedQuicVersionVector& supported_versions)
+ : QuicSession(connection, visitor, config, supported_versions,
/*num_expected_unidirectional_static_streams = */
VersionUsesHttp3(connection->transport_version())
? static_cast<QuicStreamCount>(
@@ -495,13 +466,13 @@ QuicSpdySession::QuicSpdySession(
spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
spdy_framer_visitor_(new SpdyFramerVisitor(this)),
debug_visitor_(nullptr),
- destruction_indicator_(123456789),
- next_available_datagram_flow_id_(perspective() == Perspective::IS_SERVER
- ? kFirstDatagramFlowIdServer
- : kFirstDatagramFlowIdClient) {
+ destruction_indicator_(123456789) {
h2_deframer_.set_visitor(spdy_framer_visitor_.get());
h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get());
spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
+ if (decline_server_push_stream_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_decline_server_push_stream);
+ }
}
QuicSpdySession::~QuicSpdySession() {
@@ -553,7 +524,6 @@ void QuicSpdySession::FillSettingsFrame() {
settings_.values[SETTINGS_MAX_FIELD_SECTION_SIZE] =
max_inbound_header_list_size_;
if (ShouldNegotiateHttp3Datagram() && version().UsesHttp3()) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_h3_datagram);
settings_.values[SETTINGS_H3_DATAGRAM] = 1;
}
if (WillNegotiateWebTransport()) {
@@ -1391,6 +1361,11 @@ QuicStream* QuicSpdySession::ProcessPendingStream(PendingStream* pending) {
return receive_control_stream_;
}
case kServerPushStream: { // Push Stream.
+ if (decline_server_push_stream_) {
+ CloseConnectionWithDetails(QUIC_HTTP_RECEIVE_SERVER_PUSH,
+ "Received server push stream");
+ return nullptr;
+ }
QuicSpdyStream* stream = CreateIncomingStream(pending);
return stream;
}
@@ -1645,53 +1620,55 @@ void QuicSpdySession::LogHeaderCompressionRatioHistogram(
}
}
-QuicDatagramFlowId QuicSpdySession::GetNextDatagramFlowId() {
- QuicDatagramFlowId result = next_available_datagram_flow_id_;
- next_available_datagram_flow_id_ += kDatagramFlowIdIncrement;
- return result;
-}
-
-MessageStatus QuicSpdySession::SendHttp3Datagram(QuicDatagramFlowId flow_id,
- absl::string_view payload) {
+MessageStatus QuicSpdySession::SendHttp3Datagram(
+ QuicDatagramStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ absl::string_view payload) {
size_t slice_length =
- QuicDataWriter::GetVarInt62Len(flow_id) + payload.length();
- QuicUniqueBufferPtr buffer = MakeUniqueBuffer(
- connection()->helper()->GetStreamSendBufferAllocator(), slice_length);
- QuicDataWriter writer(slice_length, buffer.get());
- if (!writer.WriteVarInt62(flow_id)) {
- QUIC_BUG(quic_bug_10360_10) << "Failed to write HTTP/3 datagram flow ID";
+ QuicDataWriter::GetVarInt62Len(stream_id) + payload.length();
+ if (context_id.has_value()) {
+ slice_length += QuicDataWriter::GetVarInt62Len(context_id.value());
+ }
+ QuicBuffer buffer(connection()->helper()->GetStreamSendBufferAllocator(),
+ slice_length);
+ QuicDataWriter writer(slice_length, buffer.data());
+ if (!writer.WriteVarInt62(stream_id)) {
+ QUIC_BUG(h3 datagram stream ID write fail)
+ << "Failed to write HTTP/3 datagram stream ID";
return MESSAGE_STATUS_INTERNAL_ERROR;
}
+ if (context_id.has_value()) {
+ if (!writer.WriteVarInt62(context_id.value())) {
+ QUIC_BUG(h3 datagram context ID write fail)
+ << "Failed to write HTTP/3 datagram context ID";
+ return MESSAGE_STATUS_INTERNAL_ERROR;
+ }
+ }
if (!writer.WriteBytes(payload.data(), payload.length())) {
- QUIC_BUG(quic_bug_10360_11) << "Failed to write HTTP/3 datagram payload";
+ QUIC_BUG(h3 datagram payload write fail)
+ << "Failed to write HTTP/3 datagram payload";
return MESSAGE_STATUS_INTERNAL_ERROR;
}
- QuicMemSlice slice(std::move(buffer), slice_length);
+ QuicMemSlice slice(std::move(buffer));
return datagram_queue()->SendOrQueueDatagram(std::move(slice));
}
-void QuicSpdySession::RegisterHttp3FlowId(
- QuicDatagramFlowId flow_id,
- QuicSpdySession::Http3DatagramVisitor* visitor) {
- QUICHE_DCHECK_NE(visitor, nullptr);
- auto insertion_result = h3_datagram_registrations_.insert({flow_id, visitor});
- QUIC_BUG_IF(quic_bug_12477_7, !insertion_result.second)
- << "Attempted to doubly register HTTP/3 flow ID " << flow_id;
+void QuicSpdySession::SetMaxDatagramTimeInQueueForStreamId(
+ QuicStreamId /*stream_id*/, QuicTime::Delta max_time_in_queue) {
+ // TODO(b/184598230): implement this in a way that works for multiple sessions
+ // on a same connection.
+ datagram_queue()->SetMaxTimeInQueue(max_time_in_queue);
}
-void QuicSpdySession::UnregisterHttp3FlowId(QuicDatagramFlowId flow_id) {
- size_t num_erased = h3_datagram_registrations_.erase(flow_id);
- QUIC_BUG_IF(quic_bug_12477_8, num_erased != 1)
- << "Attempted to unregister unknown HTTP/3 flow ID " << flow_id;
+void QuicSpdySession::RegisterHttp3DatagramFlowId(QuicDatagramStreamId flow_id,
+ QuicStreamId stream_id) {
+ h3_datagram_flow_id_to_stream_id_map_[flow_id] = stream_id;
}
-void QuicSpdySession::SetMaxTimeInQueueForFlowId(
- QuicDatagramFlowId /*flow_id*/,
- QuicTime::Delta max_time_in_queue) {
- // TODO(b/184598230): implement this in a way that works for multiple sessions
- // on a same connection.
- datagram_queue()->SetMaxTimeInQueue(max_time_in_queue);
+void QuicSpdySession::UnregisterHttp3DatagramFlowId(
+ QuicDatagramStreamId flow_id) {
+ h3_datagram_flow_id_to_stream_id_map_.erase(flow_id);
}
void QuicSpdySession::OnMessageReceived(absl::string_view message) {
@@ -1701,20 +1678,38 @@ void QuicSpdySession::OnMessageReceived(absl::string_view message) {
return;
}
QuicDataReader reader(message);
- QuicDatagramFlowId flow_id;
- if (!reader.ReadVarInt62(&flow_id)) {
- QUIC_DLOG(ERROR) << "Failed to parse flow ID in received HTTP/3 datagram";
+ uint64_t stream_id64;
+ if (!reader.ReadVarInt62(&stream_id64)) {
+ QUIC_DLOG(ERROR) << "Failed to parse stream ID in received HTTP/3 datagram";
return;
}
- auto it = h3_datagram_registrations_.find(flow_id);
- if (it == h3_datagram_registrations_.end()) {
- // TODO(dschinazi) buffer unknown HTTP/3 datagram flow IDs for a short
+ if (perspective() == Perspective::IS_SERVER) {
+ auto it = h3_datagram_flow_id_to_stream_id_map_.find(stream_id64);
+ if (it == h3_datagram_flow_id_to_stream_id_map_.end()) {
+ QUIC_DLOG(INFO) << "Received unknown HTTP/3 datagram flow ID "
+ << stream_id64;
+ return;
+ }
+ stream_id64 = it->second;
+ }
+ if (stream_id64 > std::numeric_limits<QuicStreamId>::max()) {
+ // TODO(b/181256914) make this a connection close once we deprecate
+ // draft-ietf-masque-h3-datagram-00 in favor of later drafts.
+ QUIC_DLOG(ERROR) << "Received unexpectedly high HTTP/3 datagram stream ID "
+ << stream_id64;
+ return;
+ }
+ QuicStreamId stream_id = static_cast<QuicStreamId>(stream_id64);
+ QuicSpdyStream* stream =
+ static_cast<QuicSpdyStream*>(GetActiveStream(stream_id));
+ if (stream == nullptr) {
+ QUIC_DLOG(INFO) << "Received HTTP/3 datagram for unknown stream ID "
+ << stream_id;
+ // TODO(b/181256914) buffer unknown HTTP/3 datagram flow IDs for a short
// period of time in case they were reordered.
- QUIC_DLOG(ERROR) << "Received unknown HTTP/3 datagram flow ID " << flow_id;
return;
}
- absl::string_view payload = reader.ReadRemainingPayload();
- it->second->OnHttp3Datagram(flow_id, payload);
+ stream->OnDatagramReceived(&reader);
}
bool QuicSpdySession::SupportsWebTransport() {
@@ -1850,7 +1845,7 @@ void QuicSpdySession::DatagramObserver::OnDatagramProcessed(
}
bool QuicSpdySession::ShouldNegotiateHttp3Datagram() {
- return GetQuicReloadableFlag(quic_h3_datagram);
+ return false;
}
#undef ENDPOINT // undef for jumbo builds
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h
index 214d1da13e2..78c132a6ad6 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h
@@ -78,8 +78,6 @@ class QUIC_EXPORT_PRIVATE Http3DebugVisitor {
virtual void OnAcceptChFrameReceivedViaAlps(const AcceptChFrame& /*frame*/) {}
// Incoming HTTP/3 frames on the control stream.
- // TODO(b/171463363): Remove.
- virtual void OnCancelPushFrameReceived(const CancelPushFrame& /*frame*/) {}
virtual void OnSettingsFrameReceived(const SettingsFrame& /*frame*/) = 0;
virtual void OnGoAwayFrameReceived(const GoAwayFrame& /*frame*/) {}
// TODO(b/171463363): Remove.
@@ -96,15 +94,6 @@ class QUIC_EXPORT_PRIVATE Http3DebugVisitor {
QuicByteCount /*compressed_headers_length*/) {}
virtual void OnHeadersDecoded(QuicStreamId /*stream_id*/,
QuicHeaderList /*headers*/) {}
- // TODO(b/171463363): Remove.
- virtual void OnPushPromiseFrameReceived(QuicStreamId /*stream_id*/,
- QuicStreamId /*push_id*/,
- QuicByteCount
- /*compressed_headers_length*/) {}
- // TODO(b/171463363): Remove.
- virtual void OnPushPromiseDecoded(QuicStreamId /*stream_id*/,
- QuicStreamId /*push_id*/,
- QuicHeaderList /*headers*/) {}
// Incoming HTTP/3 frames of unknown type on any stream.
virtual void OnUnknownFrameReceived(QuicStreamId /*stream_id*/,
@@ -125,12 +114,6 @@ class QUIC_EXPORT_PRIVATE Http3DebugVisitor {
virtual void OnHeadersFrameSent(
QuicStreamId /*stream_id*/,
const spdy::SpdyHeaderBlock& /*header_block*/) {}
- // TODO(b/171463363): Remove.
- virtual void OnPushPromiseFrameSent(
- QuicStreamId /*stream_id*/,
- QuicStreamId
- /*push_id*/,
- const spdy::SpdyHeaderBlock& /*header_block*/) {}
// 0-RTT related events.
virtual void OnSettingsFrameResumed(const SettingsFrame& /*frame*/) {}
@@ -391,41 +374,24 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession
// extension.
virtual void OnAcceptChFrameReceivedViaAlps(const AcceptChFrame& /*frame*/);
- // Generates a new HTTP/3 datagram flow ID.
- QuicDatagramFlowId GetNextDatagramFlowId();
-
// Whether HTTP/3 datagrams are supported on this session, based on received
// SETTINGS.
bool h3_datagram_supported() const { return h3_datagram_supported_; }
- // Sends an HTTP/3 datagram. The flow ID is not part of |payload|.
- MessageStatus SendHttp3Datagram(QuicDatagramFlowId flow_id,
- absl::string_view payload);
-
- class QUIC_EXPORT_PRIVATE Http3DatagramVisitor {
- public:
- virtual ~Http3DatagramVisitor() {}
-
- // Called when an HTTP/3 datagram is received. |payload| does not contain
- // the flow ID.
- virtual void OnHttp3Datagram(QuicDatagramFlowId flow_id,
- absl::string_view payload) = 0;
- };
-
- // Registers |visitor| to receive HTTP/3 datagrams for flow ID |flow_id|. This
- // must not be called on a previously register flow ID without first calling
- // UnregisterHttp3FlowId. |visitor| must be valid until a corresponding call
- // to UnregisterHttp3FlowId. The flow ID must be unregistered before the
- // QuicSpdySession is destroyed.
- void RegisterHttp3FlowId(QuicDatagramFlowId flow_id,
- Http3DatagramVisitor* visitor);
-
- // Unregister a given HTTP/3 datagram flow ID.
- void UnregisterHttp3FlowId(QuicDatagramFlowId flow_id);
-
- // Sets max time in queue for a specified datagram flow ID.
- void SetMaxTimeInQueueForFlowId(QuicDatagramFlowId flow_id,
- QuicTime::Delta max_time_in_queue);
+ // This must not be used except by QuicSpdyStream::SendHttp3Datagram.
+ MessageStatus SendHttp3Datagram(
+ QuicDatagramStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ absl::string_view payload);
+ // This must not be used except by QuicSpdyStream::SetMaxDatagramTimeInQueue.
+ void SetMaxDatagramTimeInQueueForStreamId(QuicStreamId stream_id,
+ QuicTime::Delta max_time_in_queue);
+ // This must not be used except by
+ // QuicSpdyStream::MaybeProcessReceivedWebTransportHeaders.
+ void RegisterHttp3DatagramFlowId(QuicDatagramStreamId flow_id,
+ QuicStreamId stream_id);
+ // This must not be used except by QuicSpdyStream::OnClose.
+ void UnregisterHttp3DatagramFlowId(QuicDatagramStreamId flow_id);
// Override from QuicSession to support HTTP/3 datagrams.
void OnMessageReceived(absl::string_view message) override;
@@ -433,6 +399,9 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession
// Indicates whether the HTTP/3 session supports WebTransport.
bool SupportsWebTransport();
+ // Indicates whether both the peer and us support HTTP/3 Datagrams.
+ bool SupportsH3Datagram() { return h3_datagram_supported_; }
+
// Indicates whether the HTTP/3 session will indicate WebTransport support to
// the peer.
bool WillNegotiateWebTransport();
@@ -445,7 +414,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession
// until the SETTINGS are received. Only works for HTTP/3.
bool ShouldBufferRequestsUntilSettings() {
return version().UsesHttp3() && perspective() == Perspective::IS_SERVER &&
- WillNegotiateWebTransport();
+ ShouldNegotiateHttp3Datagram();
}
// Returns if the incoming bidirectional streams should process data. This is
@@ -685,18 +654,17 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession
// frame has been sent yet.
absl::optional<uint64_t> last_sent_http3_goaway_id_;
- // Value of the smallest unused HTTP/3 datagram flow ID that this endpoint's
- // datagram flow ID allocation service will use next.
- QuicDatagramFlowId next_available_datagram_flow_id_;
-
// Whether both this endpoint and our peer support HTTP/3 datagrams.
bool h3_datagram_supported_ = false;
// Whether the peer has indicated WebTransport support.
bool peer_supports_webtransport_ = false;
- absl::flat_hash_map<QuicDatagramFlowId, Http3DatagramVisitor*>
- h3_datagram_registrations_;
+ // This maps from draft-ietf-masque-h3-datagram-00 flow IDs to stream IDs.
+ // TODO(b/181256914) remove this when we deprecate support for that draft in
+ // favor of more recent ones.
+ absl::flat_hash_map<uint64_t, QuicStreamId>
+ h3_datagram_flow_id_to_stream_id_map_;
// Whether any settings have been received, either from the peer or from a
// session ticket.
@@ -710,6 +678,10 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession
// Limited to kMaxUnassociatedWebTransportStreams; when the list is full,
// oldest streams are evicated first.
std::list<BufferedWebTransportStream> buffered_streams_;
+
+ // Latched value of flag_quic_decline_server_push_stream.
+ const bool decline_server_push_stream_ =
+ GetQuicReloadableFlag(quic_decline_server_push_stream);
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc
index 29e1f6b7bcd..5a984323de4 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc
@@ -35,7 +35,6 @@
#include "quic/core/quic_versions.h"
#include "quic/platform/api/quic_expect_bug.h"
#include "quic/platform/api/quic_flags.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/qpack/qpack_encoder_peer.h"
#include "quic/test_tools/qpack/qpack_test_utils.h"
@@ -309,12 +308,10 @@ class TestSession : public QuicSpdySession {
return QuicSpdySession::GetOrCreateStream(stream_id);
}
- QuicConsumedData WritevData(QuicStreamId id,
- size_t write_length,
- QuicStreamOffset offset,
- StreamSendingState state,
+ QuicConsumedData WritevData(QuicStreamId id, size_t write_length,
+ QuicStreamOffset offset, StreamSendingState state,
TransmissionType type,
- absl::optional<EncryptionLevel> level) override {
+ EncryptionLevel level) override {
bool fin = state != NO_FIN;
QuicConsumedData consumed(write_length, fin);
if (!writev_consumes_all_data_) {
@@ -356,6 +353,13 @@ class TestSession : public QuicSpdySession {
bool ShouldNegotiateWebTransport() override { return supports_webtransport_; }
void set_supports_webtransport(bool value) { supports_webtransport_ = value; }
+ bool ShouldNegotiateHttp3Datagram() override {
+ return should_negotiate_h3_datagram_;
+ }
+ void set_should_negotiate_h3_datagram(bool value) {
+ should_negotiate_h3_datagram_ = value;
+ }
+
MOCK_METHOD(void, OnAcceptChFrame, (const AcceptChFrame&), (override));
using QuicSession::closed_streams;
@@ -368,6 +372,7 @@ class TestSession : public QuicSpdySession {
bool writev_consumes_all_data_;
bool supports_webtransport_ = false;
+ bool should_negotiate_h3_datagram_ = false;
};
class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> {
@@ -420,7 +425,7 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> {
first_stream_id = QuicUtils::GetCryptoStreamId(transport_version());
}
for (QuicStreamId i = first_stream_id; i < 100; i++) {
- if (!QuicContainsKey(closed_streams_, i)) {
+ if (closed_streams_.find(i) == closed_streams_.end()) {
EXPECT_FALSE(session_.IsClosedStream(i)) << " stream id: " << i;
} else {
EXPECT_TRUE(session_.IsClosedStream(i)) << " stream id: " << i;
@@ -566,8 +571,7 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> {
headers.OnHeaderBlockStart();
headers.OnHeader(":method", "CONNECT");
headers.OnHeader(":protocol", "webtransport");
- headers.OnHeader("datagram-flow-id",
- absl::StrCat(session_.GetNextDatagramFlowId()));
+ headers.OnHeader("datagram-flow-id", absl::StrCat(session_id));
stream->OnStreamHeaderList(/*fin=*/true, 0, headers);
WebTransportHttp3* web_transport =
session_.GetWebTransportSession(session_id);
@@ -1495,61 +1499,6 @@ TEST_P(QuicSpdySessionTestServer, HandshakeUnblocksFlowControlBlockedStream) {
EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
}
-TEST_P(QuicSpdySessionTestServer,
- HandshakeUnblocksFlowControlBlockedCryptoStream) {
- if (QuicVersionUsesCryptoFrames(transport_version()) ||
- connection_->encrypted_control_frames()) {
- // QUIC version 47 onwards uses CRYPTO frames for the handshake, so this
- // test doesn't make sense for those versions. With
- // use_encryption_level_context, control frames can only be sent when
- // encryption gets established, do not send BLOCKED for crypto streams.
- return;
- }
- // Test that if the crypto stream is flow control blocked, then if the SHLO
- // contains a larger send window offset, the stream becomes unblocked.
- session_.set_writev_consumes_all_data(true);
- TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
- EXPECT_FALSE(crypto_stream->IsFlowControlBlocked());
- EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
- EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
- QuicHeadersStream* headers_stream =
- QuicSpdySessionPeer::GetHeadersStream(&session_);
- EXPECT_FALSE(headers_stream->IsFlowControlBlocked());
- EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
- EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
- EXPECT_CALL(*connection_, SendControlFrame(_))
- .WillOnce(Invoke(&ClearControlFrame));
- for (QuicStreamId i = 0; !crypto_stream->IsFlowControlBlocked() && i < 1000u;
- i++) {
- EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
- EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
- QuicStreamOffset offset = crypto_stream->stream_bytes_written();
- QuicConfig config;
- CryptoHandshakeMessage crypto_message;
- config.ToHandshakeMessage(&crypto_message, transport_version());
- crypto_stream->SendHandshakeMessage(crypto_message, ENCRYPTION_INITIAL);
- char buf[1000];
- QuicDataWriter writer(1000, buf, quiche::NETWORK_BYTE_ORDER);
- crypto_stream->WriteStreamData(offset, crypto_message.size(), &writer);
- }
- EXPECT_TRUE(crypto_stream->IsFlowControlBlocked());
- EXPECT_FALSE(headers_stream->IsFlowControlBlocked());
- EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
- EXPECT_TRUE(session_.IsStreamFlowControlBlocked());
- EXPECT_FALSE(session_.HasDataToWrite());
- EXPECT_TRUE(crypto_stream->HasBufferedData());
-
- // Now complete the crypto handshake, resulting in an increased flow control
- // send window.
- CompleteHandshake();
- EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked(
- &session_, QuicUtils::GetCryptoStreamId(transport_version())));
- // Stream is now unblocked and will no longer have buffered data.
- EXPECT_FALSE(crypto_stream->IsFlowControlBlocked());
- EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
- EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
-}
-
#if !defined(OS_IOS)
// This test is failing flakily for iOS bots.
// http://crbug.com/425050
@@ -2058,25 +2007,33 @@ TEST_P(QuicSpdySessionTestClient, Http3ServerPush) {
std::string frame_type1 = absl::HexStringToBytes("01");
QuicStreamId stream_id1 =
GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0);
- session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false,
- /* offset = */ 0, frame_type1));
+ if (!GetQuicReloadableFlag(quic_decline_server_push_stream)) {
+ session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false,
+ /* offset = */ 0, frame_type1));
- EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
- QuicStream* stream = session_.GetOrCreateStream(stream_id1);
- EXPECT_EQ(1u, QuicStreamPeer::bytes_consumed(stream));
- EXPECT_EQ(1u, session_.flow_controller()->bytes_consumed());
-
- // The same stream type can be encoded differently.
- std::string frame_type2 = absl::HexStringToBytes("80000001");
- QuicStreamId stream_id2 =
- GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1);
- session_.OnStreamFrame(QuicStreamFrame(stream_id2, /* fin = */ false,
- /* offset = */ 0, frame_type2));
-
- EXPECT_EQ(2u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
- stream = session_.GetOrCreateStream(stream_id2);
- EXPECT_EQ(4u, QuicStreamPeer::bytes_consumed(stream));
- EXPECT_EQ(5u, session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
+ QuicStream* stream = session_.GetOrCreateStream(stream_id1);
+ EXPECT_EQ(1u, QuicStreamPeer::bytes_consumed(stream));
+ EXPECT_EQ(1u, session_.flow_controller()->bytes_consumed());
+
+ // The same stream type can be encoded differently.
+ std::string frame_type2 = absl::HexStringToBytes("80000001");
+ QuicStreamId stream_id2 =
+ GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1);
+ session_.OnStreamFrame(QuicStreamFrame(stream_id2, /* fin = */ false,
+ /* offset = */ 0, frame_type2));
+
+ EXPECT_EQ(2u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
+ stream = session_.GetOrCreateStream(stream_id2);
+ EXPECT_EQ(4u, QuicStreamPeer::bytes_consumed(stream));
+ EXPECT_EQ(5u, session_.flow_controller()->bytes_consumed());
+ } else {
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_HTTP_RECEIVE_SERVER_PUSH, _, _))
+ .Times(1);
+ session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false,
+ /* offset = */ 0, frame_type1));
+ }
}
TEST_P(QuicSpdySessionTestClient, Http3ServerPushOutofOrderFrame) {
@@ -2104,10 +2061,17 @@ TEST_P(QuicSpdySessionTestClient, Http3ServerPushOutofOrderFrame) {
session_.OnStreamFrame(data2);
EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
- session_.OnStreamFrame(data1);
- EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
- QuicStream* stream = session_.GetOrCreateStream(stream_id);
- EXPECT_EQ(3u, stream->highest_received_byte_offset());
+ if (!GetQuicReloadableFlag(quic_decline_server_push_stream)) {
+ session_.OnStreamFrame(data1);
+ EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
+ QuicStream* stream = session_.GetOrCreateStream(stream_id);
+ EXPECT_EQ(3u, stream->highest_received_byte_offset());
+ } else {
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_HTTP_RECEIVE_SERVER_PUSH, _, _))
+ .Times(1);
+ session_.OnStreamFrame(data1);
+ }
}
TEST_P(QuicSpdySessionTestServer, OnStreamFrameLost) {
@@ -2921,18 +2885,13 @@ TEST_P(QuicSpdySessionTestClient, CloseConnectionOnCancelPush) {
"00"); // push ID
QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset,
cancel_push_frame);
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_FRAME_ERROR,
- "CANCEL_PUSH frame received.", _))
- .WillOnce(
- Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_,
- SendConnectionClosePacket(QUIC_HTTP_FRAME_ERROR, _,
- "CANCEL_PUSH frame received."));
- } else {
- // CANCEL_PUSH is ignored.
- EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_));
- }
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_FRAME_ERROR,
+ "CANCEL_PUSH frame received.", _))
+ .WillOnce(
+ Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+ EXPECT_CALL(*connection_,
+ SendConnectionClosePacket(QUIC_HTTP_FRAME_ERROR, _,
+ "CANCEL_PUSH frame received."));
session_.OnStreamFrame(data3);
}
@@ -3128,18 +3087,13 @@ TEST_P(QuicSpdySessionTestServer, CloseConnectionOnCancelPush) {
"00"); // push ID
QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset,
cancel_push_frame);
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_FRAME_ERROR,
- "CANCEL_PUSH frame received.", _))
- .WillOnce(
- Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
- EXPECT_CALL(*connection_,
- SendConnectionClosePacket(QUIC_HTTP_FRAME_ERROR, _,
- "CANCEL_PUSH frame received."));
- } else {
- // CANCEL_PUSH is ignored.
- EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_));
- }
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_FRAME_ERROR,
+ "CANCEL_PUSH frame received.", _))
+ .WillOnce(
+ Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+ EXPECT_CALL(*connection_,
+ SendConnectionClosePacket(QUIC_HTTP_FRAME_ERROR, _,
+ "CANCEL_PUSH frame received."));
session_.OnStreamFrame(data3);
}
@@ -3463,31 +3417,11 @@ TEST_P(QuicSpdySessionTestClient, AlpsTwoSettingsFrame) {
EXPECT_EQ("multiple SETTINGS frames", error.value());
}
-TEST_P(QuicSpdySessionTestClient, GetNextDatagramFlowId) {
- if (!version().UsesHttp3()) {
- return;
- }
- EXPECT_EQ(session_.GetNextDatagramFlowId(), 0u);
- EXPECT_EQ(session_.GetNextDatagramFlowId(), 2u);
- EXPECT_EQ(session_.GetNextDatagramFlowId(), 4u);
- EXPECT_EQ(session_.GetNextDatagramFlowId(), 6u);
-}
-
-TEST_P(QuicSpdySessionTestServer, GetNextDatagramFlowId) {
- if (!version().UsesHttp3()) {
- return;
- }
- EXPECT_EQ(session_.GetNextDatagramFlowId(), 1u);
- EXPECT_EQ(session_.GetNextDatagramFlowId(), 3u);
- EXPECT_EQ(session_.GetNextDatagramFlowId(), 5u);
- EXPECT_EQ(session_.GetNextDatagramFlowId(), 7u);
-}
-
TEST_P(QuicSpdySessionTestClient, H3DatagramSetting) {
if (!version().UsesHttp3()) {
return;
}
- SetQuicReloadableFlag(quic_h3_datagram, true);
+ session_.set_should_negotiate_h3_datagram(true);
// HTTP/3 datagrams aren't supported before SETTINGS are received.
EXPECT_FALSE(session_.h3_datagram_supported());
// Receive SETTINGS.
@@ -3506,52 +3440,11 @@ TEST_P(QuicSpdySessionTestClient, H3DatagramSetting) {
EXPECT_TRUE(session_.h3_datagram_supported());
}
-TEST_P(QuicSpdySessionTestClient, H3DatagramRegistration) {
- if (!version().UsesHttp3()) {
- return;
- }
- CompleteHandshake();
- SetQuicReloadableFlag(quic_h3_datagram, true);
- QuicSpdySessionPeer::SetH3DatagramSupported(&session_, true);
- SavingHttp3DatagramVisitor h3_datagram_visitor;
- QuicDatagramFlowId flow_id = session_.GetNextDatagramFlowId();
- ASSERT_EQ(QuicDataWriter::GetVarInt62Len(flow_id), 1);
- uint8_t datagram[256];
- datagram[0] = flow_id;
- for (size_t i = 1; i < ABSL_ARRAYSIZE(datagram); i++) {
- datagram[i] = i;
- }
- session_.RegisterHttp3FlowId(flow_id, &h3_datagram_visitor);
- session_.OnMessageReceived(absl::string_view(
- reinterpret_cast<const char*>(datagram), sizeof(datagram)));
- EXPECT_THAT(
- h3_datagram_visitor.received_h3_datagrams(),
- ElementsAre(SavingHttp3DatagramVisitor::SavedHttp3Datagram{
- flow_id, std::string(reinterpret_cast<const char*>(datagram + 1),
- sizeof(datagram) - 1)}));
- session_.UnregisterHttp3FlowId(flow_id);
-}
-
-TEST_P(QuicSpdySessionTestClient, SendHttp3Datagram) {
- if (!version().UsesHttp3()) {
- return;
- }
- CompleteHandshake();
- SetQuicReloadableFlag(quic_h3_datagram, true);
- QuicSpdySessionPeer::SetH3DatagramSupported(&session_, true);
- QuicDatagramFlowId flow_id = session_.GetNextDatagramFlowId();
- std::string h3_datagram_payload = {1, 2, 3, 4, 5, 6};
- EXPECT_CALL(*connection_, SendMessage(1, _, false))
- .WillOnce(Return(MESSAGE_STATUS_SUCCESS));
- EXPECT_EQ(session_.SendHttp3Datagram(flow_id, h3_datagram_payload),
- MESSAGE_STATUS_SUCCESS);
-}
-
TEST_P(QuicSpdySessionTestClient, WebTransportSetting) {
if (!version().UsesHttp3()) {
return;
}
- SetQuicReloadableFlag(quic_h3_datagram, true);
+ session_.set_should_negotiate_h3_datagram(true);
session_.set_supports_webtransport(true);
EXPECT_FALSE(session_.SupportsWebTransport());
@@ -3581,7 +3474,7 @@ TEST_P(QuicSpdySessionTestClient, WebTransportSettingSetToZero) {
if (!version().UsesHttp3()) {
return;
}
- SetQuicReloadableFlag(quic_h3_datagram, true);
+ session_.set_should_negotiate_h3_datagram(true);
session_.set_supports_webtransport(true);
EXPECT_FALSE(session_.SupportsWebTransport());
@@ -3611,7 +3504,7 @@ TEST_P(QuicSpdySessionTestServer, WebTransportSetting) {
if (!version().UsesHttp3()) {
return;
}
- SetQuicReloadableFlag(quic_h3_datagram, true);
+ session_.set_should_negotiate_h3_datagram(true);
session_.set_supports_webtransport(true);
EXPECT_FALSE(session_.SupportsWebTransport());
@@ -3628,7 +3521,7 @@ TEST_P(QuicSpdySessionTestServer, BufferingIncomingStreams) {
if (!version().UsesHttp3()) {
return;
}
- SetQuicReloadableFlag(quic_h3_datagram, true);
+ session_.set_should_negotiate_h3_datagram(true);
session_.set_supports_webtransport(true);
CompleteHandshake();
@@ -3661,7 +3554,7 @@ TEST_P(QuicSpdySessionTestServer, BufferingIncomingStreamsLimit) {
if (!version().UsesHttp3()) {
return;
}
- SetQuicReloadableFlag(quic_h3_datagram, true);
+ session_.set_should_negotiate_h3_datagram(true);
session_.set_supports_webtransport(true);
CompleteHandshake();
@@ -3702,7 +3595,7 @@ TEST_P(QuicSpdySessionTestServer, ResetOutgoingWebTransportStreams) {
if (!version().UsesHttp3()) {
return;
}
- SetQuicReloadableFlag(quic_h3_datagram, true);
+ session_.set_should_negotiate_h3_datagram(true);
session_.set_supports_webtransport(true);
CompleteHandshake();
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc
index 6ec48ca5896..bc23474e72a 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc
@@ -50,11 +50,6 @@ class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor {
stream_->OnUnrecoverableError(decoder->error(), decoder->error_detail());
}
- bool OnCancelPushFrame(const CancelPushFrame& /*frame*/) override {
- CloseConnectionOnWrongFrame("Cancel Push");
- return false;
- }
-
bool OnMaxPushIdFrame(const MaxPushIdFrame& /*frame*/) override {
CloseConnectionOnWrongFrame("Max Push Id");
return false;
@@ -113,42 +108,6 @@ class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor {
return stream_->OnHeadersFrameEnd();
}
- bool OnPushPromiseFrameStart(QuicByteCount header_length) override {
- if (!VersionUsesHttp3(stream_->transport_version())) {
- CloseConnectionOnWrongFrame("Push Promise");
- return false;
- }
- return stream_->OnPushPromiseFrameStart(header_length);
- }
-
- bool OnPushPromiseFramePushId(PushId push_id,
- QuicByteCount push_id_length,
- QuicByteCount header_block_length) override {
- if (!VersionUsesHttp3(stream_->transport_version())) {
- CloseConnectionOnWrongFrame("Push Promise");
- return false;
- }
- return stream_->OnPushPromiseFramePushId(push_id, push_id_length,
- header_block_length);
- }
-
- bool OnPushPromiseFramePayload(absl::string_view payload) override {
- QUICHE_DCHECK(!payload.empty());
- if (!VersionUsesHttp3(stream_->transport_version())) {
- CloseConnectionOnWrongFrame("Push Promise");
- return false;
- }
- return stream_->OnPushPromiseFramePayload(payload);
- }
-
- bool OnPushPromiseFrameEnd() override {
- if (!VersionUsesHttp3(stream_->transport_version())) {
- CloseConnectionOnWrongFrame("Push Promise");
- return false;
- }
- return stream_->OnPushPromiseFrameEnd();
- }
-
bool OnPriorityUpdateFrameStart(QuicByteCount /*header_length*/) override {
CloseConnectionOnWrongFrame("Priority update");
return false;
@@ -213,8 +172,7 @@ HttpDecoder::Options HttpDecoderOptionsForBidiStream(
}
} // namespace
-QuicSpdyStream::QuicSpdyStream(QuicStreamId id,
- QuicSpdySession* spdy_session,
+QuicSpdyStream::QuicSpdyStream(QuicStreamId id, QuicSpdySession* spdy_session,
StreamType type)
: QuicStream(id, spdy_session, /*is_static=*/false, type),
spdy_session_(spdy_session),
@@ -232,7 +190,11 @@ QuicSpdyStream::QuicSpdyStream(QuicStreamId id,
sequencer_offset_(0),
is_decoder_processing_input_(false),
ack_listener_(nullptr),
- last_sent_urgency_(kDefaultUrgency) {
+ last_sent_urgency_(kDefaultUrgency),
+ datagram_next_available_context_id_(spdy_session->perspective() ==
+ Perspective::IS_SERVER
+ ? kFirstDatagramContextIdServer
+ : kFirstDatagramContextIdClient) {
QUICHE_DCHECK_EQ(session()->connection(), spdy_session->connection());
QUICHE_DCHECK_EQ(transport_version(), spdy_session->transport_version());
QUICHE_DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id));
@@ -342,18 +304,9 @@ void QuicSpdyStream::WriteOrBufferBody(absl::string_view data, bool fin) {
spdy_session_->debug_visitor()->OnDataFrameSent(id(), data.length());
}
- // Write frame header.
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(data.length(), &buffer);
- unacked_frame_headers_offsets_.Add(
- send_buffer().stream_offset(),
- send_buffer().stream_offset() + header_length);
- QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id()
- << " is writing DATA frame header of length "
- << header_length;
- WriteOrBufferData(absl::string_view(buffer.get(), header_length), false,
- nullptr);
+ const bool success =
+ WriteDataFrameHeader(data.length(), /*force_write=*/true);
+ QUICHE_DCHECK(success);
// Write body.
QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id()
@@ -413,49 +366,71 @@ QuicConsumedData QuicSpdyStream::WritevBody(const struct iovec* iov,
return WriteBodySlices(storage.ToSpan(), fin);
}
+bool QuicSpdyStream::WriteDataFrameHeader(QuicByteCount data_length,
+ bool force_write) {
+ QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
+ QUICHE_DCHECK_GT(data_length, 0u);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ data_length,
+ spdy_session_->connection()->helper()->GetStreamSendBufferAllocator());
+ const bool can_write = CanWriteNewDataAfterData(header.size());
+ if (!can_write && !force_write) {
+ return false;
+ }
+
+ if (spdy_session_->debug_visitor()) {
+ spdy_session_->debug_visitor()->OnDataFrameSent(id(), data_length);
+ }
+
+ unacked_frame_headers_offsets_.Add(
+ send_buffer().stream_offset(),
+ send_buffer().stream_offset() + header.size());
+ QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id()
+ << " is writing DATA frame header of length "
+ << header.size();
+ if (can_write) {
+ // Save one copy and allocation if send buffer can accomodate the header.
+ QuicMemSlice header_slice(std::move(header));
+ WriteMemSlices(QuicMemSliceSpan(&header_slice), false);
+ } else {
+ QUICHE_DCHECK(force_write);
+ WriteOrBufferData(header.AsStringView(), false, nullptr);
+ }
+ return true;
+}
+
QuicConsumedData QuicSpdyStream::WriteBodySlices(QuicMemSliceSpan slices,
bool fin) {
if (!VersionUsesHttp3(transport_version()) || slices.empty()) {
return WriteMemSlices(slices, fin);
}
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(slices.total_length(), &buffer);
- if (!CanWriteNewDataAfterData(header_length)) {
+ QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection());
+ if (!WriteDataFrameHeader(slices.total_length(), /*force_write=*/false)) {
return {0, false};
}
- if (spdy_session_->debug_visitor()) {
- spdy_session_->debug_visitor()->OnDataFrameSent(id(),
- slices.total_length());
+ QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id()
+ << " is writing DATA frame payload of length "
+ << slices.total_length();
+ return WriteMemSlices(slices, fin);
+}
+
+QuicConsumedData QuicSpdyStream::WriteBodySlices(
+ absl::Span<QuicMemSlice> slices,
+ bool fin) {
+ if (!VersionUsesHttp3(transport_version()) || slices.empty()) {
+ return WriteMemSlices(slices, fin);
}
QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection());
+ const QuicByteCount data_size = MemSliceSpanTotalSize(slices);
+ if (!WriteDataFrameHeader(data_size, /*force_write=*/false)) {
+ return {0, false};
+ }
- // Write frame header.
-#if !defined(__ANDROID__)
- struct iovec header_iov = {static_cast<void*>(buffer.get()), header_length};
-#else
- struct iovec header_iov = {static_cast<void*>(buffer.get()),
- static_cast<__kernel_size_t>(header_length)};
-#endif
- QuicMemSliceStorage storage(
- &header_iov, 1,
- spdy_session_->connection()->helper()->GetStreamSendBufferAllocator(),
- GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size));
- unacked_frame_headers_offsets_.Add(
- send_buffer().stream_offset(),
- send_buffer().stream_offset() + header_length);
- QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id()
- << " is writing DATA frame header of length "
- << header_length;
- WriteMemSlices(storage.ToSpan(), false);
-
- // Write body.
QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id()
- << " is writing DATA frame payload of length "
- << slices.total_length();
+ << " is writing DATA frame payload of length " << data_size;
return WriteMemSlices(slices, fin);
}
@@ -598,10 +573,6 @@ void QuicSpdyStream::OnHeadersDecoded(QuicHeaderList headers,
OnStreamHeaderList(/* fin = */ false, headers_payload_length_, headers);
} else {
- if (debug_visitor) {
- debug_visitor->OnPushPromiseDecoded(id(), promised_stream_id, headers);
- }
-
spdy_session_->OnHeaderList(headers);
}
@@ -873,6 +844,10 @@ void QuicSpdyStream::OnClose() {
visitor->OnClose(this);
}
+ if (datagram_flow_id_.has_value()) {
+ spdy_session_->UnregisterHttp3DatagramFlowId(datagram_flow_id_.value());
+ }
+
if (web_transport_ != nullptr) {
web_transport_->CloseAllAssociatedStreams();
}
@@ -1085,50 +1060,6 @@ bool QuicSpdyStream::OnHeadersFrameEnd() {
return !sequencer()->IsClosed() && !reading_stopped();
}
-bool QuicSpdyStream::OnPushPromiseFrameStart(QuicByteCount header_length) {
- QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
- QUICHE_DCHECK(!qpack_decoded_headers_accumulator_);
-
- sequencer()->MarkConsumed(body_manager_.OnNonBody(header_length));
-
- return true;
-}
-
-bool QuicSpdyStream::OnPushPromiseFramePushId(
- PushId push_id,
- QuicByteCount push_id_length,
- QuicByteCount header_block_length) {
- QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
- QUICHE_DCHECK(!qpack_decoded_headers_accumulator_);
-
- if (spdy_session_->debug_visitor()) {
- spdy_session_->debug_visitor()->OnPushPromiseFrameReceived(
- id(), push_id, header_block_length);
- }
-
- // TODO(b/151749109): Check max push id and handle errors.
- spdy_session_->OnPushPromise(id(), push_id);
- sequencer()->MarkConsumed(body_manager_.OnNonBody(push_id_length));
-
- qpack_decoded_headers_accumulator_ =
- std::make_unique<QpackDecodedHeadersAccumulator>(
- id(), spdy_session_->qpack_decoder(), this,
- spdy_session_->max_inbound_header_list_size());
-
- return true;
-}
-
-bool QuicSpdyStream::OnPushPromiseFramePayload(absl::string_view payload) {
- spdy_session_->OnCompressedFrameSize(payload.length());
- return OnHeadersFramePayload(payload);
-}
-
-bool QuicSpdyStream::OnPushPromiseFrameEnd() {
- QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
-
- return OnHeadersFrameEnd();
-}
-
void QuicSpdyStream::OnWebTransportStreamFrameType(
QuicByteCount header_length,
WebTransportSessionId session_id) {
@@ -1237,6 +1168,16 @@ size_t QuicSpdyStream::WriteHeadersImpl(
return encoded_headers.size();
}
+bool QuicSpdyStream::CanWriteNewBodyData(QuicByteCount write_size) const {
+ QUICHE_DCHECK_NE(0u, write_size);
+ if (!VersionUsesHttp3(transport_version())) {
+ return CanWriteNewData();
+ }
+
+ return CanWriteNewDataAfterData(
+ HttpEncoder::GetDataFrameHeaderLength(write_size));
+}
+
void QuicSpdyStream::MaybeProcessReceivedWebTransportHeaders() {
if (!spdy_session_->SupportsWebTransport()) {
return;
@@ -1248,7 +1189,7 @@ void QuicSpdyStream::MaybeProcessReceivedWebTransportHeaders() {
std::string method;
std::string protocol;
- absl::optional<QuicDatagramFlowId> flow_id;
+ absl::optional<QuicDatagramStreamId> flow_id;
for (const auto& header : header_list_) {
const std::string& header_name = header.first;
const std::string& header_value = header.second;
@@ -1268,7 +1209,7 @@ void QuicSpdyStream::MaybeProcessReceivedWebTransportHeaders() {
if (flow_id.has_value() || header_value.empty()) {
return;
}
- QuicDatagramFlowId flow_id_out;
+ QuicDatagramStreamId flow_id_out;
if (!absl::SimpleAtoi(header_value, &flow_id_out)) {
return;
}
@@ -1281,8 +1222,18 @@ void QuicSpdyStream::MaybeProcessReceivedWebTransportHeaders() {
return;
}
+ RegisterHttp3DatagramFlowId(*flow_id);
+
web_transport_ =
- std::make_unique<WebTransportHttp3>(spdy_session_, this, id(), *flow_id);
+ std::make_unique<WebTransportHttp3>(spdy_session_, this, id());
+
+ // If we're in draft-ietf-masque-h3-datagram-00 mode, pretend we also received
+ // a REGISTER_DATAGRAM_NO_CONTEXT capsule with no extensions.
+ // TODO(b/181256914) remove this when we remove support for
+ // draft-ietf-masque-h3-datagram-00 in favor of later drafts.
+ RegisterHttp3DatagramContextId(/*context_id=*/absl::nullopt,
+ Http3DatagramContextExtensions(),
+ web_transport_.get());
}
void QuicSpdyStream::MaybeProcessSentWebTransportHeaders(
@@ -1304,11 +1255,14 @@ void QuicSpdyStream::MaybeProcessSentWebTransportHeaders(
return;
}
- QuicDatagramFlowId flow_id = spdy_session_->GetNextDatagramFlowId();
- headers["datagram-flow-id"] = absl::StrCat(flow_id);
+ QuicDatagramStreamId stream_id = id();
+ headers["datagram-flow-id"] = absl::StrCat(stream_id);
web_transport_ =
- std::make_unique<WebTransportHttp3>(spdy_session_, this, id(), flow_id);
+ std::make_unique<WebTransportHttp3>(spdy_session_, this, id());
+ RegisterHttp3DatagramContextId(web_transport_->context_id(),
+ Http3DatagramContextExtensions(),
+ web_transport_.get());
}
void QuicSpdyStream::OnCanWriteNewData() {
@@ -1369,5 +1323,206 @@ QuicSpdyStream::WebTransportDataStream::WebTransportDataStream(
: session_id(session_id),
adapter(stream->spdy_session_, stream, stream->sequencer()) {}
+MessageStatus QuicSpdyStream::SendHttp3Datagram(
+ absl::optional<QuicDatagramContextId> context_id,
+ absl::string_view payload) {
+ QuicDatagramStreamId stream_id =
+ datagram_flow_id_.has_value() ? datagram_flow_id_.value() : id();
+ return spdy_session_->SendHttp3Datagram(stream_id, context_id, payload);
+}
+
+void QuicSpdyStream::RegisterHttp3DatagramRegistrationVisitor(
+ Http3DatagramRegistrationVisitor* visitor) {
+ if (visitor == nullptr) {
+ QUIC_BUG(null datagram registration visitor)
+ << ENDPOINT << "Null datagram registration visitor for" << id();
+ return;
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "Registering datagram stream ID " << id();
+ datagram_registration_visitor_ = visitor;
+}
+
+void QuicSpdyStream::UnregisterHttp3DatagramRegistrationVisitor() {
+ QUIC_BUG_IF(h3 datagram unregister unknown stream ID,
+ datagram_registration_visitor_ == nullptr)
+ << ENDPOINT
+ << "Attempted to unregister unknown HTTP/3 datagram stream ID " << id();
+ QUIC_DLOG(INFO) << ENDPOINT << "Unregistering datagram stream ID " << id();
+ datagram_registration_visitor_ = nullptr;
+}
+
+void QuicSpdyStream::MoveHttp3DatagramRegistration(
+ Http3DatagramRegistrationVisitor* visitor) {
+ QUIC_BUG_IF(h3 datagram move unknown stream ID,
+ datagram_registration_visitor_ == nullptr)
+ << ENDPOINT << "Attempted to move unknown HTTP/3 datagram stream ID "
+ << id();
+ QUIC_DLOG(INFO) << ENDPOINT << "Moving datagram stream ID " << id();
+ datagram_registration_visitor_ = visitor;
+}
+
+void QuicSpdyStream::RegisterHttp3DatagramContextId(
+ absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& /*extensions*/,
+ Http3DatagramVisitor* visitor) {
+ if (visitor == nullptr) {
+ QUIC_BUG(null datagram visitor)
+ << ENDPOINT << "Null datagram visitor for stream ID " << id()
+ << " context ID " << (context_id.has_value() ? context_id.value() : 0);
+ return;
+ }
+ if (datagram_registration_visitor_ == nullptr) {
+ QUIC_BUG(context registration without registration visitor)
+ << ENDPOINT << "Cannot register context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " without registration visitor for stream ID " << id();
+ return;
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "Registering datagram context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " with stream ID " << id();
+ if (context_id.has_value()) {
+ if (datagram_no_context_visitor_ != nullptr) {
+ QUIC_BUG(h3 datagram context ID mix1)
+ << ENDPOINT
+ << "Attempted to mix registrations without and with context IDs "
+ "for stream ID "
+ << id();
+ return;
+ }
+ auto insertion_result =
+ datagram_context_visitors_.insert({context_id.value(), visitor});
+ QUIC_BUG_IF(h3 datagram double context registration,
+ !insertion_result.second)
+ << ENDPOINT << "Attempted to doubly register HTTP/3 stream ID " << id()
+ << " context ID " << context_id.value();
+ return;
+ }
+ // Registration without a context ID.
+ if (!datagram_context_visitors_.empty()) {
+ QUIC_BUG(h3 datagram context ID mix2)
+ << ENDPOINT
+ << "Attempted to mix registrations with and without context IDs "
+ "for stream ID "
+ << id();
+ return;
+ }
+ if (datagram_no_context_visitor_ != nullptr) {
+ QUIC_BUG(h3 datagram double no context registration)
+ << ENDPOINT << "Attempted to doubly register HTTP/3 stream ID " << id()
+ << " with no context ID";
+ return;
+ }
+ datagram_no_context_visitor_ = visitor;
+}
+
+void QuicSpdyStream::UnregisterHttp3DatagramContextId(
+ absl::optional<QuicDatagramContextId> context_id) {
+ if (datagram_registration_visitor_ == nullptr) {
+ QUIC_BUG(context unregistration without registration visitor)
+ << ENDPOINT << "Cannot unregister context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " without registration visitor for stream ID " << id();
+ return;
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "Unregistering datagram context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " with stream ID " << id();
+ if (context_id.has_value()) {
+ size_t num_erased = datagram_context_visitors_.erase(context_id.value());
+ QUIC_BUG_IF(h3 datagram unregister unknown context, num_erased != 1)
+ << "Attempted to unregister unknown HTTP/3 context ID "
+ << context_id.value() << " on stream ID " << id();
+ return;
+ }
+ // Unregistration without a context ID.
+ QUIC_BUG_IF(h3 datagram unknown context unregistration,
+ datagram_no_context_visitor_ == nullptr)
+ << "Attempted to unregister unknown no context on HTTP/3 stream ID "
+ << id();
+ datagram_no_context_visitor_ = nullptr;
+}
+
+void QuicSpdyStream::MoveHttp3DatagramContextIdRegistration(
+ absl::optional<QuicDatagramContextId> context_id,
+ Http3DatagramVisitor* visitor) {
+ if (datagram_registration_visitor_ == nullptr) {
+ QUIC_BUG(context move without registration visitor)
+ << ENDPOINT << "Cannot move context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " without registration visitor for stream ID " << id();
+ return;
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "Moving datagram context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " with stream ID " << id();
+ if (context_id.has_value()) {
+ QUIC_BUG_IF(h3 datagram move unknown context,
+ !datagram_context_visitors_.contains(context_id.value()))
+ << ENDPOINT << "Attempted to move unknown context ID "
+ << context_id.value() << " on stream ID " << id();
+ datagram_context_visitors_[context_id.value()] = visitor;
+ return;
+ }
+ // Move without a context ID.
+ QUIC_BUG_IF(h3 datagram unknown context move,
+ datagram_no_context_visitor_ == nullptr)
+ << "Attempted to move unknown no context on HTTP/3 stream ID " << id();
+ datagram_no_context_visitor_ = visitor;
+}
+
+void QuicSpdyStream::SetMaxDatagramTimeInQueue(
+ QuicTime::Delta max_time_in_queue) {
+ spdy_session_->SetMaxDatagramTimeInQueueForStreamId(id(), max_time_in_queue);
+}
+
+QuicDatagramContextId QuicSpdyStream::GetNextDatagramContextId() {
+ QuicDatagramContextId result = datagram_next_available_context_id_;
+ datagram_next_available_context_id_ += kDatagramContextIdIncrement;
+ return result;
+}
+
+void QuicSpdyStream::OnDatagramReceived(QuicDataReader* reader) {
+ absl::optional<QuicDatagramContextId> context_id;
+ const bool context_id_present = !datagram_context_visitors_.empty();
+ Http3DatagramVisitor* visitor;
+ if (context_id_present) {
+ QuicDatagramContextId parsed_context_id;
+ if (!reader->ReadVarInt62(&parsed_context_id)) {
+ QUIC_DLOG(ERROR) << "Failed to parse context ID in received HTTP/3 "
+ "datagram on stream ID "
+ << id();
+ return;
+ }
+ context_id = parsed_context_id;
+ auto it = datagram_context_visitors_.find(parsed_context_id);
+ if (it == datagram_context_visitors_.end()) {
+ // TODO(b/181256914) buffer unknown HTTP/3 datagrams for a short
+ // period of time in case they were reordered.
+ QUIC_DLOG(ERROR) << "Received unknown HTTP/3 datagram context ID "
+ << parsed_context_id << " on stream ID " << id();
+ return;
+ }
+ visitor = it->second;
+ } else {
+ if (datagram_no_context_visitor_ == nullptr) {
+ // TODO(b/181256914) buffer unknown HTTP/3 datagrams for a short
+ // period of time in case they were reordered.
+ QUIC_DLOG(ERROR)
+ << "Received HTTP/3 datagram without any registrations on stream ID "
+ << id();
+ return;
+ }
+ visitor = datagram_no_context_visitor_;
+ }
+ absl::string_view payload = reader->ReadRemainingPayload();
+ visitor->OnHttp3Datagram(id(), context_id, payload);
+}
+
+void QuicSpdyStream::RegisterHttp3DatagramFlowId(QuicDatagramStreamId flow_id) {
+ datagram_flow_id_ = flow_id;
+ spdy_session_->RegisterHttp3DatagramFlowId(datagram_flow_id_.value(), id());
+}
+
#undef ENDPOINT // undef for jumbo builds
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h
index 5f96d8395cf..4df1196abeb 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h
@@ -16,7 +16,9 @@
#include <memory>
#include <string>
+#include "absl/base/attributes.h"
#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
#include "quic/core/http/http_decoder.h"
#include "quic/core/http/http_encoder.h"
#include "quic/core/http/quic_header_list.h"
@@ -44,6 +46,8 @@ class QuicStreamPeer;
class QuicSpdySession;
class WebTransportHttp3;
+class QUIC_EXPORT_PRIVATE Http3DatagramContextExtensions {};
+
// A QUIC stream that can send and receive HTTP2 (SPDY) headers.
class QUIC_EXPORT_PRIVATE QuicSpdyStream
: public QuicStream,
@@ -158,6 +162,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream
// Does the same thing as WriteOrBufferBody except this method takes
// memslicespan as the data input. Right now it only calls WriteMemSlices.
QuicConsumedData WriteBodySlices(QuicMemSliceSpan slices, bool fin);
+ QuicConsumedData WriteBodySlices(absl::Span<QuicMemSlice> slices, bool fin);
// Marks the trailers as consumed. This applies to the case where this object
// receives headers and trailers as QuicHeaderLists via calls to
@@ -244,6 +249,98 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream
// error, and returns false.
bool AssertNotWebTransportDataStream(absl::string_view operation);
+ // Indicates whether a call to WriteBodySlices will be successful and not
+ // rejected due to buffer being full. |write_size| must be non-zero.
+ bool CanWriteNewBodyData(QuicByteCount write_size) const;
+
+ // Sends an HTTP/3 datagram. The stream and context IDs are not part of
+ // |payload|.
+ MessageStatus SendHttp3Datagram(
+ absl::optional<QuicDatagramContextId> context_id,
+ absl::string_view payload);
+
+ class QUIC_EXPORT_PRIVATE Http3DatagramVisitor {
+ public:
+ virtual ~Http3DatagramVisitor() {}
+
+ // Called when an HTTP/3 datagram is received. |payload| does not contain
+ // the stream or context IDs. Note that this contains the stream ID even if
+ // flow IDs from draft-ietf-masque-h3-datagram-00 are in use.
+ virtual void OnHttp3Datagram(
+ QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ absl::string_view payload) = 0;
+ };
+
+ class QUIC_EXPORT_PRIVATE Http3DatagramRegistrationVisitor {
+ public:
+ virtual ~Http3DatagramRegistrationVisitor() {}
+
+ // Called when a REGISTER_DATAGRAM_CONTEXT or REGISTER_DATAGRAM_NO_CONTEXT
+ // capsule is received. Note that this contains the stream ID even if flow
+ // IDs from draft-ietf-masque-h3-datagram-00 are in use.
+ virtual void OnContextReceived(
+ QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions) = 0;
+
+ // Called when a CLOSE_DATAGRAM_CONTEXT capsule is received. Note that this
+ // contains the stream ID even if flow IDs from
+ // draft-ietf-masque-h3-datagram-00 are in use.
+ virtual void OnContextClosed(
+ QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions) = 0;
+ };
+
+ // Registers |visitor| to receive HTTP/3 datagram context registrations. This
+ // must not be called without first calling
+ // UnregisterHttp3DatagramRegistrationVisitor. |visitor| must be valid until a
+ // corresponding call to UnregisterHttp3DatagramRegistrationVisitor.
+ void RegisterHttp3DatagramRegistrationVisitor(
+ Http3DatagramRegistrationVisitor* visitor);
+
+ // Unregisters for HTTP/3 datagram context registrations. Must not be called
+ // unless previously registered.
+ void UnregisterHttp3DatagramRegistrationVisitor();
+
+ // Moves an HTTP/3 datagram registration to a different visitor. Mainly meant
+ // to be used by the visitors' move operators.
+ void MoveHttp3DatagramRegistration(Http3DatagramRegistrationVisitor* visitor);
+
+ // Registers |visitor| to receive HTTP/3 datagrams for optional context ID
+ // |context_id|. This must not be called on a previously registered context ID
+ // without first calling UnregisterHttp3DatagramContextId. |visitor| must be
+ // valid until a corresponding call to UnregisterHttp3DatagramContextId. If
+ // this method is called multiple times, the context ID MUST either be always
+ // present, or always absent.
+ void RegisterHttp3DatagramContextId(
+ absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions,
+ Http3DatagramVisitor* visitor);
+
+ // Unregisters an HTTP/3 datagram context ID. Must be called on a previously
+ // registered context.
+ void UnregisterHttp3DatagramContextId(
+ absl::optional<QuicDatagramContextId> context_id);
+
+ // Moves an HTTP/3 datagram context ID to a different visitor. Mainly meant
+ // to be used by the visitors' move operators.
+ void MoveHttp3DatagramContextIdRegistration(
+ absl::optional<QuicDatagramContextId> context_id,
+ Http3DatagramVisitor* visitor);
+
+ // Sets max datagram time in queue.
+ void SetMaxDatagramTimeInQueue(QuicTime::Delta max_time_in_queue);
+
+ // Generates a new HTTP/3 datagram context ID for this stream. A datagram
+ // registration visitor must be currently registered on this stream.
+ QuicDatagramContextId GetNextDatagramContextId();
+
+ void OnDatagramReceived(QuicDataReader* reader);
+
+ void RegisterHttp3DatagramFlowId(QuicDatagramStreamId flow_id);
+
protected:
// Called when the received headers are too large. By default this will
// reset the stream.
@@ -292,12 +389,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream
QuicByteCount payload_length);
bool OnHeadersFramePayload(absl::string_view payload);
bool OnHeadersFrameEnd();
- bool OnPushPromiseFrameStart(QuicByteCount header_length);
- bool OnPushPromiseFramePushId(PushId push_id,
- QuicByteCount push_id_length,
- QuicByteCount header_block_length);
- bool OnPushPromiseFramePayload(absl::string_view payload);
- bool OnPushPromiseFrameEnd();
void OnWebTransportStreamFrameType(QuicByteCount header_length,
WebTransportSessionId session_id);
bool OnUnknownFrameStart(uint64_t frame_type,
@@ -314,6 +405,11 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream
void MaybeProcessSentWebTransportHeaders(spdy::SpdyHeaderBlock& headers);
void MaybeProcessReceivedWebTransportHeaders();
+ // Writes HTTP/3 DATA frame header. If |force_write| is true, use
+ // WriteOrBufferData if send buffer cannot accomodate the header + data.
+ ABSL_MUST_USE_RESULT bool WriteDataFrameHeader(QuicByteCount data_length,
+ bool force_write);
+
QuicSpdySession* spdy_session_;
bool on_body_available_called_because_sequencer_is_closed_;
@@ -382,6 +478,14 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream
// If this stream is a WebTransport data stream, |web_transport_data_|
// contains all of the associated metadata.
std::unique_ptr<WebTransportDataStream> web_transport_data_;
+
+ // HTTP/3 Datagram support.
+ Http3DatagramRegistrationVisitor* datagram_registration_visitor_ = nullptr;
+ Http3DatagramVisitor* datagram_no_context_visitor_ = nullptr;
+ absl::optional<QuicDatagramStreamId> datagram_flow_id_;
+ QuicDatagramContextId datagram_next_available_context_id_;
+ absl::flat_hash_map<QuicDatagramContextId, Http3DatagramVisitor*>
+ datagram_context_visitors_;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc
index 8b814b8400b..c19dc3c8431 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc
@@ -19,12 +19,12 @@
#include "quic/core/http/spdy_utils.h"
#include "quic/core/http/web_transport_http3.h"
#include "quic/core/quic_connection.h"
+#include "quic/core/quic_simple_buffer_allocator.h"
#include "quic/core/quic_stream_sequencer_buffer.h"
#include "quic/core/quic_utils.h"
#include "quic/core/quic_versions.h"
#include "quic/core/quic_write_blocked_list.h"
#include "quic/platform/api/quic_expect_bug.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/qpack/qpack_test_utils.h"
#include "quic/test_tools/quic_config_peer.h"
@@ -259,8 +259,16 @@ class TestSession : public MockQuicSpdySession {
bool ShouldNegotiateWebTransport() override { return enable_webtransport_; }
void EnableWebTransport() { enable_webtransport_ = true; }
+ bool ShouldNegotiateHttp3Datagram() override {
+ return should_negotiate_h3_datagram_;
+ }
+ void set_should_negotiate_h3_datagram(bool value) {
+ should_negotiate_h3_datagram_ = value;
+ }
+
private:
bool enable_webtransport_ = false;
+ bool should_negotiate_h3_datagram_ = false;
StrictMock<TestCryptoStream> crypto_stream_;
};
@@ -447,40 +455,10 @@ class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> {
return absl::StrCat(headers_frame_header, payload);
}
- // Construct PUSH_PROMISE frame with given payload.
- // TODO(b/171463363): Remove.
- std::string SerializePushPromiseFrame(PushId push_id,
- absl::string_view headers) {
- const QuicByteCount payload_length =
- QuicDataWriter::GetVarInt62Len(push_id) + headers.length();
-
- const QuicByteCount length_without_headers =
- QuicDataWriter::GetVarInt62Len(
- static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE)) +
- QuicDataWriter::GetVarInt62Len(payload_length) +
- QuicDataWriter::GetVarInt62Len(push_id);
-
- std::string push_promise_frame(length_without_headers, '\0');
- QuicDataWriter writer(length_without_headers, &*push_promise_frame.begin());
-
- QUICHE_CHECK(writer.WriteVarInt62(
- static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE)));
- QUICHE_CHECK(writer.WriteVarInt62(payload_length));
- QUICHE_CHECK(writer.WriteVarInt62(push_id));
- QUICHE_CHECK_EQ(0u, writer.remaining());
-
- absl::StrAppend(&push_promise_frame, headers);
-
- return push_promise_frame;
- }
-
std::string DataFrame(absl::string_view payload) {
- std::unique_ptr<char[]> data_buffer;
- QuicByteCount data_frame_header_length =
- HttpEncoder::SerializeDataFrameHeader(payload.length(), &data_buffer);
- absl::string_view data_frame_header(data_buffer.get(),
- data_frame_header_length);
- return absl::StrCat(data_frame_header, payload);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ payload.length(), SimpleBufferAllocator::Get());
+ return absl::StrCat(header.AsStringView(), payload);
}
std::string UnknownFrame(uint64_t frame_type, absl::string_view payload) {
@@ -1005,11 +983,10 @@ TEST_P(QuicSpdyStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) {
std::string data;
if (UsesHttp3()) {
- std::unique_ptr<char[]> buffer;
- header_length =
- HttpEncoder::SerializeDataFrameHeader(body.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
- data = header + body;
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), SimpleBufferAllocator::Get());
+ data = absl::StrCat(header.AsStringView(), body);
+ header_length = header.size();
} else {
data = body;
}
@@ -1049,11 +1026,10 @@ TEST_P(QuicSpdyStreamTest, StreamFlowControlWindowUpdate) {
std::string data;
if (UsesHttp3()) {
- std::unique_ptr<char[]> buffer;
- header_length =
- HttpEncoder::SerializeDataFrameHeader(body.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
- data = header + body;
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), SimpleBufferAllocator::Get());
+ data = absl::StrCat(header.AsStringView(), body);
+ header_length = header.size();
} else {
data = body;
}
@@ -1115,16 +1091,13 @@ TEST_P(QuicSpdyStreamTest, ConnectionFlowControlWindowUpdate) {
if (UsesHttp3()) {
body = std::string(kWindow / 4 - 2, 'a');
- std::unique_ptr<char[]> buffer;
- header_length =
- HttpEncoder::SerializeDataFrameHeader(body.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
- data = header + body;
- std::unique_ptr<char[]> buffer2;
- QuicByteCount header_length2 =
- HttpEncoder::SerializeDataFrameHeader(body2.length(), &buffer2);
- std::string header2 = std::string(buffer2.get(), header_length2);
- data2 = header2 + body2;
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), SimpleBufferAllocator::Get());
+ data = absl::StrCat(header.AsStringView(), body);
+ header_length = header.size();
+ QuicBuffer header2 = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), SimpleBufferAllocator::Get());
+ data2 = absl::StrCat(header2.AsStringView(), body2);
} else {
body = std::string(kWindow / 4, 'a');
data = body;
@@ -1595,8 +1568,9 @@ TEST_P(QuicSpdyStreamTest, WritingTrailersFinalOffset) {
std::string body(1024, 'x'); // 1 kB
QuicByteCount header_length = 0;
if (UsesHttp3()) {
- std::unique_ptr<char[]> buf;
- header_length = HttpEncoder::SerializeDataFrameHeader(body.length(), &buf);
+ header_length = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), SimpleBufferAllocator::Get())
+ .size();
}
stream_->WriteOrBufferBody(body, false);
@@ -1939,30 +1913,26 @@ TEST_P(QuicSpdyStreamTest, HeadersAckNotReportedWriteOrBufferBody) {
stream_->WriteOrBufferBody(body, false);
stream_->WriteOrBufferBody(body2, true);
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
-
- header_length =
- HttpEncoder::SerializeDataFrameHeader(body2.length(), &buffer);
- std::string header2 = std::string(buffer.get(), header_length);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), SimpleBufferAllocator::Get());
+ QuicBuffer header2 = HttpEncoder::SerializeDataFrameHeader(
+ body2.length(), SimpleBufferAllocator::Get());
EXPECT_CALL(*mock_ack_listener, OnPacketAcked(body.length(), _));
- QuicStreamFrame frame(stream_->id(), false, 0, header + body);
+ QuicStreamFrame frame(stream_->id(), false, 0,
+ absl::StrCat(header.AsStringView(), body));
EXPECT_TRUE(session_->OnFrameAcked(QuicFrame(frame), QuicTime::Delta::Zero(),
QuicTime::Zero()));
EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _));
- QuicStreamFrame frame2(stream_->id(), false, header.length() + body.length(),
- header2);
+ QuicStreamFrame frame2(stream_->id(), false, header.size() + body.length(),
+ header2.AsStringView());
EXPECT_TRUE(session_->OnFrameAcked(QuicFrame(frame2), QuicTime::Delta::Zero(),
QuicTime::Zero()));
EXPECT_CALL(*mock_ack_listener, OnPacketAcked(body2.length(), _));
QuicStreamFrame frame3(stream_->id(), true,
- header.length() + body.length() + header2.length(),
- body2);
+ header.size() + body.length() + header2.size(), body2);
EXPECT_TRUE(session_->OnFrameAcked(QuicFrame(frame3), QuicTime::Delta::Zero(),
QuicTime::Zero()));
@@ -2769,71 +2739,6 @@ TEST_P(QuicSpdyStreamIncrementalConsumptionTest, UnknownFramesInterleaved) {
EXPECT_EQ(unknown_frame4.size(), NewlyConsumedBytes());
}
-// TODO(b/171463363): Remove.
-TEST_P(QuicSpdyStreamTest, PushPromiseOnDataStream) {
- Initialize(kShouldProcessData);
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- return;
- }
- if (!UsesHttp3()) {
- return;
- }
-
- StrictMock<MockHttp3DebugVisitor> debug_visitor;
- session_->set_debug_visitor(&debug_visitor);
-
- SpdyHeaderBlock pushed_headers;
- pushed_headers["foo"] = "bar";
- std::string headers = EncodeQpackHeaders(pushed_headers);
-
- const QuicStreamId push_id = 1;
- std::string data = SerializePushPromiseFrame(push_id, headers);
- QuicStreamFrame frame(stream_->id(), false, 0, data);
-
- EXPECT_CALL(debug_visitor, OnPushPromiseFrameReceived(stream_->id(), push_id,
- headers.length()));
- EXPECT_CALL(debug_visitor,
- OnPushPromiseDecoded(stream_->id(), push_id,
- AsHeaderList(pushed_headers)));
- EXPECT_CALL(*session_,
- OnPromiseHeaderList(stream_->id(), push_id, headers.length(), _));
- stream_->OnStreamFrame(frame);
-}
-
-// Regression test for b/152518220.
-// TODO(b/171463363): Remove.
-TEST_P(QuicSpdyStreamTest,
- OnStreamHeaderBlockArgumentDoesNotIncludePushedHeaderBlock) {
- Initialize(kShouldProcessData);
- if (GetQuicReloadableFlag(quic_error_on_http3_push)) {
- return;
- }
- if (!UsesHttp3()) {
- return;
- }
-
- std::string pushed_headers = EncodeQpackHeaders({{"foo", "bar"}});
- const QuicStreamId push_id = 1;
- std::string push_promise_frame =
- SerializePushPromiseFrame(push_id, pushed_headers);
- QuicStreamOffset offset = 0;
- QuicStreamFrame frame1(stream_->id(), /* fin = */ false, offset,
- push_promise_frame);
- offset += push_promise_frame.length();
-
- EXPECT_CALL(*session_, OnPromiseHeaderList(stream_->id(), push_id,
- pushed_headers.length(), _));
- stream_->OnStreamFrame(frame1);
-
- std::string headers =
- EncodeQpackHeaders({{":method", "GET"}, {":path", "/"}});
- std::string headers_frame = HeadersFrame(headers);
- QuicStreamFrame frame2(stream_->id(), /* fin = */ false, offset,
- headers_frame);
- stream_->OnStreamFrame(frame2);
- EXPECT_EQ(headers.length(), stream_->headers_payload_length());
-}
-
// Close connection if a DATA frame is received before a HEADERS frame.
TEST_P(QuicSpdyStreamTest, DataBeforeHeaders) {
if (!UsesHttp3()) {
@@ -3118,6 +3023,7 @@ TEST_P(QuicSpdyStreamTest, ProcessOutgoingWebTransportHeaders) {
}
InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT);
+ session_->set_should_negotiate_h3_datagram(true);
session_->EnableWebTransport();
QuicSpdySessionPeer::EnableWebTransport(*session_);
@@ -3128,7 +3034,7 @@ TEST_P(QuicSpdyStreamTest, ProcessOutgoingWebTransportHeaders) {
spdy::SpdyHeaderBlock headers;
headers[":method"] = "CONNECT";
headers[":protocol"] = "webtransport";
- headers["datagram-flow-id"] = absl::StrCat(session_->GetNextDatagramFlowId());
+ headers["datagram-flow-id"] = absl::StrCat(stream_->id());
stream_->WriteHeaders(std::move(headers), /*fin=*/false, nullptr);
ASSERT_TRUE(stream_->web_transport() != nullptr);
EXPECT_EQ(stream_->id(), stream_->web_transport()->id());
@@ -3140,13 +3046,13 @@ TEST_P(QuicSpdyStreamTest, ProcessIncomingWebTransportHeaders) {
}
Initialize(kShouldProcessData);
+ session_->set_should_negotiate_h3_datagram(true);
session_->EnableWebTransport();
QuicSpdySessionPeer::EnableWebTransport(*session_);
headers_[":method"] = "CONNECT";
headers_[":protocol"] = "webtransport";
- headers_["datagram-flow-id"] =
- absl::StrCat(session_->GetNextDatagramFlowId());
+ headers_["datagram-flow-id"] = absl::StrCat(stream_->id());
stream_->OnStreamHeadersPriority(
spdy::SpdyStreamPrecedence(kV3HighestPriority));
@@ -3158,6 +3064,155 @@ TEST_P(QuicSpdyStreamTest, ProcessIncomingWebTransportHeaders) {
EXPECT_EQ(stream_->id(), stream_->web_transport()->id());
}
+TEST_P(QuicSpdyStreamTest,
+ ProcessIncomingWebTransportHeadersWithMismatchedFlowId) {
+ if (!UsesHttp3()) {
+ return;
+ }
+ // TODO(b/181256914) Remove this test when we deprecate
+ // draft-ietf-masque-h3-datagram-00 in favor of later drafts.
+
+ Initialize(kShouldProcessData);
+ session_->set_should_negotiate_h3_datagram(true);
+ session_->EnableWebTransport();
+ QuicSpdySessionPeer::EnableWebTransport(*session_);
+
+ headers_[":method"] = "CONNECT";
+ headers_[":protocol"] = "webtransport";
+ headers_["datagram-flow-id"] = "2";
+
+ stream_->OnStreamHeadersPriority(
+ spdy::SpdyStreamPrecedence(kV3HighestPriority));
+ ProcessHeaders(false, headers_);
+ EXPECT_EQ("", stream_->data());
+ EXPECT_FALSE(stream_->header_list().empty());
+ EXPECT_FALSE(stream_->IsDoneReading());
+ ASSERT_TRUE(stream_->web_transport() != nullptr);
+ EXPECT_EQ(stream_->id(), stream_->web_transport()->id());
+}
+
+TEST_P(QuicSpdyStreamTest, GetNextDatagramContextIdClient) {
+ if (!UsesHttp3()) {
+ return;
+ }
+ InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT);
+ ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor> visitor;
+ stream_->RegisterHttp3DatagramRegistrationVisitor(&visitor);
+ EXPECT_EQ(stream_->GetNextDatagramContextId(), 0u);
+ EXPECT_EQ(stream_->GetNextDatagramContextId(), 2u);
+ EXPECT_EQ(stream_->GetNextDatagramContextId(), 4u);
+ EXPECT_EQ(stream_->GetNextDatagramContextId(), 6u);
+ stream_->UnregisterHttp3DatagramRegistrationVisitor();
+}
+
+TEST_P(QuicSpdyStreamTest, GetNextDatagramContextIdServer) {
+ if (!UsesHttp3()) {
+ return;
+ }
+ InitializeWithPerspective(kShouldProcessData, Perspective::IS_SERVER);
+ ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor> visitor;
+ stream_->RegisterHttp3DatagramRegistrationVisitor(&visitor);
+ EXPECT_EQ(stream_->GetNextDatagramContextId(), 1u);
+ EXPECT_EQ(stream_->GetNextDatagramContextId(), 3u);
+ EXPECT_EQ(stream_->GetNextDatagramContextId(), 5u);
+ EXPECT_EQ(stream_->GetNextDatagramContextId(), 7u);
+ stream_->UnregisterHttp3DatagramRegistrationVisitor();
+}
+
+TEST_P(QuicSpdyStreamTest, H3DatagramRegistrationWithoutContext) {
+ if (!UsesHttp3()) {
+ return;
+ }
+ Initialize(kShouldProcessData);
+ session_->set_should_negotiate_h3_datagram(true);
+ QuicSpdySessionPeer::SetH3DatagramSupported(session_.get(), true);
+ session_->RegisterHttp3DatagramFlowId(stream_->id(), stream_->id());
+ ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor>
+ h3_datagram_registration_visitor;
+ SavingHttp3DatagramVisitor h3_datagram_visitor;
+ absl::optional<QuicDatagramContextId> context_id;
+ Http3DatagramContextExtensions extensions;
+ ASSERT_EQ(QuicDataWriter::GetVarInt62Len(stream_->id()), 1);
+ std::array<char, 256> datagram;
+ datagram[0] = stream_->id();
+ for (size_t i = 1; i < datagram.size(); i++) {
+ datagram[i] = i;
+ }
+ stream_->RegisterHttp3DatagramRegistrationVisitor(
+ &h3_datagram_registration_visitor);
+ stream_->RegisterHttp3DatagramContextId(context_id, extensions,
+ &h3_datagram_visitor);
+ session_->OnMessageReceived(
+ absl::string_view(datagram.data(), datagram.size()));
+ EXPECT_THAT(h3_datagram_visitor.received_h3_datagrams(),
+ ElementsAre(SavingHttp3DatagramVisitor::SavedHttp3Datagram{
+ stream_->id(), context_id,
+ std::string(&datagram[1], datagram.size() - 1)}));
+ // Test move.
+ ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor>
+ h3_datagram_registration_visitor2;
+ stream_->MoveHttp3DatagramRegistration(&h3_datagram_registration_visitor2);
+ SavingHttp3DatagramVisitor h3_datagram_visitor2;
+ stream_->MoveHttp3DatagramContextIdRegistration(context_id,
+ &h3_datagram_visitor2);
+ EXPECT_TRUE(h3_datagram_visitor2.received_h3_datagrams().empty());
+ session_->OnMessageReceived(
+ absl::string_view(datagram.data(), datagram.size()));
+ EXPECT_THAT(h3_datagram_visitor2.received_h3_datagrams(),
+ ElementsAre(SavingHttp3DatagramVisitor::SavedHttp3Datagram{
+ stream_->id(), context_id,
+ std::string(&datagram[1], datagram.size() - 1)}));
+ // Cleanup.
+ stream_->UnregisterHttp3DatagramContextId(context_id);
+ stream_->UnregisterHttp3DatagramRegistrationVisitor();
+ session_->UnregisterHttp3DatagramFlowId(stream_->id());
+}
+
+TEST_P(QuicSpdyStreamTest, H3DatagramRegistrationWithContext) {
+ if (!UsesHttp3()) {
+ return;
+ }
+ Initialize(kShouldProcessData);
+ session_->set_should_negotiate_h3_datagram(true);
+ QuicSpdySessionPeer::SetH3DatagramSupported(session_.get(), true);
+ session_->RegisterHttp3DatagramFlowId(stream_->id(), stream_->id());
+ ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor>
+ h3_datagram_registration_visitor;
+ SavingHttp3DatagramVisitor h3_datagram_visitor;
+ absl::optional<QuicDatagramContextId> context_id = 42;
+ Http3DatagramContextExtensions extensions;
+ stream_->RegisterHttp3DatagramRegistrationVisitor(
+ &h3_datagram_registration_visitor);
+ stream_->RegisterHttp3DatagramContextId(context_id, extensions,
+ &h3_datagram_visitor);
+ // Test move.
+ ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor>
+ h3_datagram_registration_visitor2;
+ stream_->MoveHttp3DatagramRegistration(&h3_datagram_registration_visitor2);
+ SavingHttp3DatagramVisitor h3_datagram_visitor2;
+ stream_->MoveHttp3DatagramContextIdRegistration(context_id,
+ &h3_datagram_visitor2);
+ // Cleanup.
+ stream_->UnregisterHttp3DatagramContextId(context_id);
+ stream_->UnregisterHttp3DatagramRegistrationVisitor();
+ session_->UnregisterHttp3DatagramFlowId(stream_->id());
+}
+
+TEST_P(QuicSpdyStreamTest, SendHttp3Datagram) {
+ if (!UsesHttp3()) {
+ return;
+ }
+ Initialize(kShouldProcessData);
+ session_->set_should_negotiate_h3_datagram(true);
+ QuicSpdySessionPeer::SetH3DatagramSupported(session_.get(), true);
+ absl::optional<QuicDatagramContextId> context_id;
+ std::string h3_datagram_payload = {1, 2, 3, 4, 5, 6};
+ EXPECT_CALL(*connection_, SendMessage(1, _, false))
+ .WillOnce(Return(MESSAGE_STATUS_SUCCESS));
+ EXPECT_EQ(stream_->SendHttp3Datagram(context_id, h3_datagram_payload),
+ MESSAGE_STATUS_SUCCESS);
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc
index 28b463afebe..27f5314b05f 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc
@@ -16,7 +16,6 @@
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
-#include "quic/platform/api/quic_map_util.h"
#include "common/quiche_text_utils.h"
#include "spdy/core/spdy_protocol.h"
@@ -78,7 +77,7 @@ bool SpdyUtils::CopyAndValidateHeaders(const QuicHeaderList& header_list,
headers->AppendValueOrAddHeader(name, p.second);
}
- if (QuicContainsKey(*headers, "content-length") &&
+ if (headers->contains("content-length") &&
!ExtractContentLengthFromHeaders(content_length, headers)) {
return false;
}
@@ -155,7 +154,7 @@ bool SpdyUtils::PopulateHeaderBlockFromUrl(const std::string url,
}
// static
-absl::optional<QuicDatagramFlowId> SpdyUtils::ParseDatagramFlowIdHeader(
+absl::optional<QuicDatagramStreamId> SpdyUtils::ParseDatagramFlowIdHeader(
const spdy::SpdyHeaderBlock& headers) {
auto flow_id_pair = headers.find("datagram-flow-id");
if (flow_id_pair == headers.end()) {
@@ -163,7 +162,7 @@ absl::optional<QuicDatagramFlowId> SpdyUtils::ParseDatagramFlowIdHeader(
}
std::vector<absl::string_view> flow_id_strings =
absl::StrSplit(flow_id_pair->second, ',');
- absl::optional<QuicDatagramFlowId> first_named_flow_id;
+ absl::optional<QuicDatagramStreamId> first_named_flow_id;
for (absl::string_view flow_id_string : flow_id_strings) {
std::vector<absl::string_view> flow_id_components =
absl::StrSplit(flow_id_string, ';');
@@ -173,7 +172,7 @@ absl::optional<QuicDatagramFlowId> SpdyUtils::ParseDatagramFlowIdHeader(
absl::string_view flow_id_value_string = flow_id_components[0];
quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(
&flow_id_value_string);
- QuicDatagramFlowId flow_id;
+ QuicDatagramStreamId flow_id;
if (!absl::SimpleAtoi(flow_id_value_string, &flow_id)) {
continue;
}
@@ -191,7 +190,7 @@ absl::optional<QuicDatagramFlowId> SpdyUtils::ParseDatagramFlowIdHeader(
// static
void SpdyUtils::AddDatagramFlowIdHeader(spdy::SpdyHeaderBlock* headers,
- QuicDatagramFlowId flow_id) {
+ QuicDatagramStreamId flow_id) {
(*headers)["datagram-flow-id"] = absl::StrCat(flow_id);
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h
index e3a8a97de48..01fae4f17a7 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h
+++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h
@@ -55,12 +55,12 @@ class QUIC_EXPORT_PRIVATE SpdyUtils {
// Parses the "datagram-flow-id" header, returns the flow ID on success, or
// returns absl::nullopt if the header was not present or failed to parse.
- static absl::optional<QuicDatagramFlowId> ParseDatagramFlowIdHeader(
+ static absl::optional<QuicDatagramStreamId> ParseDatagramFlowIdHeader(
const spdy::SpdyHeaderBlock& headers);
// Adds the "datagram-flow-id" header.
static void AddDatagramFlowIdHeader(spdy::SpdyHeaderBlock* headers,
- QuicDatagramFlowId flow_id);
+ QuicDatagramStreamId flow_id);
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc
index 7307665da3d..918739d6f90 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc
@@ -34,7 +34,7 @@ static std::unique_ptr<QuicHeaderList> FromList(
static void ValidateDatagramFlowId(
const std::string& header_value,
- absl::optional<QuicDatagramFlowId> expected_flow_id) {
+ absl::optional<QuicDatagramStreamId> expected_flow_id) {
SpdyHeaderBlock headers;
headers["datagram-flow-id"] = header_value;
ASSERT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), expected_flow_id);
@@ -391,7 +391,7 @@ TEST_F(DatagramFlowIdTest, DatagramFlowId) {
SpdyHeaderBlock headers;
EXPECT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), absl::nullopt);
// Add header and verify it parses.
- QuicDatagramFlowId flow_id = 123;
+ QuicDatagramStreamId flow_id = 123;
SpdyUtils::AddDatagramFlowIdHeader(&headers, flow_id);
EXPECT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), flow_id);
// Test empty header.
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.cc b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.cc
index 2a105c2e5bf..820950b31bd 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.cc
@@ -36,17 +36,19 @@ class QUIC_NO_EXPORT NoopWebTransportVisitor : public WebTransportVisitor {
WebTransportHttp3::WebTransportHttp3(QuicSpdySession* session,
QuicSpdyStream* connect_stream,
- WebTransportSessionId id,
- QuicDatagramFlowId flow_id)
+ WebTransportSessionId id)
: session_(session),
connect_stream_(connect_stream),
id_(id),
- flow_id_(flow_id),
visitor_(std::make_unique<NoopWebTransportVisitor>()) {
QUICHE_DCHECK(session_->SupportsWebTransport());
QUICHE_DCHECK(IsValidWebTransportSessionId(id, session_->version()));
QUICHE_DCHECK_EQ(connect_stream_->id(), id);
- session_->RegisterHttp3FlowId(flow_id, this);
+ connect_stream_->RegisterHttp3DatagramRegistrationVisitor(this);
+ if (session_->perspective() == Perspective::IS_CLIENT) {
+ context_is_known_ = true;
+ context_currently_registered_ = true;
+ }
}
void WebTransportHttp3::AssociateStream(QuicStreamId stream_id) {
@@ -74,7 +76,11 @@ void WebTransportHttp3::CloseAllAssociatedStreams() {
for (QuicStreamId id : streams) {
session_->ResetStream(id, QUIC_STREAM_WEBTRANSPORT_SESSION_GONE);
}
- session_->UnregisterHttp3FlowId(flow_id_);
+ if (context_currently_registered_) {
+ context_currently_registered_ = false;
+ connect_stream_->UnregisterHttp3DatagramContextId(context_id_);
+ }
+ connect_stream_->UnregisterHttp3DatagramRegistrationVisitor();
}
void WebTransportHttp3::HeadersReceived(const spdy::SpdyHeaderBlock& headers) {
@@ -153,21 +159,81 @@ WebTransportStream* WebTransportHttp3::OpenOutgoingUnidirectionalStream() {
}
MessageStatus WebTransportHttp3::SendOrQueueDatagram(QuicMemSlice datagram) {
- return session_->SendHttp3Datagram(
- flow_id_, absl::string_view(datagram.data(), datagram.length()));
+ return connect_stream_->SendHttp3Datagram(
+ context_id_, absl::string_view(datagram.data(), datagram.length()));
}
void WebTransportHttp3::SetDatagramMaxTimeInQueue(
QuicTime::Delta max_time_in_queue) {
- session_->SetMaxTimeInQueueForFlowId(flow_id_, max_time_in_queue);
+ connect_stream_->SetMaxDatagramTimeInQueue(max_time_in_queue);
}
-void WebTransportHttp3::OnHttp3Datagram(QuicDatagramFlowId flow_id,
- absl::string_view payload) {
- QUICHE_DCHECK_EQ(flow_id, flow_id_);
+void WebTransportHttp3::OnHttp3Datagram(
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
+ absl::string_view payload) {
+ QUICHE_DCHECK_EQ(stream_id, connect_stream_->id());
+ QUICHE_DCHECK(context_id == context_id_);
visitor_->OnDatagramReceived(payload);
}
+void WebTransportHttp3::OnContextReceived(
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& /*extensions*/) {
+ if (stream_id != connect_stream_->id()) {
+ QUIC_BUG(WT3 bad datagram context registration)
+ << ENDPOINT << "Registered stream ID " << stream_id << ", expected "
+ << connect_stream_->id();
+ return;
+ }
+ if (!context_is_known_) {
+ context_is_known_ = true;
+ context_id_ = context_id;
+ }
+ if (context_id != context_id_) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Ignoring unexpected context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " instead of "
+ << (context_id_.has_value() ? context_id_.value() : 0)
+ << " on stream ID " << connect_stream_->id();
+ return;
+ }
+ if (session_->perspective() == Perspective::IS_SERVER) {
+ if (context_currently_registered_) {
+ QUIC_DLOG(ERROR) << ENDPOINT << "Received duplicate context ID "
+ << (context_id_.has_value() ? context_id_.value() : 0)
+ << " on stream ID " << connect_stream_->id();
+ session_->ResetStream(connect_stream_->id(), QUIC_STREAM_CANCELLED);
+ return;
+ }
+ context_currently_registered_ = true;
+ Http3DatagramContextExtensions reply_extensions;
+ connect_stream_->RegisterHttp3DatagramContextId(context_id_,
+ reply_extensions, this);
+ }
+}
+
+void WebTransportHttp3::OnContextClosed(
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& /*extensions*/) {
+ if (stream_id != connect_stream_->id()) {
+ QUIC_BUG(WT3 bad datagram context registration)
+ << ENDPOINT << "Closed context on stream ID " << stream_id
+ << ", expected " << connect_stream_->id();
+ return;
+ }
+ if (context_id != context_id_) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Ignoring unexpected close of context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " instead of "
+ << (context_id_.has_value() ? context_id_.value() : 0)
+ << " on stream ID " << connect_stream_->id();
+ return;
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "Received datagram context close on stream ID "
+ << connect_stream_->id() << ", resetting stream";
+ session_->ResetStream(connect_stream_->id(), QUIC_STREAM_CANCELLED);
+}
+
WebTransportHttp3UnidirectionalStream::WebTransportHttp3UnidirectionalStream(
PendingStream* pending,
QuicSpdySession* session)
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h
index 667256ea515..df5c4f61bf7 100644
--- a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h
+++ b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h
@@ -28,12 +28,11 @@ class QuicSpdyStream;
// <https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http3>
class QUIC_EXPORT_PRIVATE WebTransportHttp3
: public WebTransportSession,
- public QuicSpdySession::Http3DatagramVisitor {
+ public QuicSpdyStream::Http3DatagramRegistrationVisitor,
+ public QuicSpdyStream::Http3DatagramVisitor {
public:
- WebTransportHttp3(QuicSpdySession* session,
- QuicSpdyStream* connect_stream,
- WebTransportSessionId id,
- QuicDatagramFlowId flow_id);
+ WebTransportHttp3(QuicSpdySession* session, QuicSpdyStream* connect_stream,
+ WebTransportSessionId id);
void HeadersReceived(const spdy::SpdyHeaderBlock& headers);
void SetVisitor(std::unique_ptr<WebTransportVisitor> visitor) {
@@ -42,6 +41,9 @@ class QUIC_EXPORT_PRIVATE WebTransportHttp3
WebTransportSessionId id() { return id_; }
bool ready() { return ready_; }
+ absl::optional<QuicDatagramContextId> context_id() const {
+ return context_id_;
+ }
void AssociateStream(QuicStreamId stream_id);
void OnStreamClosed(QuicStreamId stream_id) { streams_.erase(stream_id); }
@@ -63,16 +65,32 @@ class QUIC_EXPORT_PRIVATE WebTransportHttp3
MessageStatus SendOrQueueDatagram(QuicMemSlice datagram) override;
void SetDatagramMaxTimeInQueue(QuicTime::Delta max_time_in_queue) override;
- void OnHttp3Datagram(QuicDatagramFlowId flow_id,
+ // From QuicSpdyStream::Http3DatagramVisitor.
+ void OnHttp3Datagram(QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
absl::string_view payload) override;
+ // From QuicSpdyStream::Http3DatagramRegistrationVisitor.
+ void OnContextReceived(
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions) override;
+ void OnContextClosed(
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions) override;
+
private:
QuicSpdySession* const session_; // Unowned.
QuicSpdyStream* const connect_stream_; // Unowned.
const WebTransportSessionId id_;
- const QuicDatagramFlowId flow_id_;
+ absl::optional<QuicDatagramContextId> context_id_;
// |ready_| is set to true when the peer has seen both sets of headers.
bool ready_ = false;
+ // Whether we know which |context_id_| to use. On the client this is always
+ // true, and on the server it becomes true when we receive a context
+ // registeration capsule.
+ bool context_is_known_ = false;
+ // Whether |context_id_| is currently registered with |connect_stream_|.
+ bool context_currently_registered_ = false;
std::unique_ptr<WebTransportVisitor> visitor_;
absl::flat_hash_set<QuicStreamId> streams_;
quiche::QuicheCircularDeque<QuicStreamId> incoming_bidirectional_streams_;
diff --git a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc
index 2ddf4b2498a..19128abf2ca 100644
--- a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc
@@ -7,7 +7,6 @@
#include "quic/core/quic_types.h"
#include "quic/core/quic_utils.h"
#include "quic/core/quic_versions.h"
-#include "quic/platform/api/quic_map_util.h"
namespace quic {
@@ -125,7 +124,7 @@ bool LegacyQuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
return largest_peer_created_stream_id_ ==
QuicUtils::GetInvalidStreamId(transport_version_) ||
id > largest_peer_created_stream_id_ ||
- QuicContainsKey(available_streams_, id);
+ available_streams_.contains(id);
}
bool LegacyQuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc
index 495a58ffdfd..acd52c2ce33 100644
--- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc
@@ -86,8 +86,6 @@ bool QpackInstructionDecoder::Decode(absl::string_view data) {
return true;
}
}
-
- return true;
}
bool QpackInstructionDecoder::AtInstructionBoundary() const {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc b/chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc
index bfef316ff62..81640a4c68e 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc
@@ -4,30 +4,74 @@
#include "quic/core/quic_alarm.h"
+#include "quic/platform/api/quic_bug_tracker.h"
+#include "quic/platform/api/quic_flag_utils.h"
+#include "quic/platform/api/quic_flags.h"
+#include "quic/platform/api/quic_stack_trace.h"
+
namespace quic {
QuicAlarm::QuicAlarm(QuicArenaScopedPtr<Delegate> delegate)
: delegate_(std::move(delegate)), deadline_(QuicTime::Zero()) {}
-QuicAlarm::~QuicAlarm() {}
+QuicAlarm::~QuicAlarm() {
+ if (GetQuicRestartFlag(quic_alarm_add_permanent_cancel) && IsSet()) {
+ QUIC_CODE_COUNT(quic_alarm_not_cancelled_in_dtor);
+ static uint64_t hit_count = 0;
+ ++hit_count;
+ if ((hit_count & (hit_count - 1)) == 0) {
+ QUIC_LOG(ERROR) << "QuicAlarm not cancelled at destruction. "
+ << QuicStackTrace();
+ }
+ }
+}
void QuicAlarm::Set(QuicTime new_deadline) {
QUICHE_DCHECK(!IsSet());
QUICHE_DCHECK(new_deadline.IsInitialized());
+
+ if (IsPermanentlyCancelled()) {
+ QUIC_BUG(quic_alarm_illegal_set)
+ << "Set called after alarm is permanently cancelled. new_deadline:"
+ << new_deadline;
+ return;
+ }
+
deadline_ = new_deadline;
SetImpl();
}
-void QuicAlarm::Cancel() {
- if (!IsSet()) {
- // Don't try to cancel an alarm that hasn't been set.
+void QuicAlarm::CancelInternal(bool permanent) {
+ if (!GetQuicRestartFlag(quic_alarm_add_permanent_cancel)) {
+ if (!IsSet()) {
+ // Don't try to cancel an alarm that hasn't been set.
+ return;
+ }
+ deadline_ = QuicTime::Zero();
+ CancelImpl();
return;
}
- deadline_ = QuicTime::Zero();
- CancelImpl();
+
+ if (IsSet()) {
+ deadline_ = QuicTime::Zero();
+ CancelImpl();
+ }
+
+ if (permanent) {
+ delegate_.reset();
+ }
}
+bool QuicAlarm::IsPermanentlyCancelled() const { return delegate_ == nullptr; }
+
void QuicAlarm::Update(QuicTime new_deadline, QuicTime::Delta granularity) {
+ if (IsPermanentlyCancelled()) {
+ QUIC_BUG(quic_alarm_illegal_update)
+ << "Update called after alarm is permanently cancelled. new_deadline:"
+ << new_deadline << ", granularity:" << granularity;
+ return;
+ }
+
if (!new_deadline.IsInitialized()) {
Cancel();
return;
@@ -55,7 +99,9 @@ void QuicAlarm::Fire() {
}
deadline_ = QuicTime::Zero();
- delegate_->OnAlarm();
+ if (!IsPermanentlyCancelled()) {
+ delegate_->OnAlarm();
+ }
}
void QuicAlarm::UpdateImpl() {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_alarm.h b/chromium/net/third_party/quiche/src/quic/core/quic_alarm.h
index 527d2e3c662..5080fd4b263 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_alarm.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_alarm.h
@@ -36,11 +36,18 @@ class QUIC_EXPORT_PRIVATE QuicAlarm {
// then Set().
void Set(QuicTime new_deadline);
- // Cancels the alarm. May be called repeatedly. Does not
- // guarantee that the underlying scheduling system will remove
- // the alarm's associated task, but guarantees that the
- // delegates OnAlarm method will not be called.
- void Cancel();
+ // Both PermanentCancel() and Cancel() can cancel the alarm. If permanent,
+ // future calls to Set() and Update() will become no-op except emitting an
+ // error log.
+ //
+ // Both may be called repeatedly. Does not guarantee that the underlying
+ // scheduling system will remove the alarm's associated task, but guarantees
+ // that the delegates OnAlarm method will not be called.
+ void PermanentCancel() { CancelInternal(true); }
+ void Cancel() { CancelInternal(false); }
+
+ // Return true if PermanentCancel() has been called.
+ bool IsPermanentlyCancelled() const;
// Cancels and sets the alarm if the |deadline| is farther from the current
// deadline than |granularity|, and otherwise does nothing. If |deadline| is
@@ -77,6 +84,8 @@ class QUIC_EXPORT_PRIVATE QuicAlarm {
void Fire();
private:
+ void CancelInternal(bool permanent);
+
QuicArenaScopedPtr<Delegate> delegate_;
QuicTime deadline_;
};
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc
index 8f7296c8e10..ab070022eec 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc
@@ -4,6 +4,7 @@
#include "quic/core/quic_alarm.h"
+#include "quic/platform/api/quic_expect_bug.h"
#include "quic/platform/api/quic_test.h"
using testing::Invoke;
@@ -111,6 +112,48 @@ TEST_F(QuicAlarmTest, Cancel) {
EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
}
+TEST_F(QuicAlarmTest, PermanentCancel) {
+ QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
+ alarm_.Set(deadline);
+ alarm_.PermanentCancel();
+ EXPECT_FALSE(alarm_.IsSet());
+ EXPECT_FALSE(alarm_.scheduled());
+ EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
+
+ if (!GetQuicRestartFlag(quic_alarm_add_permanent_cancel)) {
+ alarm_.Set(deadline);
+ // When flag is false, PermanentCancel should work like a normal Cancel.
+ EXPECT_TRUE(alarm_.IsSet());
+ EXPECT_TRUE(alarm_.scheduled());
+ EXPECT_EQ(deadline, alarm_.deadline());
+ EXPECT_FALSE(alarm_.IsPermanentlyCancelled());
+ alarm_.PermanentCancel();
+ } else {
+ EXPECT_QUIC_BUG(alarm_.Set(deadline),
+ "Set called after alarm is permanently cancelled");
+ EXPECT_TRUE(alarm_.IsPermanentlyCancelled());
+ }
+ EXPECT_FALSE(alarm_.IsSet());
+ EXPECT_FALSE(alarm_.scheduled());
+ EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
+
+ if (!GetQuicRestartFlag(quic_alarm_add_permanent_cancel)) {
+ alarm_.Update(deadline, QuicTime::Delta::Zero());
+ EXPECT_TRUE(alarm_.IsSet());
+ EXPECT_TRUE(alarm_.scheduled());
+ EXPECT_EQ(deadline, alarm_.deadline());
+ EXPECT_FALSE(alarm_.IsPermanentlyCancelled());
+ alarm_.PermanentCancel();
+ } else {
+ EXPECT_QUIC_BUG(alarm_.Update(deadline, QuicTime::Delta::Zero()),
+ "Update called after alarm is permanently cancelled");
+ EXPECT_TRUE(alarm_.IsPermanentlyCancelled());
+ }
+ EXPECT_FALSE(alarm_.IsSet());
+ EXPECT_FALSE(alarm_.scheduled());
+ EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
+}
+
TEST_F(QuicAlarmTest, Update) {
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm_.Set(deadline);
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.h b/chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.h
index a661a7ee76b..d3905e70064 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.h
@@ -9,6 +9,7 @@
#include <memory>
+#include "absl/strings/string_view.h"
#include "quic/platform/api/quic_export.h"
namespace quic {
@@ -56,6 +57,58 @@ inline QuicUniqueBufferPtr MakeUniqueBuffer(QuicBufferAllocator* allocator,
QuicBufferDeleter(allocator));
}
+// QuicUniqueBufferPtr with a length attached to it. Similar to QuicMemSlice,
+// except unlike QuicMemSlice, QuicBuffer is mutable and is not
+// platform-specific. Also unlike QuicMemSlice, QuicBuffer can be empty.
+class QUIC_EXPORT_PRIVATE QuicBuffer {
+ public:
+ QuicBuffer() : buffer_(nullptr, QuicBufferDeleter(nullptr)), size_(0) {}
+ QuicBuffer(QuicBufferAllocator* allocator, size_t size)
+ : buffer_(MakeUniqueBuffer(allocator, size)), size_(size) {}
+
+ // Make sure the move constructor zeroes out the size field.
+ QuicBuffer(QuicBuffer&& other)
+ : buffer_(std::move(other.buffer_)), size_(other.size_) {
+ other.buffer_ = nullptr;
+ other.size_ = 0;
+ }
+ QuicBuffer& operator=(QuicBuffer&& other) {
+ buffer_ = std::move(other.buffer_);
+ size_ = other.size_;
+
+ other.buffer_ = nullptr;
+ other.size_ = 0;
+ return *this;
+ }
+
+ // Convenience method to initialize a QuicBuffer by copying from an existing
+ // one.
+ static QuicBuffer Copy(QuicBufferAllocator* allocator,
+ absl::string_view data) {
+ QuicBuffer result(allocator, data.size());
+ memcpy(result.data(), data.data(), data.size());
+ return result;
+ }
+
+ const char* data() const { return buffer_.get(); }
+ char* data() { return buffer_.get(); }
+ size_t size() const { return size_; }
+ bool empty() const { return size_ == 0; }
+ absl::string_view AsStringView() const {
+ return absl::string_view(data(), size());
+ }
+
+ // Releases the ownership of the underlying buffer.
+ QuicUniqueBufferPtr Release() {
+ size_ = 0;
+ return std::move(buffer_);
+ }
+
+ private:
+ QuicUniqueBufferPtr buffer_;
+ size_t size_;
+};
+
} // namespace quic
#endif // QUICHE_QUIC_CORE_QUIC_BUFFER_ALLOCATOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc
index e4d4c971338..9312ea5deae 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc
@@ -8,7 +8,6 @@
#include "quic/platform/api/quic_bug_tracker.h"
#include "quic/platform/api/quic_flags.h"
-#include "quic/platform/api/quic_map_util.h"
namespace quic {
@@ -77,7 +76,12 @@ QuicBufferedPacketStore::QuicBufferedPacketStore(
expiration_alarm_(
alarm_factory->CreateAlarm(new ConnectionExpireAlarm(this))) {}
-QuicBufferedPacketStore::~QuicBufferedPacketStore() {}
+QuicBufferedPacketStore::~QuicBufferedPacketStore() {
+ if (GetQuicRestartFlag(quic_alarm_add_permanent_cancel) &&
+ expiration_alarm_ != nullptr) {
+ expiration_alarm_->PermanentCancel();
+ }
+}
EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket(
QuicConnectionId connection_id,
@@ -92,15 +96,14 @@ EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket(
QUIC_BUG_IF(quic_bug_12410_1, !GetQuicFlag(FLAGS_quic_allow_chlo_buffering))
<< "Shouldn't buffer packets if disabled via flag.";
QUIC_BUG_IF(quic_bug_12410_2,
- is_chlo && QuicContainsKey(connections_with_chlo_, connection_id))
+ is_chlo && connections_with_chlo_.contains(connection_id))
<< "Shouldn't buffer duplicated CHLO on connection " << connection_id;
QUIC_BUG_IF(quic_bug_12410_3, !is_chlo && !alpns.empty())
<< "Shouldn't have an ALPN defined for a non-CHLO packet.";
QUIC_BUG_IF(quic_bug_12410_4, is_chlo && !version.IsKnown())
<< "Should have version for CHLO packet.";
- const bool is_first_packet =
- !QuicContainsKey(undecryptable_packets_, connection_id);
+ const bool is_first_packet = !undecryptable_packets_.contains(connection_id);
if (is_first_packet) {
if (ShouldNotBufferPacket(is_chlo)) {
// Drop the packet if the upper limit of undecryptable packets has been
@@ -112,17 +115,16 @@ EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket(
undecryptable_packets_.back().second.ietf_quic = ietf_quic;
undecryptable_packets_.back().second.version = version;
}
- QUICHE_CHECK(QuicContainsKey(undecryptable_packets_, connection_id));
+ QUICHE_CHECK(undecryptable_packets_.contains(connection_id));
BufferedPacketList& queue =
undecryptable_packets_.find(connection_id)->second;
if (!is_chlo) {
// If current packet is not CHLO, it might not be buffered because store
// only buffers certain number of undecryptable packets per connection.
- size_t num_non_chlo_packets =
- QuicContainsKey(connections_with_chlo_, connection_id)
- ? (queue.buffered_packets.size() - 1)
- : queue.buffered_packets.size();
+ size_t num_non_chlo_packets = connections_with_chlo_.contains(connection_id)
+ ? (queue.buffered_packets.size() - 1)
+ : queue.buffered_packets.size();
if (num_non_chlo_packets >= kDefaultMaxUndecryptablePackets) {
// If there are kMaxBufferedPacketsPerConnection packets buffered up for
// this connection, drop the current packet.
@@ -169,7 +171,7 @@ EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket(
bool QuicBufferedPacketStore::HasBufferedPackets(
QuicConnectionId connection_id) const {
- return QuicContainsKey(undecryptable_packets_, connection_id);
+ return undecryptable_packets_.contains(connection_id);
}
bool QuicBufferedPacketStore::HasChlosBuffered() const {
@@ -254,7 +256,7 @@ BufferedPacketList QuicBufferedPacketStore::DeliverPacketsForNextConnection(
bool QuicBufferedPacketStore::HasChloForConnection(
QuicConnectionId connection_id) {
- return QuicContainsKey(connections_with_chlo_, connection_id);
+ return connections_with_chlo_.contains(connection_id);
}
bool QuicBufferedPacketStore::IngestPacketForTlsChloExtraction(
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h
index 29be2c86a42..5ba0a25ed0b 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h
@@ -14,9 +14,9 @@
#include "quic/core/quic_packets.h"
#include "quic/core/quic_time.h"
#include "quic/core/tls_chlo_extractor.h"
-#include "quic/platform/api/quic_containers.h"
#include "quic/platform/api/quic_export.h"
#include "quic/platform/api/quic_socket_address.h"
+#include "common/quiche_linked_hash_map.h"
namespace quic {
@@ -78,9 +78,9 @@ class QUIC_NO_EXPORT QuicBufferedPacketStore {
TlsChloExtractor tls_chlo_extractor;
};
- using BufferedPacketMap = QuicLinkedHashMap<QuicConnectionId,
- BufferedPacketList,
- QuicConnectionIdHash>;
+ using BufferedPacketMap = quiche::QuicheLinkedHashMap<QuicConnectionId,
+ BufferedPacketList,
+ QuicConnectionIdHash>;
class QUIC_NO_EXPORT VisitorInterface {
public:
@@ -186,7 +186,7 @@ class QUIC_NO_EXPORT QuicBufferedPacketStore {
// Keeps track of connection with CHLO buffered up already and the order they
// arrive.
- QuicLinkedHashMap<QuicConnectionId, bool, QuicConnectionIdHash>
+ quiche::QuicheLinkedHashMap<QuicConnectionId, bool, QuicConnectionIdHash>
connections_with_chlo_;
};
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc
index 09806f5a8a8..12bf186d318 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc
@@ -45,7 +45,6 @@
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_hostname_utils.h"
#include "quic/platform/api/quic_logging.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/platform/api/quic_server_stats.h"
#include "quic/platform/api/quic_socket_address.h"
#include "common/quiche_text_utils.h"
@@ -63,13 +62,23 @@ const QuicPacketCount kMaxConsecutiveNonRetransmittablePackets = 19;
// The minimum release time into future in ms.
const int kMinReleaseTimeIntoFutureMs = 1;
-// An alarm that is scheduled to send an ack if a timeout occurs.
-class AckAlarmDelegate : public QuicAlarm::Delegate {
+// Base class of all alarms owned by a QuicConnection.
+class QuicConnectionAlarmDelegate : public QuicAlarm::Delegate {
public:
- explicit AckAlarmDelegate(QuicConnection* connection)
+ explicit QuicConnectionAlarmDelegate(QuicConnection* connection)
: connection_(connection) {}
- AckAlarmDelegate(const AckAlarmDelegate&) = delete;
- AckAlarmDelegate& operator=(const AckAlarmDelegate&) = delete;
+ QuicConnectionAlarmDelegate(const QuicConnectionAlarmDelegate&) = delete;
+ QuicConnectionAlarmDelegate& operator=(const QuicConnectionAlarmDelegate&) =
+ delete;
+
+ protected:
+ QuicConnection* connection_;
+};
+
+// An alarm that is scheduled to send an ack if a timeout occurs.
+class AckAlarmDelegate : public QuicConnectionAlarmDelegate {
+ public:
+ using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->ack_frame_updated());
@@ -81,136 +90,86 @@ class AckAlarmDelegate : public QuicAlarm::Delegate {
connection_->SendAck();
}
}
-
- private:
- QuicConnection* connection_;
};
// This alarm will be scheduled any time a data-bearing packet is sent out.
// When the alarm goes off, the connection checks to see if the oldest packets
// have been acked, and retransmit them if they have not.
-class RetransmissionAlarmDelegate : public QuicAlarm::Delegate {
+class RetransmissionAlarmDelegate : public QuicConnectionAlarmDelegate {
public:
- explicit RetransmissionAlarmDelegate(QuicConnection* connection)
- : connection_(connection) {}
- RetransmissionAlarmDelegate(const RetransmissionAlarmDelegate&) = delete;
- RetransmissionAlarmDelegate& operator=(const RetransmissionAlarmDelegate&) =
- delete;
+ using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
connection_->OnRetransmissionTimeout();
}
-
- private:
- QuicConnection* connection_;
};
// An alarm that is scheduled when the SentPacketManager requires a delay
// before sending packets and fires when the packet may be sent.
-class SendAlarmDelegate : public QuicAlarm::Delegate {
+class SendAlarmDelegate : public QuicConnectionAlarmDelegate {
public:
- explicit SendAlarmDelegate(QuicConnection* connection)
- : connection_(connection) {}
- SendAlarmDelegate(const SendAlarmDelegate&) = delete;
- SendAlarmDelegate& operator=(const SendAlarmDelegate&) = delete;
+ using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
connection_->WriteIfNotBlocked();
}
-
- private:
- QuicConnection* connection_;
};
-class PingAlarmDelegate : public QuicAlarm::Delegate {
+class PingAlarmDelegate : public QuicConnectionAlarmDelegate {
public:
- explicit PingAlarmDelegate(QuicConnection* connection)
- : connection_(connection) {}
- PingAlarmDelegate(const PingAlarmDelegate&) = delete;
- PingAlarmDelegate& operator=(const PingAlarmDelegate&) = delete;
+ using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
connection_->OnPingTimeout();
}
-
- private:
- QuicConnection* connection_;
};
-class MtuDiscoveryAlarmDelegate : public QuicAlarm::Delegate {
+class MtuDiscoveryAlarmDelegate : public QuicConnectionAlarmDelegate {
public:
- explicit MtuDiscoveryAlarmDelegate(QuicConnection* connection)
- : connection_(connection) {}
- MtuDiscoveryAlarmDelegate(const MtuDiscoveryAlarmDelegate&) = delete;
- MtuDiscoveryAlarmDelegate& operator=(const MtuDiscoveryAlarmDelegate&) =
- delete;
+ using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
connection_->DiscoverMtu();
}
-
- private:
- QuicConnection* connection_;
};
-class ProcessUndecryptablePacketsAlarmDelegate : public QuicAlarm::Delegate {
+class ProcessUndecryptablePacketsAlarmDelegate
+ : public QuicConnectionAlarmDelegate {
public:
- explicit ProcessUndecryptablePacketsAlarmDelegate(QuicConnection* connection)
- : connection_(connection) {}
- ProcessUndecryptablePacketsAlarmDelegate(
- const ProcessUndecryptablePacketsAlarmDelegate&) = delete;
- ProcessUndecryptablePacketsAlarmDelegate& operator=(
- const ProcessUndecryptablePacketsAlarmDelegate&) = delete;
+ using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
QuicConnection::ScopedPacketFlusher flusher(connection_);
connection_->MaybeProcessUndecryptablePackets();
}
-
- private:
- QuicConnection* connection_;
};
-class DiscardPreviousOneRttKeysAlarmDelegate : public QuicAlarm::Delegate {
+class DiscardPreviousOneRttKeysAlarmDelegate
+ : public QuicConnectionAlarmDelegate {
public:
- explicit DiscardPreviousOneRttKeysAlarmDelegate(QuicConnection* connection)
- : connection_(connection) {}
- DiscardPreviousOneRttKeysAlarmDelegate(
- const DiscardPreviousOneRttKeysAlarmDelegate&) = delete;
- DiscardPreviousOneRttKeysAlarmDelegate& operator=(
- const DiscardPreviousOneRttKeysAlarmDelegate&) = delete;
+ using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
connection_->DiscardPreviousOneRttKeys();
}
-
- private:
- QuicConnection* connection_;
};
-class DiscardZeroRttDecryptionKeysAlarmDelegate : public QuicAlarm::Delegate {
+class DiscardZeroRttDecryptionKeysAlarmDelegate
+ : public QuicConnectionAlarmDelegate {
public:
- explicit DiscardZeroRttDecryptionKeysAlarmDelegate(QuicConnection* connection)
- : connection_(connection) {}
- DiscardZeroRttDecryptionKeysAlarmDelegate(
- const DiscardZeroRttDecryptionKeysAlarmDelegate&) = delete;
- DiscardZeroRttDecryptionKeysAlarmDelegate& operator=(
- const DiscardZeroRttDecryptionKeysAlarmDelegate&) = delete;
+ using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
QUIC_DLOG(INFO) << "0-RTT discard alarm fired";
connection_->RemoveDecrypter(ENCRYPTION_ZERO_RTT);
}
-
- private:
- QuicConnection* connection_;
};
// When the clearer goes out of scope, the coalesced packet gets cleared.
@@ -278,8 +237,6 @@ QuicConnection::QuicConnection(
encryption_level_(ENCRYPTION_INITIAL),
clock_(helper->GetClock()),
random_generator_(helper->GetRandomGenerator()),
- server_connection_id_(server_connection_id),
- client_connection_id_(EmptyQuicConnectionId()),
client_connection_id_is_set_(false),
direct_peer_address_(initial_peer_address),
default_path_(initial_self_address,
@@ -335,7 +292,7 @@ QuicConnection::QuicConnection(
visitor_(nullptr),
debug_visitor_(nullptr),
packet_creator_(server_connection_id, &framer_, random_generator_, this),
- time_of_last_received_packet_(clock_->ApproximateNow()),
+ last_received_packet_info_(clock_->ApproximateNow()),
sent_packet_manager_(perspective,
clock_,
random_generator_,
@@ -357,8 +314,6 @@ QuicConnection::QuicConnection(
bundle_retransmittable_with_pto_ack_(false),
fill_up_link_during_probing_(false),
probing_retransmission_pending_(false),
- stateless_reset_token_received_(false),
- received_stateless_reset_token_({}),
last_control_frame_id_(kInvalidControlFrameId),
is_path_degrading_(false),
processing_ack_frame_(false),
@@ -369,18 +324,13 @@ QuicConnection::QuicConnection(
clock_->ApproximateNow(),
&arena_,
alarm_factory_),
- encrypted_control_frames_(
- GetQuicReloadableFlag(quic_encrypted_control_frames)),
- use_encryption_level_context_(
- encrypted_control_frames_ &&
- GetQuicReloadableFlag(quic_use_encryption_level_context)),
path_validator_(alarm_factory_, &arena_, this, random_generator_),
most_recent_frame_type_(NUM_FRAME_TYPES) {
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT ||
default_path_.self_address.IsInitialized());
- if (use_encryption_level_context_) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_use_encryption_level_context);
+ if (add_missing_update_ack_timeout_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_add_missing_update_ack_timeout);
}
support_multiple_connection_ids_ =
@@ -417,7 +367,7 @@ QuicConnection::QuicConnection(
MaybeEnableMultiplePacketNumberSpacesSupport();
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT ||
supported_versions.size() == 1);
- InstallInitialCrypters(ServerConnectionId());
+ InstallInitialCrypters(default_path_.server_connection_id);
// On the server side, version negotiation has been done by the dispatcher,
// and the server connection is created with the right version.
@@ -482,9 +432,9 @@ bool QuicConnection::ValidateConfigConnectionIds(const QuicConfig& config) {
// Validate initial_source_connection_id.
QuicConnectionId expected_initial_source_connection_id;
if (perspective_ == Perspective::IS_CLIENT) {
- expected_initial_source_connection_id = ServerConnectionId();
+ expected_initial_source_connection_id = default_path_.server_connection_id;
} else {
- expected_initial_source_connection_id = ClientConnectionId();
+ expected_initial_source_connection_id = default_path_.client_connection_id;
}
if (!config.HasReceivedInitialSourceConnectionId() ||
config.ReceivedInitialSourceConnectionId() !=
@@ -682,14 +632,8 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) {
no_stop_waiting_frames_ = true;
}
if (config.HasReceivedStatelessResetToken()) {
- if (use_connection_id_on_default_path_) {
- default_path_.stateless_reset_token_received = true;
- default_path_.stateless_reset_token =
- config.ReceivedStatelessResetToken();
- } else {
- stateless_reset_token_received_ = true;
- received_stateless_reset_token_ = config.ReceivedStatelessResetToken();
- }
+ default_path_.stateless_reset_token_received = true;
+ default_path_.stateless_reset_token = config.ReceivedStatelessResetToken();
}
if (config.HasReceivedAckDelayExponent()) {
framer_.set_peer_ack_delay_exponent(config.ReceivedAckDelayExponent());
@@ -720,7 +664,6 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) {
// 2) Client side's rollout can be protected by the same connection option.
connection_migration_use_new_cid_ =
support_multiple_connection_ids_ && validate_client_addresses_ &&
- use_connection_id_on_default_path_ &&
group_path_response_and_challenge_sending_closer_ &&
GetQuicReloadableFlag(quic_drop_unsent_path_response) &&
GetQuicReloadableFlag(quic_connection_migration_use_new_cid_v2);
@@ -858,7 +801,8 @@ bool QuicConnection::SelectMutualVersion(
framer_.supported_versions();
for (size_t i = 0; i < supported_versions.size(); ++i) {
const ParsedQuicVersion& version = supported_versions[i];
- if (QuicContainsValue(available_versions, version)) {
+ if (std::find(available_versions.begin(), available_versions.end(),
+ version) != available_versions.end()) {
framer_.set_version(version);
return true;
}
@@ -885,7 +829,7 @@ void QuicConnection::OnPublicResetPacket(const QuicPublicResetPacket& packet) {
// Check that any public reset packet with a different connection ID that was
// routed to this QuicConnection has been redirected before control reaches
// here. (Check for a bug regression.)
- QUICHE_DCHECK_EQ(ServerConnectionId(), packet.connection_id);
+ QUICHE_DCHECK_EQ(default_path_.server_connection_id, packet.connection_id);
QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
QUICHE_DCHECK(!version().HasIetfInvariantHeader());
if (debug_visitor_ != nullptr) {
@@ -923,7 +867,7 @@ void QuicConnection::OnVersionNegotiationPacket(
// Check that any public reset packet with a different connection ID that was
// routed to this QuicConnection has been redirected before control reaches
// here. (Check for a bug regression.)
- QUICHE_DCHECK_EQ(ServerConnectionId(), packet.connection_id);
+ QUICHE_DCHECK_EQ(default_path_.server_connection_id, packet.connection_id);
if (perspective_ == Perspective::IS_SERVER) {
const std::string error_details =
"Server received version negotiation packet.";
@@ -942,7 +886,8 @@ void QuicConnection::OnVersionNegotiationPacket(
return;
}
- if (QuicContainsValue(packet.versions, version())) {
+ if (std::find(packet.versions.begin(), packet.versions.end(), version()) !=
+ packet.versions.end()) {
const std::string error_details = absl::StrCat(
"Server already supports client's version ",
ParsedQuicVersionToString(version()),
@@ -974,17 +919,17 @@ void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id,
absl::string_view retry_without_tag) {
QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
if (version().UsesTls()) {
- if (!CryptoUtils::ValidateRetryIntegrityTag(version(), ServerConnectionId(),
- retry_without_tag,
- retry_integrity_tag)) {
+ if (!CryptoUtils::ValidateRetryIntegrityTag(
+ version(), default_path_.server_connection_id, retry_without_tag,
+ retry_integrity_tag)) {
QUIC_DLOG(ERROR) << "Ignoring RETRY with invalid integrity tag";
return;
}
} else {
- if (original_connection_id != ServerConnectionId()) {
+ if (original_connection_id != default_path_.server_connection_id) {
QUIC_DLOG(ERROR) << "Ignoring RETRY with original connection ID "
<< original_connection_id << " not matching expected "
- << ServerConnectionId() << " token "
+ << default_path_.server_connection_id << " token "
<< absl::BytesToHexString(retry_token);
return;
}
@@ -992,10 +937,11 @@ void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id,
framer_.set_drop_incoming_retry_packets(true);
stats_.retry_packet_processed = true;
QUIC_DLOG(INFO) << "Received RETRY, replacing connection ID "
- << ServerConnectionId() << " with " << new_connection_id
- << ", received token " << absl::BytesToHexString(retry_token);
+ << default_path_.server_connection_id << " with "
+ << new_connection_id << ", received token "
+ << absl::BytesToHexString(retry_token);
if (!original_destination_connection_id_.has_value()) {
- original_destination_connection_id_ = ServerConnectionId();
+ original_destination_connection_id_ = default_path_.server_connection_id;
}
QUICHE_DCHECK(!retry_source_connection_id_.has_value())
<< retry_source_connection_id_.value();
@@ -1004,51 +950,32 @@ void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id,
packet_creator_.SetRetryToken(retry_token);
// Reinstall initial crypters because the connection ID changed.
- InstallInitialCrypters(ServerConnectionId());
+ InstallInitialCrypters(default_path_.server_connection_id);
sent_packet_manager_.MarkInitialPacketsForRetransmission();
}
-bool QuicConnection::HasIncomingConnectionId(
- QuicConnectionId connection_id) const {
- if (quic_deprecate_incoming_connection_ids_) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_deprecate_incoming_connection_ids);
- // TODO(haoyuewang) Inline this after the flag is deprecated.
- return connection_id == original_destination_connection_id_;
- }
- for (QuicConnectionId const& incoming_connection_id :
- incoming_connection_ids_) {
- if (incoming_connection_id == connection_id) {
- return true;
- }
- }
- return false;
-}
-
void QuicConnection::SetOriginalDestinationConnectionId(
const QuicConnectionId& original_destination_connection_id) {
QUIC_DLOG(INFO) << "Setting original_destination_connection_id to "
<< original_destination_connection_id
<< " on connection with server_connection_id "
- << ServerConnectionId();
- QUICHE_DCHECK_NE(original_destination_connection_id, ServerConnectionId());
- if (!quic_deprecate_incoming_connection_ids_) {
- if (!HasIncomingConnectionId(original_destination_connection_id)) {
- incoming_connection_ids_.push_back(original_destination_connection_id);
- }
- }
+ << default_path_.server_connection_id;
+ QUICHE_DCHECK_NE(original_destination_connection_id,
+ default_path_.server_connection_id);
InstallInitialCrypters(original_destination_connection_id);
QUICHE_DCHECK(!original_destination_connection_id_.has_value())
<< original_destination_connection_id_.value();
original_destination_connection_id_ = original_destination_connection_id;
- original_destination_connection_id_replacement_ = ServerConnectionId();
+ original_destination_connection_id_replacement_ =
+ default_path_.server_connection_id;
}
QuicConnectionId QuicConnection::GetOriginalDestinationConnectionId() {
if (original_destination_connection_id_.has_value()) {
return original_destination_connection_id_.value();
}
- return ServerConnectionId();
+ return default_path_.server_connection_id;
}
bool QuicConnection::ValidateServerConnectionId(
@@ -1061,15 +988,15 @@ bool QuicConnection::ValidateServerConnectionId(
QuicConnectionId server_connection_id =
GetServerConnectionIdAsRecipient(header, perspective_);
- if (server_connection_id == ServerConnectionId() ||
- HasIncomingConnectionId(server_connection_id)) {
+ if (server_connection_id == default_path_.server_connection_id ||
+ server_connection_id == original_destination_connection_id_) {
return true;
}
if (PacketCanReplaceServerConnectionId(header, perspective_)) {
QUIC_DLOG(INFO) << ENDPOINT << "Accepting packet with new connection ID "
<< server_connection_id << " instead of "
- << ServerConnectionId();
+ << default_path_.server_connection_id;
return true;
}
@@ -1089,13 +1016,10 @@ bool QuicConnection::OnUnauthenticatedPublicHeader(
// If last packet destination connection ID is the original server
// connection ID chosen by client, replaces it with the connection ID chosen
// by server.
- if (use_connection_id_on_default_path_ &&
- perspective_ == Perspective::IS_SERVER &&
+ if (perspective_ == Perspective::IS_SERVER &&
original_destination_connection_id_.has_value() &&
last_packet_destination_connection_id_ ==
*original_destination_connection_id_) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_connection_id_on_default_path_v2, 3,
- 3);
last_packet_destination_connection_id_ =
original_destination_connection_id_replacement_;
}
@@ -1112,7 +1036,7 @@ bool QuicConnection::OnUnauthenticatedPublicHeader(
QUIC_DLOG(INFO) << ENDPOINT
<< "Ignoring packet from unexpected server connection ID "
<< server_connection_id << " instead of "
- << ServerConnectionId();
+ << default_path_.server_connection_id;
if (debug_visitor_ != nullptr) {
debug_visitor_->OnIncorrectConnectionId(server_connection_id);
}
@@ -1135,7 +1059,7 @@ bool QuicConnection::OnUnauthenticatedPublicHeader(
QuicConnectionId client_connection_id =
GetClientConnectionIdAsRecipient(header, perspective_);
- if (client_connection_id == ClientConnectionId()) {
+ if (client_connection_id == default_path_.client_connection_id) {
return true;
}
@@ -1158,7 +1082,7 @@ bool QuicConnection::OnUnauthenticatedPublicHeader(
QUIC_DLOG(INFO) << ENDPOINT
<< "Ignoring packet from unexpected client connection ID "
<< client_connection_id << " instead of "
- << ClientConnectionId();
+ << default_path_.client_connection_id;
return false;
}
@@ -1261,7 +1185,8 @@ void QuicConnection::OnDecryptedPacket(size_t /*length*/,
default_path_.validated = true;
stats_.address_validated_via_decrypting_packet = true;
}
- idle_network_detector_.OnPacketReceived(time_of_last_received_packet_);
+ idle_network_detector_.OnPacketReceived(
+ last_received_packet_info_.receipt_time);
visitor_->OnPacketDecrypted(level);
}
@@ -1270,7 +1195,7 @@ QuicSocketAddress QuicConnection::GetEffectivePeerAddressFromCurrentPacket()
const {
// By default, the connection is not proxied, and the effective peer address
// is the packet's source address, i.e. the direct peer address.
- return last_packet_source_address_;
+ return last_received_packet_info_.source_address;
}
bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
@@ -1300,7 +1225,7 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
// for client connections.
// TODO(fayang): only change peer addresses in application data packet
// number space.
- UpdatePeerAddress(last_packet_source_address_);
+ UpdatePeerAddress(last_received_packet_info_.source_address);
default_path_.peer_address = GetEffectivePeerAddressFromCurrentPacket();
}
} else {
@@ -1338,19 +1263,20 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
// last_packet_destination_connection_id_ has the advantage that it is
// still present in the session map since the packet can be routed here
// regardless of packet reordering.
- if (IsDefaultPath(last_packet_destination_address_,
+ if (IsDefaultPath(last_received_packet_info_.destination_address,
effective_peer_address)) {
default_path_.server_connection_id =
last_packet_destination_connection_id_;
- } else if (IsAlternativePath(last_packet_destination_address_,
- effective_peer_address)) {
+ } else if (IsAlternativePath(
+ last_received_packet_info_.destination_address,
+ effective_peer_address)) {
alternative_path_.server_connection_id =
last_packet_destination_connection_id_;
}
}
- if (use_connection_id_on_default_path_ &&
- last_packet_destination_connection_id_ != ServerConnectionId() &&
+ if (last_packet_destination_connection_id_ !=
+ default_path_.server_connection_id &&
(!original_destination_connection_id_.has_value() ||
last_packet_destination_connection_id_ !=
*original_destination_connection_id_)) {
@@ -1374,9 +1300,15 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
// Record packet receipt to populate ack info before processing stream
// frames, since the processing may result in sending a bundled ack.
+ QuicTime receipt_time = idle_network_detector_.time_of_last_received_packet();
+ if (reset_per_packet_state_for_undecryptable_packets_ &&
+ SupportsMultiplePacketNumberSpaces()) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(
+ quic_reset_per_packet_state_for_undecryptable_packets, 2, 2);
+ receipt_time = last_received_packet_info_.receipt_time;
+ }
uber_received_packet_manager_.RecordPacketReceived(
- last_decrypted_packet_level_, last_header_,
- idle_network_detector_.time_of_last_received_packet());
+ last_decrypted_packet_level_, last_header_, receipt_time);
if (EnforceAntiAmplificationLimit() && !IsHandshakeConfirmed() &&
!header.retry_token.empty() &&
visitor_->ValidateToken(header.retry_token)) {
@@ -1422,6 +1354,9 @@ bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) {
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
+ // TODO(fayang): Consider moving UpdatePacketContent and
+ // MaybeUpdateAckTimeout to a stand-alone function instead of calling them for
+ // all frames.
MaybeUpdateAckTimeout();
visitor_->OnStreamFrame(frame);
stats_.stream_bytes_received += frame.data_length;
@@ -1730,7 +1665,9 @@ bool QuicConnection::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
QUIC_DLOG(INFO) << ENDPOINT << "STOP_SENDING frame received for stream: "
<< frame.stream_id
<< " with error: " << frame.ietf_error_code;
-
+ if (add_missing_update_ack_timeout_) {
+ MaybeUpdateAckTimeout();
+ }
visitor_->OnStopSendingFrame(frame);
return connected_;
}
@@ -1777,7 +1714,7 @@ bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) {
group_path_response_and_challenge_sending_closer_
? nullptr
: std::make_unique<QuicPacketCreator::ScopedPeerAddressContext>(
- &packet_creator_, last_packet_source_address_,
+ &packet_creator_, last_received_packet_info_.source_address,
/*update_connection_id=*/false);
if (!OnPathChallengeFrameInternal(frame)) {
return false;
@@ -1802,12 +1739,12 @@ bool QuicConnection::OnPathChallengeFrameInternal(
GetEffectivePeerAddressFromCurrentPacket();
if (group_path_response_and_challenge_sending_closer_) {
QuicConnectionId client_cid, server_cid;
- FindOnPathConnectionIds(last_packet_destination_address_,
+ FindOnPathConnectionIds(last_received_packet_info_.destination_address,
current_effective_peer_address, &client_cid,
&server_cid);
context = std::make_unique<QuicPacketCreator::ScopedPeerAddressContext>(
- &packet_creator_, last_packet_source_address_, client_cid, server_cid,
- connection_migration_use_new_cid_);
+ &packet_creator_, last_received_packet_info_.source_address, client_cid,
+ server_cid, connection_migration_use_new_cid_);
}
if (should_proactively_validate_peer_address_on_path_challenge_) {
QUIC_RELOADABLE_FLAG_COUNT(
@@ -1821,7 +1758,8 @@ bool QuicConnection::OnPathChallengeFrameInternal(
<< current_effective_peer_address;
QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 2, 6);
ValidatePath(std::make_unique<ReversePathValidationContext>(
- default_path_.self_address, last_packet_source_address_,
+ default_path_.self_address,
+ last_received_packet_info_.source_address,
current_effective_peer_address, this),
std::make_unique<ReversePathValidationResultDelegate>(
this, peer_address()));
@@ -1840,9 +1778,9 @@ bool QuicConnection::OnPathChallengeFrameInternal(
// Queue or send PATH_RESPONSE. Send PATH_RESPONSE to the source address of
// the current incoming packet, even if it's not the default path or the
// alternative path.
- const bool success =
- SendPathResponse(frame.data_buffer, last_packet_source_address_,
- current_effective_peer_address);
+ const bool success = SendPathResponse(
+ frame.data_buffer, last_received_packet_info_.source_address,
+ current_effective_peer_address);
if (GetQuicReloadableFlag(quic_drop_unsent_path_response)) {
QUIC_RELOADABLE_FLAG_COUNT(quic_drop_unsent_path_response);
}
@@ -1851,7 +1789,7 @@ bool QuicConnection::OnPathChallengeFrameInternal(
if (!GetQuicReloadableFlag(quic_drop_unsent_path_response)) {
// Queue the payloads to re-try later.
pending_path_challenge_payloads_.push_back(
- {frame.data_buffer, last_packet_source_address_});
+ {frame.data_buffer, last_received_packet_info_.source_address});
}
}
// TODO(b/150095588): change the stats to
@@ -1876,8 +1814,8 @@ bool QuicConnection::OnPathResponseFrame(const QuicPathResponseFrame& frame) {
MaybeUpdateAckTimeout();
if (use_path_validator_) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_pass_path_response_to_validator, 1, 4);
- path_validator_.OnPathResponse(frame.data_buffer,
- last_packet_destination_address_);
+ path_validator_.OnPathResponse(
+ frame.data_buffer, last_received_packet_info_.destination_address);
} else {
if (!transmitted_connectivity_probe_payload_ ||
*transmitted_connectivity_probe_payload_ != frame.data_buffer) {
@@ -1953,6 +1891,9 @@ bool QuicConnection::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnMaxStreamsFrame(frame);
}
+ if (add_missing_update_ack_timeout_) {
+ MaybeUpdateAckTimeout();
+ }
return visitor_->OnMaxStreamsFrame(frame) && connected_;
}
@@ -1969,6 +1910,9 @@ bool QuicConnection::OnStreamsBlockedFrame(
if (debug_visitor_ != nullptr) {
debug_visitor_->OnStreamsBlockedFrame(frame);
}
+ if (add_missing_update_ack_timeout_) {
+ MaybeUpdateAckTimeout();
+ }
return visitor_->OnStreamsBlockedFrame(frame) && connected_;
}
@@ -2018,12 +1962,14 @@ bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
}
void QuicConnection::OnClientConnectionIdAvailable() {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 3, 5);
QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
if (!peer_issued_cid_manager_->HasUnusedConnectionId()) {
return;
}
if (default_path_.client_connection_id.IsEmpty()) {
+ // Count client connection ID patched onto the default path.
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 3,
+ 6);
const QuicConnectionIdData* unused_cid_data =
peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
QUIC_DVLOG(1) << ENDPOINT << "Patch connection ID "
@@ -2039,6 +1985,9 @@ void QuicConnection::OnClientConnectionIdAvailable() {
}
if (alternative_path_.peer_address.IsInitialized() &&
alternative_path_.client_connection_id.IsEmpty()) {
+ // Count client connection ID patched onto the alternative path.
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 4,
+ 6);
const QuicConnectionIdData* unused_cid_data =
peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
QUIC_DVLOG(1) << ENDPOINT << "Patch connection ID "
@@ -2050,6 +1999,28 @@ void QuicConnection::OnClientConnectionIdAvailable() {
}
}
+bool QuicConnection::ShouldSetRetransmissionAlarmOnPacketSent(
+ bool in_flight, EncryptionLevel level) const {
+ if (!retransmission_alarm_->IsSet()) {
+ return true;
+ }
+ if (!in_flight) {
+ return false;
+ }
+
+ if (!SupportsMultiplePacketNumberSpaces()) {
+ return true;
+ }
+ // Before handshake gets confirmed, do not re-arm PTO timer on application
+ // data. Think about this scenario: on the client side, the CHLO gets
+ // acknowledged and the SHLO is not received yet. The PTO alarm is set when
+ // the CHLO acknowledge is received (and there is no in flight INITIAL
+ // packet). Re-arming PTO alarm on 0-RTT packet would keep postponing the PTO
+ // alarm.
+ return IsHandshakeConfirmed() || level == ENCRYPTION_INITIAL ||
+ level == ENCRYPTION_HANDSHAKE;
+}
+
bool QuicConnection::OnNewConnectionIdFrameInner(
const QuicNewConnectionIdFrame& frame) {
QUICHE_DCHECK(support_multiple_connection_ids_);
@@ -2068,11 +2039,14 @@ bool QuicConnection::OnNewConnectionIdFrameInner(
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
- if (use_connection_id_on_default_path_ &&
- perspective_ == Perspective::IS_SERVER) {
+ if (perspective_ == Perspective::IS_SERVER) {
OnClientConnectionIdAvailable();
}
QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_support_multiple_cids_v4, 1, 2);
+ if (ack_cid_frames_) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_ack_cid_frames, 1, 2);
+ MaybeUpdateAckTimeout();
+ }
return true;
}
@@ -2108,8 +2082,7 @@ bool QuicConnection::OnRetireConnectionIdFrame(
if (debug_visitor_ != nullptr) {
debug_visitor_->OnRetireConnectionIdFrame(frame);
}
- if (use_connection_id_on_default_path_ &&
- !connection_migration_use_new_cid_) {
+ if (!connection_migration_use_new_cid_) {
// Do not respond to RetireConnectionId frame.
return true;
}
@@ -2132,6 +2105,12 @@ bool QuicConnection::OnRetireConnectionIdFrame(
return false;
}
QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_support_multiple_cids_v4, 2, 2);
+ // Count successfully received RETIRE_CONNECTION_ID frames.
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 5, 6);
+ if (ack_cid_frames_) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_ack_cid_frames, 2, 2);
+ MaybeUpdateAckTimeout();
+ }
return true;
}
@@ -2311,8 +2290,8 @@ void QuicConnection::MaybeRespondToConnectivityProbingOrMigration() {
if (perspective_ == Perspective::IS_CLIENT) {
// This node is a client, notify that a speculative connectivity probing
// packet has been received anyway.
- visitor_->OnPacketReceived(last_packet_destination_address_,
- last_packet_source_address_,
+ visitor_->OnPacketReceived(last_received_packet_info_.destination_address,
+ last_received_packet_info_.source_address,
/*is_connectivity_probe=*/false);
return;
}
@@ -2325,29 +2304,31 @@ void QuicConnection::MaybeRespondToConnectivityProbingOrMigration() {
// If the packet contains PATH CHALLENGE, send appropriate RESPONSE.
// There was at least one PATH CHALLENGE in the received packet,
// Generate the required PATH RESPONSE.
- SendGenericPathProbePacket(nullptr, last_packet_source_address_,
+ SendGenericPathProbePacket(nullptr,
+ last_received_packet_info_.source_address,
/* is_response=*/true);
return;
}
} else {
if (IsCurrentPacketConnectivityProbing()) {
- visitor_->OnPacketReceived(last_packet_destination_address_,
- last_packet_source_address_,
+ visitor_->OnPacketReceived(last_received_packet_info_.destination_address,
+ last_received_packet_info_.source_address,
/*is_connectivity_probe=*/true);
return;
}
if (perspective_ == Perspective::IS_CLIENT) {
// This node is a client, notify that a speculative connectivity probing
// packet has been received anyway.
- QUIC_DVLOG(1) << ENDPOINT
- << "Received a speculative connectivity probing packet for "
- << GetServerConnectionIdAsRecipient(last_header_,
- perspective_)
- << " from ip:port: "
- << last_packet_source_address_.ToString() << " to ip:port: "
- << last_packet_destination_address_.ToString();
- visitor_->OnPacketReceived(last_packet_destination_address_,
- last_packet_source_address_,
+ QUIC_DVLOG(1)
+ << ENDPOINT
+ << "Received a speculative connectivity probing packet for "
+ << GetServerConnectionIdAsRecipient(last_header_, perspective_)
+ << " from ip:port: "
+ << last_received_packet_info_.source_address.ToString()
+ << " to ip:port: "
+ << last_received_packet_info_.destination_address.ToString();
+ visitor_->OnPacketReceived(last_received_packet_info_.destination_address,
+ last_received_packet_info_.source_address,
/*is_connectivity_probe=*/false);
return;
}
@@ -2356,12 +2337,10 @@ void QuicConnection::MaybeRespondToConnectivityProbingOrMigration() {
bool QuicConnection::IsValidStatelessResetToken(
const StatelessResetToken& token) const {
- if (use_connection_id_on_default_path_) {
+ QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
return default_path_.stateless_reset_token_received &&
- token == default_path_.stateless_reset_token;
- }
- return stateless_reset_token_received_ &&
- token == received_stateless_reset_token_;
+ QuicUtils::AreStatelessResetTokensEqual(
+ token, default_path_.stateless_reset_token);
}
void QuicConnection::OnAuthenticatedIetfStatelessResetPacket(
@@ -2373,10 +2352,10 @@ void QuicConnection::OnAuthenticatedIetfStatelessResetPacket(
if (use_path_validator_) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_pass_path_response_to_validator, 4, 4);
- if (!IsDefaultPath(last_packet_destination_address_,
- last_packet_source_address_)) {
+ if (!IsDefaultPath(last_received_packet_info_.destination_address,
+ last_received_packet_info_.source_address)) {
// This packet is received on a probing path. Do not close connection.
- if (IsAlternativePath(last_packet_destination_address_,
+ if (IsAlternativePath(last_received_packet_info_.destination_address,
GetEffectivePeerAddressFromCurrentPacket())) {
QUIC_BUG_IF(quic_bug_12714_18, alternative_path_.validated)
<< "STATELESS_RESET received on alternate path after it's "
@@ -2388,8 +2367,9 @@ void QuicConnection::OnAuthenticatedIetfStatelessResetPacket(
}
return;
}
- } else if (!visitor_->ValidateStatelessReset(last_packet_destination_address_,
- last_packet_source_address_)) {
+ } else if (!visitor_->ValidateStatelessReset(
+ last_received_packet_info_.destination_address,
+ last_received_packet_info_.source_address)) {
// This packet is received on a probing path. Do not close connection.
return;
}
@@ -2570,15 +2550,12 @@ QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id,
}
if (perspective_ == Perspective::IS_SERVER &&
version().CanSendCoalescedPackets() && !IsHandshakeConfirmed()) {
- if (GetQuicReloadableFlag(quic_donot_pto_half_rtt_data)) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_donot_pto_half_rtt_data);
- if (in_on_retransmission_time_out_ &&
- coalesced_packet_.NumberOfPackets() == 0u) {
- // PTO fires while handshake is not confirmed. Do not preempt handshake
- // data with stream data.
- QUIC_CODE_COUNT(quic_try_to_send_half_rtt_data_when_pto_fires);
- return QuicConsumedData(0, false);
- }
+ if (in_on_retransmission_time_out_ &&
+ coalesced_packet_.NumberOfPackets() == 0u) {
+ // PTO fires while handshake is not confirmed. Do not preempt handshake
+ // data with stream data.
+ QUIC_CODE_COUNT(quic_try_to_send_half_rtt_data_when_pto_fires);
+ return QuicConsumedData(0, false);
}
if (coalesced_packet_.ContainsPacketOfEncryptionLevel(ENCRYPTION_INITIAL) &&
coalesced_packet_.NumberOfPackets() == 1u) {
@@ -2739,25 +2716,38 @@ void QuicConnection::OnUndecryptablePacket(const QuicEncryptedPacket& packet,
}
bool QuicConnection::ShouldEnqueueUnDecryptablePacket(
- EncryptionLevel decryption_level,
- bool has_decryption_key) const {
- if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) {
+ EncryptionLevel decryption_level, bool has_decryption_key) const {
+ if (!GetQuicReloadableFlag(quic_queue_until_handshake_complete) &&
+ encryption_level_ == ENCRYPTION_FORWARD_SECURE) {
// We do not expect to install any further keys.
return false;
}
- if (undecryptable_packets_.size() >= max_undecryptable_packets_) {
- // We do not queue more than max_undecryptable_packets_ packets.
- return false;
- }
if (has_decryption_key) {
// We already have the key for this decryption level, therefore no
// future keys will allow it be decrypted.
return false;
}
+ if (GetQuicReloadableFlag(quic_queue_until_handshake_complete) &&
+ IsHandshakeComplete()) {
+ QUICHE_RELOADABLE_FLAG_COUNT(quic_queue_until_handshake_complete);
+ // We do not expect to install any further keys.
+ return false;
+ }
+ if (undecryptable_packets_.size() >= max_undecryptable_packets_) {
+ // We do not queue more than max_undecryptable_packets_ packets.
+ return false;
+ }
if (version().KnowsWhichDecrypterToUse() &&
- decryption_level <= encryption_level_) {
- // On versions that know which decrypter to use, we install keys in order
- // so we will not get newer keys for lower encryption levels.
+ decryption_level == ENCRYPTION_INITIAL) {
+ // When the corresponding decryption key is not available, all
+ // non-Initial packets should be buffered until the handshake is complete.
+ return false;
+ }
+ if (perspective_ == Perspective::IS_CLIENT && version().UsesTls() &&
+ decryption_level == ENCRYPTION_ZERO_RTT) {
+ // Only clients send Zero RTT packets in IETF QUIC.
+ QUIC_PEER_BUG(quic_peer_bug_client_received_zero_rtt)
+ << "Client received a Zero RTT packet, not buffering.";
return false;
}
return true;
@@ -2812,18 +2802,17 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPacketReceived(self_address, peer_address, packet);
}
- current_incoming_packet_received_bytes_counted_ = false;
+ last_received_packet_info_ =
+ ReceivedPacketInfo(self_address, peer_address, packet.receipt_time());
last_size_ = packet.length();
current_packet_data_ = packet.data();
- last_packet_destination_address_ = self_address;
- last_packet_source_address_ = peer_address;
if (!default_path_.self_address.IsInitialized()) {
- default_path_.self_address = last_packet_destination_address_;
+ default_path_.self_address = last_received_packet_info_.destination_address;
}
if (!direct_peer_address_.IsInitialized()) {
- UpdatePeerAddress(last_packet_source_address_);
+ UpdatePeerAddress(last_received_packet_info_.source_address);
}
if (!default_path_.peer_address.IsInitialized()) {
@@ -2844,11 +2833,11 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
if (EnforceAntiAmplificationLimit()) {
default_path_.bytes_received_before_address_validation += last_size_;
}
- } else if (IsDefaultPath(last_packet_destination_address_,
- last_packet_source_address_) &&
+ } else if (IsDefaultPath(last_received_packet_info_.destination_address,
+ last_received_packet_info_.source_address) &&
EnforceAntiAmplificationLimit()) {
QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 1, 5);
- current_incoming_packet_received_bytes_counted_ = true;
+ last_received_packet_info_.received_bytes_counted = true;
default_path_.bytes_received_before_address_validation += last_size_;
}
@@ -2860,10 +2849,9 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
<< " too far from current time:"
<< clock_->ApproximateNow().ToDebuggingValue();
}
- time_of_last_received_packet_ = packet.receipt_time();
QUIC_DVLOG(1) << ENDPOINT << "time of last received packet: "
<< packet.receipt_time().ToDebuggingValue() << " from peer "
- << last_packet_source_address_;
+ << last_received_packet_info_.source_address;
ScopedPacketFlusher flusher(this);
if (!framer_.ProcessPacket(packet)) {
@@ -2897,9 +2885,7 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
}
}
- const bool processed = MaybeProcessCoalescedPackets();
- if (!donot_write_mid_packet_processing_ || !processed) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_donot_write_mid_packet_processing, 3, 3);
+ if (!MaybeProcessCoalescedPackets()) {
MaybeProcessUndecryptablePackets();
MaybeSendInResponseToPacket();
}
@@ -2980,7 +2966,7 @@ void QuicConnection::OnCanWrite() {
}
void QuicConnection::WriteIfNotBlocked() {
- if (donot_write_mid_packet_processing_ && framer().is_processing_packet()) {
+ if (framer().is_processing_packet()) {
QUIC_BUG(connection_write_mid_packet_processing)
<< ENDPOINT << "Tried to write in mid of packet processing";
return;
@@ -2990,14 +2976,14 @@ void QuicConnection::WriteIfNotBlocked() {
}
}
-void QuicConnection::SetServerConnectionId(
- const QuicConnectionId& server_connection_id) {
- if (use_connection_id_on_default_path_) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_connection_id_on_default_path_v2, 2,
- 3);
- default_path_.server_connection_id = server_connection_id;
- } else {
- server_connection_id_ = server_connection_id;
+void QuicConnection::MaybeClearQueuedPacketsOnPathChange() {
+ if (connection_migration_use_new_cid_ &&
+ peer_issued_cid_manager_ != nullptr && HasQueuedPackets()) {
+ // Discard packets serialized with the connection ID on the old code path.
+ // It is possible to clear queued packets only if connection ID changes.
+ // However, the case where connection ID is unchanged and queued packets are
+ // non-empty is quite rare.
+ ClearQueuedPackets();
}
}
@@ -3011,11 +2997,12 @@ void QuicConnection::ReplaceInitialServerConnectionId(
if (peer_issued_cid_manager_ != nullptr) {
QUIC_BUG_IF(quic_bug_12714_22,
!peer_issued_cid_manager_->IsConnectionIdActive(
- ServerConnectionId()))
+ default_path_.server_connection_id))
<< "Connection ID replaced header is no longer active. old id: "
- << ServerConnectionId() << " new_id: " << new_server_connection_id;
- peer_issued_cid_manager_->ReplaceConnectionId(ServerConnectionId(),
- new_server_connection_id);
+ << default_path_.server_connection_id
+ << " new_id: " << new_server_connection_id;
+ peer_issued_cid_manager_->ReplaceConnectionId(
+ default_path_.server_connection_id, new_server_connection_id);
} else {
peer_issued_cid_manager_ =
std::make_unique<QuicPeerIssuedConnectionIdManager>(
@@ -3024,8 +3011,8 @@ void QuicConnection::ReplaceInitialServerConnectionId(
}
}
}
- SetServerConnectionId(new_server_connection_id);
- packet_creator_.SetServerConnectionId(ServerConnectionId());
+ default_path_.server_connection_id = new_server_connection_id;
+ packet_creator_.SetServerConnectionId(default_path_.server_connection_id);
}
void QuicConnection::FindMatchingOrNewClientConnectionIdOrToken(
@@ -3035,9 +3022,6 @@ void QuicConnection::FindMatchingOrNewClientConnectionIdOrToken(
QuicConnectionId* client_connection_id,
bool* stateless_reset_token_received,
StatelessResetToken* stateless_reset_token) {
- if (!use_connection_id_on_default_path_) {
- return;
- }
QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
if (peer_issued_cid_manager_ == nullptr ||
server_connection_id == default_path.server_connection_id) {
@@ -3097,9 +3081,10 @@ void QuicConnection::SetDefaultPathState(PathState new_path_state) {
bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) {
if (perspective_ == Perspective::IS_CLIENT && version().HasIetfQuicFrames() &&
direct_peer_address_.IsInitialized() &&
- last_packet_source_address_.IsInitialized() &&
- direct_peer_address_ != last_packet_source_address_ &&
- !visitor_->IsKnownServerAddress(last_packet_source_address_)) {
+ last_received_packet_info_.source_address.IsInitialized() &&
+ direct_peer_address_ != last_received_packet_info_.source_address &&
+ !visitor_->IsKnownServerAddress(
+ last_received_packet_info_.source_address)) {
// TODO(haoyuewang) Revisit this when preferred_address transport parameter
// is used on the client side.
// Discard packets received from unseen server addresses.
@@ -3108,13 +3093,15 @@ bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) {
if (perspective_ == Perspective::IS_SERVER &&
default_path_.self_address.IsInitialized() &&
- last_packet_destination_address_.IsInitialized() &&
- default_path_.self_address != last_packet_destination_address_) {
+ last_received_packet_info_.destination_address.IsInitialized() &&
+ default_path_.self_address !=
+ last_received_packet_info_.destination_address) {
// Allow change between pure IPv4 and equivalent mapped IPv4 address.
if (default_path_.self_address.port() !=
- last_packet_destination_address_.port() ||
+ last_received_packet_info_.destination_address.port() ||
default_path_.self_address.host().Normalized() !=
- last_packet_destination_address_.host().Normalized()) {
+ last_received_packet_info_.destination_address.host()
+ .Normalized()) {
if (!visitor_->AllowSelfAddressChange()) {
CloseConnection(
QUIC_ERROR_MIGRATING_ADDRESS,
@@ -3123,24 +3110,24 @@ bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) {
return false;
}
}
- default_path_.self_address = last_packet_destination_address_;
+ default_path_.self_address = last_received_packet_info_.destination_address;
}
if (PacketCanReplaceServerConnectionId(header, perspective_) &&
- ServerConnectionId() != header.source_connection_id) {
+ default_path_.server_connection_id != header.source_connection_id) {
QUICHE_DCHECK_EQ(header.long_packet_type, INITIAL);
if (server_connection_id_replaced_by_initial_) {
QUIC_DLOG(ERROR) << ENDPOINT << "Refusing to replace connection ID "
- << ServerConnectionId() << " with "
+ << default_path_.server_connection_id << " with "
<< header.source_connection_id;
return false;
}
server_connection_id_replaced_by_initial_ = true;
QUIC_DLOG(INFO) << ENDPOINT << "Replacing connection ID "
- << ServerConnectionId() << " with "
+ << default_path_.server_connection_id << " with "
<< header.source_connection_id;
if (!original_destination_connection_id_.has_value()) {
- original_destination_connection_id_ = ServerConnectionId();
+ original_destination_connection_id_ = default_path_.server_connection_id;
}
ReplaceInitialServerConnectionId(header.source_connection_id);
}
@@ -3569,7 +3556,7 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) {
legacy_version_encapsulation_sni_,
absl::string_view(packet->encrypted_buffer,
packet->encrypted_length),
- ServerConnectionId(), framer_.creation_time(),
+ default_path_.server_connection_id, framer_.creation_time(),
GetLimitedMaxPacketSize(long_term_mtu_),
const_cast<char*>(packet->encrypted_buffer));
if (encapsulated_length != 0) {
@@ -3759,8 +3746,15 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) {
return true;
}
}
-
- if (in_flight || !retransmission_alarm_->IsSet()) {
+ if (GetQuicReloadableFlag(
+ quic_donot_rearm_pto_on_application_data_during_handshake)) {
+ QUIC_RELOADABLE_FLAG_COUNT(
+ quic_donot_rearm_pto_on_application_data_during_handshake);
+ if (ShouldSetRetransmissionAlarmOnPacketSent(in_flight,
+ packet->encryption_level)) {
+ SetRetransmissionAlarm();
+ }
+ } else if (in_flight || !retransmission_alarm_->IsSet()) {
SetRetransmissionAlarm();
}
SetPingAlarm();
@@ -4078,13 +4072,14 @@ void QuicConnection::OnPathMtuIncreased(QuicPacketLength packet_size) {
std::unique_ptr<QuicSelfIssuedConnectionIdManager>
QuicConnection::MakeSelfIssuedConnectionIdManager() {
QUICHE_DCHECK((perspective_ == Perspective::IS_CLIENT &&
- !ClientConnectionId().IsEmpty()) ||
+ !default_path_.client_connection_id.IsEmpty()) ||
(perspective_ == Perspective::IS_SERVER &&
- !ServerConnectionId().IsEmpty()));
+ !default_path_.server_connection_id.IsEmpty()));
return std::make_unique<QuicSelfIssuedConnectionIdManager>(
kMinNumOfActiveConnectionIds,
- perspective_ == Perspective::IS_CLIENT ? ClientConnectionId()
- : ServerConnectionId(),
+ perspective_ == Perspective::IS_CLIENT
+ ? default_path_.client_connection_id
+ : default_path_.server_connection_id,
clock_, alarm_factory_, this);
}
@@ -4149,9 +4144,7 @@ void QuicConnection::OnPingTimeout() {
!visitor_->ShouldKeepConnectionAlive()) {
return;
}
- SendPingAtLevel(use_encryption_level_context_
- ? framer().GetEncryptionLevelToSendApplicationData()
- : encryption_level_);
+ SendPingAtLevel(framer().GetEncryptionLevelToSendApplicationData());
}
void QuicConnection::SendAck() {
@@ -4218,7 +4211,7 @@ void QuicConnection::OnRetransmissionTimeout() {
blackhole_detector_.IsDetectionInProgress()) {
// Stop detection in quiescence.
QUICHE_DCHECK_EQ(QuicSentPacketManager::LOSS_MODE, retransmission_mode);
- blackhole_detector_.StopDetection();
+ blackhole_detector_.StopDetection(/*permanent=*/false);
}
WriteIfNotBlocked();
@@ -4249,15 +4242,28 @@ void QuicConnection::OnRetransmissionTimeout() {
<< retransmission_mode << ", send PING";
QUICHE_DCHECK_LT(0u,
sent_packet_manager_.pending_timer_transmission_count());
- EncryptionLevel level = encryption_level_;
- PacketNumberSpace packet_number_space = NUM_PACKET_NUMBER_SPACES;
- if (SupportsMultiplePacketNumberSpaces() &&
- sent_packet_manager_
- .GetEarliestPacketSentTimeForPto(&packet_number_space)
- .IsInitialized()) {
- level = QuicUtils::GetEncryptionLevel(packet_number_space);
+ if (SupportsMultiplePacketNumberSpaces()) {
+ // Based on https://datatracker.ietf.org/doc/html/rfc9002#appendix-A.9
+ PacketNumberSpace packet_number_space;
+ if (sent_packet_manager_
+ .GetEarliestPacketSentTimeForPto(&packet_number_space)
+ .IsInitialized()) {
+ SendPingAtLevel(QuicUtils::GetEncryptionLevel(packet_number_space));
+ } else {
+ // The client must PTO when there is nothing in flight if the server
+ // could be blocked from sending by the amplification limit
+ QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
+ if (framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_HANDSHAKE)) {
+ SendPingAtLevel(ENCRYPTION_HANDSHAKE);
+ } else if (framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_INITIAL)) {
+ SendPingAtLevel(ENCRYPTION_INITIAL);
+ } else {
+ QUIC_BUG(quic_bug_no_pto) << "PTO fired but nothing was sent.";
+ }
+ }
+ } else {
+ SendPingAtLevel(encryption_level_);
}
- SendPingAtLevel(level);
}
if (retransmission_mode == QuicSentPacketManager::PTO_MODE) {
sent_packet_manager_.AdjustPendingTimerTransmissions();
@@ -4417,9 +4423,14 @@ void QuicConnection::QueueUndecryptablePacket(
}
}
QUIC_DVLOG(1) << ENDPOINT << "Queueing undecryptable packet.";
- undecryptable_packets_.emplace_back(packet, decryption_level);
+ undecryptable_packets_.emplace_back(packet, decryption_level,
+ last_received_packet_info_);
if (perspective_ == Perspective::IS_CLIENT) {
- SetRetransmissionAlarm();
+ if (!retransmission_alarm_->IsSet() ||
+ GetRetransmissionDeadline() < retransmission_alarm_->deadline()) {
+ // Re-arm PTO only if we can make it sooner to speed up recovery.
+ SetRetransmissionAlarm();
+ }
}
}
@@ -4445,7 +4456,19 @@ void QuicConnection::MaybeProcessUndecryptablePackets() {
debug_visitor_->OnAttemptingToProcessUndecryptablePacket(
undecryptable_packet->encryption_level);
}
- if (framer_.ProcessPacket(*undecryptable_packet->packet)) {
+ bool processed = false;
+ if (reset_per_packet_state_for_undecryptable_packets_) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(
+ quic_reset_per_packet_state_for_undecryptable_packets, 1, 2);
+ last_received_packet_info_ = undecryptable_packet->packet_info;
+ last_size_ = undecryptable_packet->packet->length();
+ current_packet_data_ = undecryptable_packet->packet->data();
+ processed = framer_.ProcessPacket(*undecryptable_packet->packet);
+ current_packet_data_ = nullptr;
+ } else {
+ processed = framer_.ProcessPacket(*undecryptable_packet->packet);
+ }
+ if (processed) {
QUIC_DVLOG(1) << ENDPOINT << "Processed undecryptable packet!";
iter = undecryptable_packets_.erase(iter);
++stats_.packets_processed;
@@ -4479,7 +4502,14 @@ void QuicConnection::MaybeProcessUndecryptablePackets() {
undecryptable_packets_.clear();
}
if (perspective_ == Perspective::IS_CLIENT) {
- SetRetransmissionAlarm();
+ if (!retransmission_alarm_->IsSet() || undecryptable_packets_.empty() ||
+ GetRetransmissionDeadline() < retransmission_alarm_->deadline()) {
+ // 1) If there is still undecryptable packet, only re-arm PTO to make it
+ // sooner to speed up recovery.
+ // 2) If all undecryptable packets get processed, re-arm (which may
+ // postpone) PTO since no immediate recovery is needed.
+ SetRetransmissionAlarm();
+ }
}
}
@@ -4514,11 +4544,7 @@ bool QuicConnection::MaybeProcessCoalescedPackets() {
}
if (processed) {
MaybeProcessUndecryptablePackets();
- if (donot_write_mid_packet_processing_) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_donot_write_mid_packet_processing, 2,
- 3);
- MaybeSendInResponseToPacket();
- }
+ MaybeSendInResponseToPacket();
}
return processed;
}
@@ -4571,12 +4597,8 @@ void QuicConnection::SendConnectionClosePacket(
default_path_.server_connection_id, connection_migration_use_new_cid_);
if (!SupportsMultiplePacketNumberSpaces()) {
QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet.";
- if (!use_encryption_level_context_) {
- SetDefaultEncryptionLevel(GetConnectionCloseEncryptionLevel());
- }
- ScopedEncryptionLevelContext context(
- use_encryption_level_context_ ? this : nullptr,
- GetConnectionCloseEncryptionLevel());
+ ScopedEncryptionLevelContext context(this,
+ GetConnectionCloseEncryptionLevel());
if (version().CanSendCoalescedPackets()) {
coalesced_packet_.Clear();
}
@@ -4607,7 +4629,6 @@ void QuicConnection::SendConnectionClosePacket(
ClearQueuedPackets();
return;
}
- const EncryptionLevel current_encryption_level = encryption_level_;
ScopedPacketFlusher flusher(this);
// Now that the connection is being closed, discard any unsent packets
@@ -4625,11 +4646,7 @@ void QuicConnection::SendConnectionClosePacket(
}
QUIC_DLOG(INFO) << ENDPOINT
<< "Sending connection close packet at level: " << level;
- if (!use_encryption_level_context_) {
- SetDefaultEncryptionLevel(level);
- }
- ScopedEncryptionLevelContext context(
- use_encryption_level_context_ ? this : nullptr, level);
+ ScopedEncryptionLevelContext context(this, level);
// Bundle an ACK of the corresponding packet number space for debugging
// purpose.
bool send_ack = error != QUIC_PACKET_WRITE_ERROR &&
@@ -4662,9 +4679,6 @@ void QuicConnection::SendConnectionClosePacket(
// Since the connection is closing, if the connection close packets were not
// sent, then they should be discarded.
ClearQueuedPackets();
- if (!use_encryption_level_context_) {
- SetDefaultEncryptionLevel(current_encryption_level);
- }
}
void QuicConnection::TearDownLocalConnectionState(
@@ -4712,15 +4726,15 @@ void QuicConnection::TearDownLocalConnectionState(
void QuicConnection::CancelAllAlarms() {
QUIC_DVLOG(1) << "Cancelling all QuicConnection alarms.";
- ack_alarm_->Cancel();
- ping_alarm_->Cancel();
- retransmission_alarm_->Cancel();
- send_alarm_->Cancel();
- mtu_discovery_alarm_->Cancel();
- process_undecryptable_packets_alarm_->Cancel();
- discard_previous_one_rtt_keys_alarm_->Cancel();
- discard_zero_rtt_decryption_keys_alarm_->Cancel();
- blackhole_detector_.StopDetection();
+ ack_alarm_->PermanentCancel();
+ ping_alarm_->PermanentCancel();
+ retransmission_alarm_->PermanentCancel();
+ send_alarm_->PermanentCancel();
+ mtu_discovery_alarm_->PermanentCancel();
+ process_undecryptable_packets_alarm_->PermanentCancel();
+ discard_previous_one_rtt_keys_alarm_->PermanentCancel();
+ discard_zero_rtt_decryption_keys_alarm_->PermanentCancel();
+ blackhole_detector_.StopDetection(/*permanent=*/true);
idle_network_detector_.StopDetection();
}
@@ -4754,6 +4768,9 @@ void QuicConnection::SetNetworkTimeouts(QuicTime::Delta handshake_timeout,
}
void QuicConnection::SetPingAlarm() {
+ if (!connected_) {
+ return;
+ }
if (perspective_ == Perspective::IS_SERVER &&
initial_retransmittable_on_wire_timeout_.IsInfinite()) {
// The PING alarm exists to support two features:
@@ -5097,7 +5114,7 @@ bool QuicConnection::SendGenericPathProbePacket(
QUIC_DLOG(INFO) << ENDPOINT
<< "Sending path probe packet for connection_id = "
- << ServerConnectionId();
+ << default_path_.server_connection_id;
std::unique_ptr<SerializedPacket> probing_packet;
if (!version().HasIetfQuicFrames()) {
@@ -5142,7 +5159,7 @@ bool QuicConnection::WritePacketUsingWriter(
const QuicTime packet_send_time = clock_->Now();
QUIC_DVLOG(2) << ENDPOINT
<< "Sending path probe packet for server connection ID "
- << ServerConnectionId() << std::endl
+ << default_path_.server_connection_id << std::endl
<< quiche::QuicheTextUtils::HexDump(absl::string_view(
packet->encrypted_buffer, packet->encrypted_length));
WriteResult result = writer->WritePacket(
@@ -5277,11 +5294,20 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) {
QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 3, 6);
if (type == NO_CHANGE) {
- UpdatePeerAddress(last_packet_source_address_);
+ UpdatePeerAddress(last_received_packet_info_.source_address);
QUIC_BUG(quic_bug_10511_36)
<< "EffectivePeerMigration started without address change.";
return;
}
+ if (packet_creator_.HasPendingFrames()) {
+ QUIC_BUG(bug_731_2)
+ << "Starts effective peer migration with pending frame types: "
+ << packet_creator_.GetPendingFramesInfo() << ". Address change type is "
+ << AddressChangeTypeToString(type)
+ << ". Current frame type: " << framer_.current_received_frame_type()
+ << ". Previous frame type: "
+ << framer_.previously_received_frame_type();
+ }
// Action items:
// 1. Switch congestion controller;
@@ -5302,6 +5328,7 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) {
const QuicSocketAddress previous_direct_peer_address = direct_peer_address_;
PathState previous_default_path = std::move(default_path_);
active_effective_peer_migration_type_ = type;
+ MaybeClearQueuedPacketsOnPathChange();
OnConnectionMigration();
// Update congestion controller if the address change type is not PORT_CHANGE.
@@ -5338,9 +5365,18 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) {
}
// Update to the new peer address.
- UpdatePeerAddress(last_packet_source_address_);
+ if (packet_creator_.HasPendingFrames()) {
+ QUIC_BUG(bug_731_1)
+ << "Starts effective peer migration with pending frame types: "
+ << packet_creator_.GetPendingFramesInfo() << ". Address change type is "
+ << AddressChangeTypeToString(type)
+ << ". Current frame type: " << framer_.current_received_frame_type()
+ << ". Previous frame type: "
+ << framer_.previously_received_frame_type();
+ }
+ UpdatePeerAddress(last_received_packet_info_.source_address);
// Update the default path.
- if (IsAlternativePath(last_packet_destination_address_,
+ if (IsAlternativePath(last_received_packet_info_.destination_address,
current_effective_peer_address)) {
SetDefaultPathState(std::move(alternative_path_));
} else {
@@ -5351,10 +5387,11 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) {
previous_default_path, alternative_path_,
last_packet_destination_connection_id_, &client_connection_id,
&stateless_reset_token_received, &stateless_reset_token);
- SetDefaultPathState(PathState(
- last_packet_destination_address_, current_effective_peer_address,
- client_connection_id, last_packet_destination_connection_id_,
- stateless_reset_token_received, stateless_reset_token));
+ SetDefaultPathState(
+ PathState(last_received_packet_info_.destination_address,
+ current_effective_peer_address, client_connection_id,
+ last_packet_destination_connection_id_,
+ stateless_reset_token_received, stateless_reset_token));
// The path is considered validated if its peer IP address matches any
// validated path's peer IP address.
default_path_.validated =
@@ -5363,10 +5400,10 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) {
alternative_path_.validated) ||
(previous_default_path.validated && type == PORT_CHANGE);
}
- if (!current_incoming_packet_received_bytes_counted_) {
+ if (!last_received_packet_info_.received_bytes_counted) {
// Increment bytes counting on the new default path.
default_path_.bytes_received_before_address_validation += last_size_;
- current_incoming_packet_received_bytes_counted_ = true;
+ last_received_packet_info_.received_bytes_counted = true;
}
if (!previous_default_path.validated) {
@@ -5547,19 +5584,19 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) {
QuicSocketAddress current_effective_peer_address =
GetEffectivePeerAddressFromCurrentPacket();
if (!count_bytes_on_alternative_path_separately_ ||
- IsDefaultPath(last_packet_destination_address_,
- last_packet_source_address_)) {
+ IsDefaultPath(last_received_packet_info_.destination_address,
+ last_received_packet_info_.source_address)) {
return connected_;
}
QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 3, 5);
if (perspective_ == Perspective::IS_SERVER &&
type == PATH_CHALLENGE_FRAME &&
- !IsAlternativePath(last_packet_destination_address_,
+ !IsAlternativePath(last_received_packet_info_.destination_address,
current_effective_peer_address)) {
QUIC_DVLOG(1)
<< "The peer is probing a new path with effective peer address "
<< current_effective_peer_address << ", self address "
- << last_packet_destination_address_;
+ << last_received_packet_info_.destination_address;
if (!validate_client_addresses_) {
QuicConnectionId client_cid;
bool stateless_reset_token_received = false;
@@ -5568,10 +5605,11 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) {
default_path_, alternative_path_,
last_packet_destination_connection_id_, &client_cid,
&stateless_reset_token_received, &stateless_reset_token);
- alternative_path_ = PathState(
- last_packet_destination_address_, current_effective_peer_address,
- client_cid, last_packet_destination_connection_id_,
- stateless_reset_token_received, stateless_reset_token);
+ alternative_path_ =
+ PathState(last_received_packet_info_.destination_address,
+ current_effective_peer_address, client_cid,
+ last_packet_destination_connection_id_,
+ stateless_reset_token_received, stateless_reset_token);
} else if (!default_path_.validated) {
QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 4, 6);
// Skip reverse path validation because either handshake hasn't
@@ -5598,10 +5636,11 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) {
// Only override alternative path state upon receiving a PATH_CHALLENGE
// from an unvalidated peer address, and the connection isn't validating
// a recent peer migration.
- alternative_path_ = PathState(
- last_packet_destination_address_, current_effective_peer_address,
- client_connection_id, last_packet_destination_connection_id_,
- stateless_reset_token_received, stateless_reset_token);
+ alternative_path_ =
+ PathState(last_received_packet_info_.destination_address,
+ current_effective_peer_address, client_connection_id,
+ last_packet_destination_connection_id_,
+ stateless_reset_token_received, stateless_reset_token);
if (group_path_response_and_challenge_sending_closer_) {
should_proactively_validate_peer_address_on_path_challenge_ = true;
} else {
@@ -5613,12 +5652,12 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) {
QUIC_DVLOG(1) << "Proactively validate the effective peer address "
<< current_effective_peer_address;
QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 1, 6);
- ValidatePath(
- std::make_unique<ReversePathValidationContext>(
- default_path_.self_address, last_packet_source_address_,
- current_effective_peer_address, this),
- std::make_unique<ReversePathValidationResultDelegate>(
- this, peer_address()));
+ ValidatePath(std::make_unique<ReversePathValidationContext>(
+ default_path_.self_address,
+ last_received_packet_info_.source_address,
+ current_effective_peer_address, this),
+ std::make_unique<ReversePathValidationResultDelegate>(
+ this, peer_address()));
}
}
}
@@ -5659,15 +5698,17 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) {
<< current_effective_peer_migration_type_;
} else {
is_current_packet_connectivity_probing_ =
- (last_packet_source_address_ != peer_address()) ||
- (last_packet_destination_address_ != default_path_.self_address);
+ (last_received_packet_info_.source_address != peer_address()) ||
+ (last_received_packet_info_.destination_address !=
+ default_path_.self_address);
QUIC_DLOG_IF(INFO, is_current_packet_connectivity_probing_)
<< ENDPOINT
<< "Detected connectivity probing packet. "
- "last_packet_source_address_:"
- << last_packet_source_address_ << ", peer_address_:" << peer_address()
- << ", last_packet_destination_address_:"
- << last_packet_destination_address_
+ "last_packet_source_address:"
+ << last_received_packet_info_.source_address
+ << ", peer_address_:" << peer_address()
+ << ", last_packet_destination_address:"
+ << last_received_packet_info_.destination_address
<< ", default path self_address :" << default_path_.self_address;
}
return connected_;
@@ -5676,7 +5717,7 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) {
current_packet_content_ = NOT_PADDED_PING;
if (GetLargestReceivedPacket().IsInitialized() &&
last_header_.packet_number == GetLargestReceivedPacket()) {
- UpdatePeerAddress(last_packet_source_address_);
+ UpdatePeerAddress(last_received_packet_info_.source_address);
if (current_effective_peer_migration_type_ != NO_CHANGE) {
// Start effective peer migration immediately when the current packet is
// confirmed not a connectivity probing packet.
@@ -5715,11 +5756,11 @@ void QuicConnection::MaybeStartIetfPeerMigration() {
// TODO(fayang): When multiple packet number spaces is supported, only
// start peer migration for the application data.
if (!validate_client_addresses_) {
- UpdatePeerAddress(last_packet_source_address_);
+ UpdatePeerAddress(last_received_packet_info_.source_address);
}
StartEffectivePeerMigration(current_effective_peer_migration_type_);
} else {
- UpdatePeerAddress(last_packet_source_address_);
+ UpdatePeerAddress(last_received_packet_info_.source_address);
}
}
current_effective_peer_migration_type_ = NO_CHANGE;
@@ -5746,7 +5787,7 @@ void QuicConnection::PostProcessAfterAckFrame(bool send_stop_waiting,
// In case no new packets get acknowledged, it is possible packets are
// detected lost because of time based loss detection. Cancel blackhole
// detection if there is no packets in flight.
- blackhole_detector_.StopDetection();
+ blackhole_detector_.StopDetection(/*permanent=*/false);
}
if (send_stop_waiting) {
@@ -5793,14 +5834,14 @@ void QuicConnection::ResetAckStates() {
}
MessageStatus QuicConnection::SendMessage(QuicMessageId message_id,
- QuicMemSliceSpan message,
+ absl::Span<QuicMemSlice> message,
bool flush) {
if (!VersionSupportsMessageFrames(transport_version())) {
QUIC_BUG(quic_bug_10511_38)
<< "MESSAGE frame is not supported for version " << transport_version();
return MESSAGE_STATUS_UNSUPPORTED;
}
- if (message.total_length() > GetCurrentLargestMessagePayload()) {
+ if (MemSliceSpanTotalSize(message) > GetCurrentLargestMessagePayload()) {
return MESSAGE_STATUS_TOO_LARGE;
}
if (!connected_ || (!flush && !CanWrite(HAS_RETRANSMITTABLE_DATA))) {
@@ -5899,8 +5940,6 @@ void QuicConnection::SendAllPendingAcks() {
if (!earliest_ack_timeout.IsInitialized()) {
return;
}
- // Latches current encryption level.
- const EncryptionLevel current_encryption_level = encryption_level_;
for (int8_t i = INITIAL_DATA; i <= APPLICATION_DATA; ++i) {
const QuicTime ack_timeout = uber_received_packet_manager_.GetAckTimeout(
static_cast<PacketNumberSpace>(i));
@@ -5920,14 +5959,8 @@ void QuicConnection::SendAllPendingAcks() {
QUIC_DVLOG(1) << ENDPOINT << "Sending ACK of packet number space "
<< PacketNumberSpaceToString(
static_cast<PacketNumberSpace>(i));
- // Switch to the appropriate encryption level.
- if (!use_encryption_level_context_) {
- SetDefaultEncryptionLevel(
- QuicUtils::GetEncryptionLevel(static_cast<PacketNumberSpace>(i)));
- }
ScopedEncryptionLevelContext context(
- use_encryption_level_context_ ? this : nullptr,
- QuicUtils::GetEncryptionLevel(static_cast<PacketNumberSpace>(i)));
+ this, QuicUtils::GetEncryptionLevel(static_cast<PacketNumberSpace>(i)));
QuicFrames frames;
frames.push_back(uber_received_packet_manager_.GetUpdatedAckFrame(
static_cast<PacketNumberSpace>(i), clock_->ApproximateNow()));
@@ -5943,10 +5976,6 @@ void QuicConnection::SendAllPendingAcks() {
}
ResetAckStates();
}
- if (!use_encryption_level_context_) {
- // Restores encryption level.
- SetDefaultEncryptionLevel(current_encryption_level);
- }
const QuicTime timeout =
uber_received_packet_manager_.GetEarliestAckTimeout();
@@ -6138,6 +6167,9 @@ void QuicConnection::SetLargestReceivedPacketWithAck(
}
void QuicConnection::OnForwardProgressMade() {
+ if (GetQuicRestartFlag(quic_alarm_add_permanent_cancel) && !connected_) {
+ return;
+ }
if (is_path_degrading_) {
visitor_->OnForwardProgressMadeAfterPathDegrading();
is_path_degrading_ = false;
@@ -6149,7 +6181,7 @@ void QuicConnection::OnForwardProgressMade() {
GetPathMtuReductionDeadline());
} else {
// Stop detections in quiecense.
- blackhole_detector_.StopDetection();
+ blackhole_detector_.StopDetection(/*permanent=*/false);
}
QUIC_BUG_IF(quic_bug_12714_35,
default_enable_5rto_blackhole_detection_ &&
@@ -6257,13 +6289,7 @@ void QuicConnection::set_client_connection_id(
<< client_connection_id << " with unsupported version " << version();
return;
}
- if (use_connection_id_on_default_path_) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_connection_id_on_default_path_v2, 1,
- 3);
- default_path_.client_connection_id = client_connection_id;
- } else {
- client_connection_id_ = client_connection_id;
- }
+ default_path_.client_connection_id = client_connection_id;
client_connection_id_is_set_ = true;
if (support_multiple_connection_ids_ && !client_connection_id.IsEmpty()) {
@@ -6280,11 +6306,12 @@ void QuicConnection::set_client_connection_id(
}
}
QUIC_DLOG(INFO) << ENDPOINT << "setting client connection ID to "
- << ClientConnectionId()
+ << default_path_.client_connection_id
<< " for connection with server connection ID "
- << ServerConnectionId();
- packet_creator_.SetClientConnectionId(ClientConnectionId());
- framer_.SetExpectedClientConnectionIdLength(ClientConnectionId().length());
+ << default_path_.server_connection_id;
+ packet_creator_.SetClientConnectionId(default_path_.client_connection_id);
+ framer_.SetExpectedClientConnectionIdLength(
+ default_path_.client_connection_id.length());
}
void QuicConnection::OnPathDegradingDetected() {
@@ -6333,6 +6360,10 @@ void QuicConnection::OnIdleNetworkDetected() {
"No recent network activity after ", duration.ToDebuggingValue(),
". Timeout:",
idle_network_detector_.idle_network_timeout().ToDebuggingValue());
+ if (perspective() == Perspective::IS_CLIENT && version().UsesTls() &&
+ !IsHandshakeComplete()) {
+ absl::StrAppend(&error_details, UndecryptablePacketsInfo());
+ }
QUIC_DVLOG(1) << ENDPOINT << error_details;
const bool has_consecutive_pto =
sent_packet_manager_.GetConsecutiveTlpCount() > 0 ||
@@ -6362,9 +6393,10 @@ void QuicConnection::OnIdleNetworkDetected() {
void QuicConnection::OnPeerIssuedConnectionIdRetired() {
QUICHE_DCHECK(peer_issued_cid_manager_ != nullptr);
- QuicConnectionId* default_path_cid = perspective_ == Perspective::IS_CLIENT
- ? &ServerConnectionId()
- : &ClientConnectionId();
+ QuicConnectionId* default_path_cid =
+ perspective_ == Perspective::IS_CLIENT
+ ? &default_path_.server_connection_id
+ : &default_path_.client_connection_id;
QuicConnectionId* alternative_path_cid =
perspective_ == Perspective::IS_CLIENT
? &alternative_path_.server_connection_id
@@ -6376,8 +6408,7 @@ void QuicConnection::OnPeerIssuedConnectionIdRetired() {
*default_path_cid = QuicConnectionId();
}
// TODO(haoyuewang) Handle the change for default_path_ & alternatvie_path_
- // via the same helper function after use_connection_id_on_default_path_ is
- // default true.
+ // via the same helper function.
if (default_path_cid->IsEmpty()) {
// Try setting a new connection ID now such that subsequent
// RetireConnectionId frames can be sent on the default path.
@@ -6385,15 +6416,9 @@ void QuicConnection::OnPeerIssuedConnectionIdRetired() {
peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
if (unused_connection_id_data != nullptr) {
*default_path_cid = unused_connection_id_data->connection_id;
- if (use_connection_id_on_default_path_) {
- default_path_.stateless_reset_token =
- unused_connection_id_data->stateless_reset_token;
- default_path_.stateless_reset_token_received = true;
- } else {
- received_stateless_reset_token_ =
- unused_connection_id_data->stateless_reset_token;
- stateless_reset_token_received_ = true;
- }
+ default_path_.stateless_reset_token =
+ unused_connection_id_data->stateless_reset_token;
+ default_path_.stateless_reset_token_received = true;
if (perspective_ == Perspective::IS_CLIENT) {
packet_creator_.SetServerConnectionId(
unused_connection_id_data->connection_id);
@@ -6403,25 +6428,23 @@ void QuicConnection::OnPeerIssuedConnectionIdRetired() {
}
}
}
- if (use_connection_id_on_default_path_) {
- if (default_path_and_alternative_path_use_the_same_peer_connection_id) {
- *alternative_path_cid = *default_path_cid;
- alternative_path_.stateless_reset_token_received =
- default_path_.stateless_reset_token_received;
+ if (default_path_and_alternative_path_use_the_same_peer_connection_id) {
+ *alternative_path_cid = *default_path_cid;
+ alternative_path_.stateless_reset_token_received =
+ default_path_.stateless_reset_token_received;
+ alternative_path_.stateless_reset_token =
+ default_path_.stateless_reset_token;
+ } else if (!alternative_path_cid->IsEmpty() &&
+ !peer_issued_cid_manager_->IsConnectionIdActive(
+ *alternative_path_cid)) {
+ *alternative_path_cid = EmptyQuicConnectionId();
+ const QuicConnectionIdData* unused_connection_id_data =
+ peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
+ if (unused_connection_id_data != nullptr) {
+ *alternative_path_cid = unused_connection_id_data->connection_id;
alternative_path_.stateless_reset_token =
- default_path_.stateless_reset_token;
- } else if (!alternative_path_cid->IsEmpty() &&
- !peer_issued_cid_manager_->IsConnectionIdActive(
- *alternative_path_cid)) {
- *alternative_path_cid = EmptyQuicConnectionId();
- const QuicConnectionIdData* unused_connection_id_data =
- peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
- if (unused_connection_id_data != nullptr) {
- *alternative_path_cid = unused_connection_id_data->connection_id;
- alternative_path_.stateless_reset_token =
- unused_connection_id_data->stateless_reset_token;
- alternative_path_.stateless_reset_token_received = true;
- }
+ unused_connection_id_data->stateless_reset_token;
+ alternative_path_.stateless_reset_token_received = true;
}
}
@@ -6532,6 +6555,9 @@ bool QuicConnection::SendPathChallenge(
const QuicSocketAddress& peer_address,
const QuicSocketAddress& effective_peer_address,
QuicPacketWriter* writer) {
+ if (!framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_FORWARD_SECURE)) {
+ return connected_;
+ }
if (connection_migration_use_new_cid_) {
{
QuicConnectionId client_cid, server_cid;
@@ -6653,9 +6679,12 @@ bool QuicConnection::SendPathResponse(
const QuicPathFrameBuffer& data_buffer,
const QuicSocketAddress& peer_address_to_send,
const QuicSocketAddress& effective_peer_address) {
+ if (!framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_FORWARD_SECURE)) {
+ return false;
+ }
QuicConnectionId client_cid, server_cid;
if (connection_migration_use_new_cid_) {
- FindOnPathConnectionIds(last_packet_destination_address_,
+ FindOnPathConnectionIds(last_received_packet_info_.destination_address,
effective_peer_address, &client_cid, &server_cid);
}
// Send PATH_RESPONSE using the provided peer address. If the creator has been
@@ -6665,7 +6694,8 @@ bool QuicConnection::SendPathResponse(
&packet_creator_, peer_address_to_send, client_cid, server_cid,
connection_migration_use_new_cid_);
QUIC_DVLOG(1) << ENDPOINT << "Send PATH_RESPONSE to " << peer_address_to_send;
- if (default_path_.self_address == last_packet_destination_address_) {
+ if (default_path_.self_address ==
+ last_received_packet_info_.destination_address) {
// The PATH_CHALLENGE is received on the default socket. Respond on the same
// socket.
return packet_creator_.AddPathResponseFrame(data_buffer);
@@ -6676,8 +6706,9 @@ bool QuicConnection::SendPathResponse(
// used to send PATH_RESPONSE.
if (!path_validator_.HasPendingPathValidation() ||
path_validator_.GetContext()->self_address() !=
- last_packet_destination_address_) {
- // Ignore this PATH_CHALLENGE if it's received from an uninteresting socket.
+ last_received_packet_info_.destination_address) {
+ // Ignore this PATH_CHALLENGE if it's received from an uninteresting
+ // socket.
return true;
}
QuicPacketWriter* writer = path_validator_.GetContext()->WriterToUse();
@@ -6688,12 +6719,13 @@ bool QuicConnection::SendPathResponse(
QUICHE_DCHECK_EQ(IsRetransmittable(*probing_packet), NO_RETRANSMITTABLE_DATA);
QUIC_DVLOG(1) << ENDPOINT
<< "Send PATH_RESPONSE from alternative socket with address "
- << last_packet_destination_address_;
+ << last_received_packet_info_.destination_address;
// Ignore the return value to treat write error on the alternative writer as
// part of network error. If the writer becomes blocked, wait for the peer to
// send another PATH_CHALLENGE.
WritePacketUsingWriter(std::move(probing_packet), writer,
- last_packet_destination_address_, peer_address_to_send,
+ last_received_packet_info_.destination_address,
+ peer_address_to_send,
/*measure_rtt=*/false);
return true;
}
@@ -6766,7 +6798,6 @@ bool QuicConnection::UpdateConnectionIdsOnClientMigration(
}
void QuicConnection::RetirePeerIssuedConnectionIdsNoLongerOnPath() {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 4, 5);
if (!connection_migration_use_new_cid_ ||
peer_issued_cid_manager_ == nullptr) {
return;
@@ -6786,13 +6817,20 @@ bool QuicConnection::MigratePath(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
QuicPacketWriter* writer,
bool owns_writer) {
+ QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
if (!connected_) {
+ if (owns_writer) {
+ delete writer;
+ }
return false;
}
QUICHE_DCHECK(!version().UsesHttp3() || IsHandshakeConfirmed());
if (connection_migration_use_new_cid_) {
if (!UpdateConnectionIdsOnClientMigration(self_address, peer_address)) {
+ if (owns_writer) {
+ delete writer;
+ }
return false;
}
if (packet_creator_.GetServerConnectionId().length() !=
@@ -6816,6 +6854,7 @@ bool QuicConnection::MigratePath(const QuicSocketAddress& self_address,
SetSelfAddress(self_address);
UpdatePeerAddress(peer_address);
SetQuicPacketWriter(writer, owns_writer);
+ MaybeClearQueuedPacketsOnPathChange();
OnSuccessfulMigration(is_port_change);
return true;
}
@@ -6825,13 +6864,19 @@ void QuicConnection::OnPathValidationFailureAtClient() {
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
alternative_path_.Clear();
}
+ // The alarm to retire connection IDs no longer on paths is scheduled at the
+ // end of writing and reading packet. On path validation failure, there could
+ // be no packet to write or read. Hence the retirement alarm for the
+ // connection ID associated with the failed path needs to be proactively
+ // scheduled here.
+ RetirePeerIssuedConnectionIdsNoLongerOnPath();
}
std::vector<QuicConnectionId> QuicConnection::GetActiveServerConnectionIds()
const {
if (!support_multiple_connection_ids_ ||
self_issued_cid_manager_ == nullptr) {
- return {ServerConnectionId()};
+ return {default_path_.server_connection_id};
}
return self_issued_cid_manager_->GetUnretiredConnectionIds();
}
@@ -6842,14 +6887,14 @@ void QuicConnection::CreateConnectionIdManager() {
}
if (perspective_ == Perspective::IS_CLIENT) {
- if (!ServerConnectionId().IsEmpty()) {
+ if (!default_path_.server_connection_id.IsEmpty()) {
peer_issued_cid_manager_ =
std::make_unique<QuicPeerIssuedConnectionIdManager>(
- kMinNumOfActiveConnectionIds, ServerConnectionId(), clock_,
- alarm_factory_, this);
+ kMinNumOfActiveConnectionIds, default_path_.server_connection_id,
+ clock_, alarm_factory_, this);
}
} else {
- if (!ServerConnectionId().IsEmpty()) {
+ if (!default_path_.server_connection_id.IsEmpty()) {
self_issued_cid_manager_ = MakeSelfIssuedConnectionIdManager();
}
}
@@ -6905,20 +6950,20 @@ void QuicConnection::MaybeUpdateBytesReceivedFromAlternativeAddress(
QuicByteCount received_packet_size) {
if (!version().SupportsAntiAmplificationLimit() ||
perspective_ != Perspective::IS_SERVER ||
- !IsAlternativePath(last_packet_destination_address_,
+ !IsAlternativePath(last_received_packet_info_.destination_address,
GetEffectivePeerAddressFromCurrentPacket()) ||
- current_incoming_packet_received_bytes_counted_) {
+ last_received_packet_info_.received_bytes_counted) {
return;
}
// Only update bytes received if this probing frame is received on the most
// recent alternative path.
- QUICHE_DCHECK(!IsDefaultPath(last_packet_destination_address_,
+ QUICHE_DCHECK(!IsDefaultPath(last_received_packet_info_.destination_address,
GetEffectivePeerAddressFromCurrentPacket()));
if (!alternative_path_.validated) {
alternative_path_.bytes_received_before_address_validation +=
received_packet_size;
}
- current_incoming_packet_received_bytes_counted_ = true;
+ last_received_packet_info_.received_bytes_counted = true;
}
bool QuicConnection::IsDefaultPath(
@@ -7061,6 +7106,7 @@ void QuicConnection::RestoreToLastValidatedPath(
ConnectionCloseBehavior::SILENT_CLOSE);
return;
}
+ MaybeClearQueuedPacketsOnPathChange();
// Revert congestion control context to old state.
OnPeerIpAddressChanged();
@@ -7097,7 +7143,7 @@ QuicConnection::OnPeerIpAddressChanged() {
// re-arm it.
SetRetransmissionAlarm();
// Stop detections in quiecense.
- blackhole_detector_.StopDetection();
+ blackhole_detector_.StopDetection(/*permanent=*/false);
return old_send_algorithm;
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection.h
index b9567250637..dea513a1768 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_connection.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection.h
@@ -36,6 +36,7 @@
#include "quic/core/quic_alarm.h"
#include "quic/core/quic_alarm_factory.h"
#include "quic/core/quic_blocked_writer_interface.h"
+#include "quic/core/quic_connection_context.h"
#include "quic/core/quic_connection_id.h"
#include "quic/core/quic_connection_id_manager.h"
#include "quic/core/quic_connection_stats.h"
@@ -777,9 +778,11 @@ class QUIC_EXPORT_PRIVATE QuicConnection
const QuicSocketAddress& effective_peer_address() const {
return default_path_.peer_address;
}
- const QuicConnectionId& connection_id() const { return ServerConnectionId(); }
+ const QuicConnectionId& connection_id() const {
+ return default_path_.server_connection_id;
+ }
const QuicConnectionId& client_connection_id() const {
- return ClientConnectionId();
+ return default_path_.client_connection_id;
}
void set_client_connection_id(QuicConnectionId client_connection_id);
const QuicClock* clock() const { return clock_; }
@@ -985,7 +988,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection
// If |flush| is false, this will return a MESSAGE_STATUS_BLOCKED
// when the connection is deemed unwritable.
virtual MessageStatus SendMessage(QuicMessageId message_id,
- QuicMemSliceSpan message,
+ absl::Span<QuicMemSlice> message,
bool flush);
// Returns the largest payload that will fit into a single MESSAGE frame.
@@ -1028,7 +1031,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection
}
const QuicSocketAddress& last_packet_source_address() const {
- return last_packet_source_address_;
+ return last_received_packet_info_.source_address;
}
bool fill_up_link_during_probing() const {
@@ -1199,12 +1202,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection
bool is_processing_packet() const { return framer_.is_processing_packet(); }
- bool encrypted_control_frames() const { return encrypted_control_frames_; }
-
- bool use_encryption_level_context() const {
- return use_encryption_level_context_;
- }
-
bool HasPendingPathValidation() const;
QuicPathValidationContext* GetPathValidationContext() const;
@@ -1225,9 +1222,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection
void SetSourceAddressTokenToSend(absl::string_view token);
void SendPing() {
- SendPingAtLevel(use_encryption_level_context_
- ? framer().GetEncryptionLevelToSendApplicationData()
- : encryption_level_);
+ SendPingAtLevel(framer().GetEncryptionLevelToSendApplicationData());
}
virtual std::vector<QuicConnectionId> GetActiveServerConnectionIds() const;
@@ -1238,10 +1233,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection
return support_multiple_connection_ids_;
}
- bool use_connection_id_on_default_path() const {
- return use_connection_id_on_default_path_;
- }
-
bool connection_migration_use_new_cid() const {
return connection_migration_use_new_cid_;
}
@@ -1253,8 +1244,11 @@ class QUIC_EXPORT_PRIVATE QuicConnection
// Instantiates connection ID manager.
void CreateConnectionIdManager();
- bool donot_write_mid_packet_processing() const {
- return donot_write_mid_packet_processing_;
+ QuicConnectionContext* context() { return &context_; }
+ const QuicConnectionContext* context() const { return &context_; }
+
+ void set_tracer(std::unique_ptr<QuicConnectionTracer> tracer) {
+ context_.tracer.swap(tracer);
}
protected:
@@ -1423,15 +1417,38 @@ class QUIC_EXPORT_PRIVATE QuicConnection
const QuicSocketAddress peer_address;
};
- // UndecrytablePacket comprises a undecryptable packet and the its encryption
- // level.
+ // ReceivedPacketInfo comprises the received packet information, which can be
+ // retrieved before the packet gets successfully decrypted.
+ struct QUIC_EXPORT_PRIVATE ReceivedPacketInfo {
+ explicit ReceivedPacketInfo(QuicTime receipt_time)
+ : received_bytes_counted(false), receipt_time(receipt_time) {}
+ ReceivedPacketInfo(const QuicSocketAddress& destination_address,
+ const QuicSocketAddress& source_address,
+ QuicTime receipt_time)
+ : received_bytes_counted(false),
+ destination_address(destination_address),
+ source_address(source_address),
+ receipt_time(receipt_time) {}
+
+ bool received_bytes_counted;
+ QuicSocketAddress destination_address;
+ QuicSocketAddress source_address;
+ QuicTime receipt_time;
+ };
+
+ // UndecrytablePacket comprises a undecryptable packet and related
+ // information.
struct QUIC_EXPORT_PRIVATE UndecryptablePacket {
UndecryptablePacket(const QuicEncryptedPacket& packet,
- EncryptionLevel encryption_level)
- : packet(packet.Clone()), encryption_level(encryption_level) {}
+ EncryptionLevel encryption_level,
+ const ReceivedPacketInfo& packet_info)
+ : packet(packet.Clone()),
+ encryption_level(encryption_level),
+ packet_info(packet_info) {}
std::unique_ptr<QuicEncryptedPacket> packet;
EncryptionLevel encryption_level;
+ ReceivedPacketInfo packet_info;
};
// Handles the reverse path validation result depending on connection state:
@@ -1468,27 +1485,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection
QuicConnection* connection_; // Not owned.
};
- QuicConnectionId& ClientConnectionId() {
- return use_connection_id_on_default_path_
- ? default_path_.client_connection_id
- : client_connection_id_;
- }
- const QuicConnectionId& ClientConnectionId() const {
- return use_connection_id_on_default_path_
- ? default_path_.client_connection_id
- : client_connection_id_;
- }
- QuicConnectionId& ServerConnectionId() {
- return use_connection_id_on_default_path_
- ? default_path_.server_connection_id
- : server_connection_id_;
- }
- const QuicConnectionId& ServerConnectionId() const {
- return use_connection_id_on_default_path_
- ? default_path_.server_connection_id
- : server_connection_id_;
- }
- void SetServerConnectionId(const QuicConnectionId& server_connection_id);
+ // If peer uses non-empty connection ID, discards any buffered packets on path
+ // change in IETF QUIC.
+ void MaybeClearQueuedPacketsOnPathChange();
// Notifies the visitor of the close and marks the connection as disconnected.
// Does not send a connection close frame to the peer. It should only be
@@ -1704,9 +1703,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection
// Returns the largest sent packet number that has been ACKed by peer.
QuicPacketNumber GetLargestAckedPacket() const;
- // Whether incoming_connection_ids_ contains connection_id.
- bool HasIncomingConnectionId(QuicConnectionId connection_id) const;
-
// Whether connection is limited by amplification factor.
bool LimitedByAmplificationFactor() const;
@@ -1846,6 +1842,13 @@ class QUIC_EXPORT_PRIVATE QuicConnection
// when a new client connection ID is received.
void OnClientConnectionIdAvailable();
+ // Returns true if connection needs to set retransmission alarm after a packet
+ // gets sent.
+ bool ShouldSetRetransmissionAlarmOnPacketSent(bool in_flight,
+ EncryptionLevel level) const;
+
+ QuicConnectionContext context_;
+
QuicFramer framer_;
// Contents received in the current packet, especially used to identify
@@ -1873,8 +1876,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection
const QuicClock* clock_;
QuicRandom* random_generator_;
- QuicConnectionId server_connection_id_;
- QuicConnectionId client_connection_id_;
// On the server, the connection ID is set when receiving the first packet.
// This variable ensures we only set it this way once.
bool client_connection_id_is_set_;
@@ -2022,10 +2023,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection
QuicPacketCreator packet_creator_;
- // The time that a packet is received for this connection. Initialized to
- // connection creation time.
- // This does not indicate the packet was processed.
- QuicTime time_of_last_received_packet_;
+ // Information about the last received QUIC packet, which may not have been
+ // successfully decrypted and processed.
+ ReceivedPacketInfo last_received_packet_info_;
// Sent packet manager which tracks the status of packets sent by this
// connection and contains the send and receive algorithms to determine when
@@ -2043,12 +2043,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection
// close.
bool connected_;
- // Destination address of the last received packet.
- QuicSocketAddress last_packet_destination_address_;
-
- // Source address of the last received packet.
- QuicSocketAddress last_packet_source_address_;
-
// Destination connection ID of the last received packet. If this ID is the
// original server connection ID chosen by client and server replaces it with
// a different ID, last_packet_destination_connection_id_ is set to the
@@ -2118,12 +2112,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection
// retransmission code.
bool probing_retransmission_pending_;
- // Indicates whether a stateless reset token has been received from peer.
- bool stateless_reset_token_received_;
- // Stores received stateless reset token from peer. Used to verify whether a
- // packet is a stateless reset packet.
- StatelessResetToken received_stateless_reset_token_;
-
// Id of latest sent control frame. 0 if no control frame has been sent.
QuicControlFrameId last_control_frame_id_;
@@ -2164,11 +2152,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection
quiche::QuicheCircularDeque<PendingPathChallenge>
pending_path_challenge_payloads_;
- // Set of connection IDs that should be accepted as destination on
- // received packets. This is conceptually a set but is implemented as a
- // vector to improve performance since it is expected to be very small.
- std::vector<QuicConnectionId> incoming_connection_ids_;
-
// When we receive a RETRY packet or some INITIAL packets, we replace
// |server_connection_id_| with the value from that packet and save off the
// original value of |server_connection_id_| into
@@ -2252,10 +2235,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection
// True if we are currently processing OnRetransmissionTimeout.
bool in_on_retransmission_time_out_ = false;
- const bool encrypted_control_frames_;
-
- const bool use_encryption_level_context_;
-
QuicPathValidator path_validator_;
// Stores information of a path which maybe used as default path in the
@@ -2273,8 +2252,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection
// This field is used to debug b/177312785.
QuicFrameType most_recent_frame_type_;
- bool current_incoming_packet_received_bytes_counted_ = false;
-
bool count_bytes_on_alternative_path_separately_ =
GetQuicReloadableFlag(quic_count_bytes_on_alternative_path_seperately);
@@ -2283,12 +2260,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection
bool support_multiple_connection_ids_ = false;
- const bool donot_write_mid_packet_processing_ =
- GetQuicReloadableFlag(quic_donot_write_mid_packet_processing);
-
- bool use_connection_id_on_default_path_ =
- GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2);
-
// Indicates whether we should proactively validate peer address on a
// PATH_CHALLENGE received.
bool should_proactively_validate_peer_address_on_path_challenge_ = false;
@@ -2300,8 +2271,14 @@ class QUIC_EXPORT_PRIVATE QuicConnection
GetQuicReloadableFlag(
quic_group_path_response_and_challenge_sending_closer);
- const bool quic_deprecate_incoming_connection_ids_ =
- GetQuicReloadableFlag(quic_deprecate_incoming_connection_ids);
+ const bool reset_per_packet_state_for_undecryptable_packets_ =
+ GetQuicReloadableFlag(
+ quic_reset_per_packet_state_for_undecryptable_packets);
+
+ const bool add_missing_update_ack_timeout_ =
+ GetQuicReloadableFlag(quic_add_missing_update_ack_timeout);
+
+ const bool ack_cid_frames_ = GetQuicReloadableFlag(quic_ack_cid_frames);
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_context.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_context.cc
new file mode 100644
index 00000000000..e77fc431289
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_context.cc
@@ -0,0 +1,36 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quic/core/quic_connection_context.h"
+
+#include "common/platform/api/quiche_thread_local.h"
+
+namespace quic {
+namespace {
+DEFINE_QUICHE_THREAD_LOCAL_POINTER(CurrentContext, QuicConnectionContext);
+} // namespace
+
+// static
+QuicConnectionContext* QuicConnectionContext::Current() {
+ return GET_QUICHE_THREAD_LOCAL_POINTER(CurrentContext);
+}
+
+QuicConnectionContextSwitcher::QuicConnectionContextSwitcher(
+ QuicConnectionContext* new_context)
+ : old_context_(QuicConnectionContext::Current()) {
+ SET_QUICHE_THREAD_LOCAL_POINTER(CurrentContext, new_context);
+ if (new_context && new_context->tracer) {
+ new_context->tracer->Activate();
+ }
+}
+
+QuicConnectionContextSwitcher::~QuicConnectionContextSwitcher() {
+ QuicConnectionContext* current = QuicConnectionContext::Current();
+ if (current && current->tracer) {
+ current->tracer->Deactivate();
+ }
+ SET_QUICHE_THREAD_LOCAL_POINTER(CurrentContext, old_context_);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_context.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_context.h
new file mode 100644
index 00000000000..7cedee63353
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_context.h
@@ -0,0 +1,118 @@
+// Copyright 2021 The Chromium 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 QUICHE_QUIC_CORE_QUIC_CONNECTION_CONTEXT_H_
+#define QUICHE_QUIC_CORE_QUIC_CONNECTION_CONTEXT_H_
+
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "quic/platform/api/quic_export.h"
+#include "common/platform/api/quiche_logging.h"
+
+namespace quic {
+
+// QuicConnectionTracer is responsible for emit trace messages for a single
+// QuicConnection.
+// QuicConnectionTracer is part of the QuicConnectionContext.
+class QUIC_EXPORT_PRIVATE QuicConnectionTracer {
+ public:
+ virtual ~QuicConnectionTracer() = default;
+
+ // Emit a trace message from a string literal. The trace may simply remember
+ // the address of the literal in this function and read it at a later time.
+ virtual void PrintLiteral(const char* literal) = 0;
+
+ // Emit a trace message from a string_view. Unlike PrintLiteral, this function
+ // will not read |s| after it returns.
+ virtual void PrintString(absl::string_view s) = 0;
+
+ // Emit a trace message from printf-style arguments.
+ template <typename... Args>
+ void Printf(const absl::FormatSpec<Args...>& format, const Args&... args) {
+ std::string s = absl::StrFormat(format, args...);
+ PrintString(s);
+ }
+
+ private:
+ friend class QuicConnectionContextSwitcher;
+
+ // Called by QuicConnectionContextSwitcher, when |this| becomes the current
+ // thread's QUIC connection tracer.
+ //
+ // Activate/Deactivate are only called by QuicConnectionContextSwitcher's
+ // constructor/destructor, they always come in pairs.
+ virtual void Activate() {}
+
+ // Called by QuicConnectionContextSwitcher, when |this| stops from being the
+ // current thread's QUIC connection tracer.
+ //
+ // Activate/Deactivate are only called by QuicConnectionContextSwitcher's
+ // constructor/destructor, they always come in pairs.
+ virtual void Deactivate() {}
+};
+
+// QuicConnectionContext is a per-QuicConnection context that includes
+// facilities useable by any part of a QuicConnection. A QuicConnectionContext
+// is owned by a QuicConnection.
+//
+// The 'top-level' QuicConnection functions are responsible for maintaining the
+// thread-local QuicConnectionContext pointer, such that any function called by
+// them(directly or indirectly) can access the context.
+//
+// Like QuicConnection, all facilities in QuicConnectionContext are assumed to
+// be called from a single thread at a time, they are NOT thread-safe.
+struct QUIC_EXPORT_PRIVATE QuicConnectionContext final {
+ // Get the context on the current executing thread. nullptr if the current
+ // function is not called from a 'top-level' QuicConnection function.
+ static QuicConnectionContext* Current();
+
+ std::unique_ptr<QuicConnectionTracer> tracer;
+};
+
+// QuicConnectionContextSwitcher is a RAII object used for maintaining the
+// thread-local QuicConnectionContext pointer.
+class QUIC_EXPORT_PRIVATE QuicConnectionContextSwitcher final {
+ public:
+ // The constructor switches from QuicConnectionContext::Current() to
+ // |new_context|.
+ explicit QuicConnectionContextSwitcher(QuicConnectionContext* new_context);
+
+ // The destructor switches from QuicConnectionContext::Current() back to the
+ // old context.
+ ~QuicConnectionContextSwitcher();
+
+ private:
+ QuicConnectionContext* old_context_;
+};
+
+// Emit a trace message from a string literal to the current tracer(if any).
+inline void QUIC_TRACELITERAL(const char* literal) {
+ QuicConnectionContext* current = QuicConnectionContext::Current();
+ if (current && current->tracer) {
+ current->tracer->PrintLiteral(literal);
+ }
+}
+
+// Emit a trace message from a string_view to the current tracer(if any).
+inline void QUIC_TRACESTRING(absl::string_view s) {
+ QuicConnectionContext* current = QuicConnectionContext::Current();
+ if (current && current->tracer) {
+ current->tracer->PrintString(s);
+ }
+}
+
+// Emit a trace message from printf-style arguments to the current tracer(if
+// any).
+template <typename... Args>
+void QUIC_TRACEPRINTF(const absl::FormatSpec<Args...>& format,
+ const Args&... args) {
+ QuicConnectionContext* current = QuicConnectionContext::Current();
+ if (current && current->tracer) {
+ current->tracer->Printf(format, args...);
+ }
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CONNECTION_CONTEXT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_context_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_context_test.cc
new file mode 100644
index 00000000000..0f5b2c481b6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_context_test.cc
@@ -0,0 +1,173 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quic/core/quic_connection_context.h"
+
+#include "quic/platform/api/quic_test.h"
+#include "quic/platform/api/quic_thread.h"
+
+using testing::ElementsAre;
+
+namespace quic {
+namespace {
+
+class TraceCollector : public QuicConnectionTracer {
+ public:
+ ~TraceCollector() override = default;
+
+ void PrintLiteral(const char* literal) override { trace_.push_back(literal); }
+
+ void PrintString(absl::string_view s) override {
+ trace_.push_back(std::string(s));
+ }
+
+ const std::vector<std::string>& trace() const { return trace_; }
+
+ private:
+ std::vector<std::string> trace_;
+};
+
+struct FakeConnection {
+ FakeConnection() { context.tracer = std::make_unique<TraceCollector>(); }
+
+ const std::vector<std::string>& trace() const {
+ return static_cast<const TraceCollector*>(context.tracer.get())->trace();
+ }
+
+ QuicConnectionContext context;
+};
+
+void SimpleSwitch() {
+ FakeConnection connection;
+
+ // These should be ignored since current context is nullptr.
+ EXPECT_EQ(QuicConnectionContext::Current(), nullptr);
+ QUIC_TRACELITERAL("before switch: literal");
+ QUIC_TRACESTRING(std::string("before switch: string"));
+ QUIC_TRACEPRINTF("%s: %s", "before switch", "printf");
+
+ {
+ QuicConnectionContextSwitcher switcher(&connection.context);
+ QUIC_TRACELITERAL("literal");
+ QUIC_TRACESTRING(std::string("string"));
+ QUIC_TRACEPRINTF("%s", "printf");
+ }
+
+ EXPECT_EQ(QuicConnectionContext::Current(), nullptr);
+ QUIC_TRACELITERAL("after switch: literal");
+ QUIC_TRACESTRING(std::string("after switch: string"));
+ QUIC_TRACEPRINTF("%s: %s", "after switch", "printf");
+
+ EXPECT_THAT(connection.trace(), ElementsAre("literal", "string", "printf"));
+}
+
+void NestedSwitch() {
+ FakeConnection outer, inner;
+
+ {
+ QuicConnectionContextSwitcher switcher(&outer.context);
+ QUIC_TRACELITERAL("outer literal 0");
+ QUIC_TRACESTRING(std::string("outer string 0"));
+ QUIC_TRACEPRINTF("%s %s %d", "outer", "printf", 0);
+
+ {
+ QuicConnectionContextSwitcher switcher(&inner.context);
+ QUIC_TRACELITERAL("inner literal");
+ QUIC_TRACESTRING(std::string("inner string"));
+ QUIC_TRACEPRINTF("%s %s", "inner", "printf");
+ }
+
+ QUIC_TRACELITERAL("outer literal 1");
+ QUIC_TRACESTRING(std::string("outer string 1"));
+ QUIC_TRACEPRINTF("%s %s %d", "outer", "printf", 1);
+ }
+
+ EXPECT_THAT(outer.trace(), ElementsAre("outer literal 0", "outer string 0",
+ "outer printf 0", "outer literal 1",
+ "outer string 1", "outer printf 1"));
+
+ EXPECT_THAT(inner.trace(),
+ ElementsAre("inner literal", "inner string", "inner printf"));
+}
+
+void AlternatingSwitch() {
+ FakeConnection zero, one, two;
+ for (int i = 0; i < 15; ++i) {
+ FakeConnection* connection =
+ ((i % 3) == 0) ? &zero : (((i % 3) == 1) ? &one : &two);
+
+ QuicConnectionContextSwitcher switcher(&connection->context);
+ QUIC_TRACEPRINTF("%d", i);
+ }
+
+ EXPECT_THAT(zero.trace(), ElementsAre("0", "3", "6", "9", "12"));
+ EXPECT_THAT(one.trace(), ElementsAre("1", "4", "7", "10", "13"));
+ EXPECT_THAT(two.trace(), ElementsAre("2", "5", "8", "11", "14"));
+}
+
+typedef void (*ThreadFunction)();
+
+template <ThreadFunction func>
+class TestThread : public QuicThread {
+ public:
+ TestThread() : QuicThread("TestThread") {}
+ ~TestThread() override = default;
+
+ protected:
+ void Run() override { func(); }
+};
+
+template <ThreadFunction func>
+void RunInThreads(size_t n_threads) {
+ using ThreadType = TestThread<func>;
+ std::vector<ThreadType> threads(n_threads);
+
+ for (ThreadType& t : threads) {
+ t.Start();
+ }
+
+ for (ThreadType& t : threads) {
+ t.Join();
+ }
+}
+
+class QuicConnectionContextTest : public QuicTest {
+ protected:
+};
+
+TEST_F(QuicConnectionContextTest, NullTracerOK) {
+ FakeConnection connection;
+ std::unique_ptr<QuicConnectionTracer> tracer;
+
+ {
+ QuicConnectionContextSwitcher switcher(&connection.context);
+ QUIC_TRACELITERAL("msg 1 recorded");
+ }
+
+ connection.context.tracer.swap(tracer);
+
+ {
+ QuicConnectionContextSwitcher switcher(&connection.context);
+ // Should be a no-op since connection.context.tracer is nullptr.
+ QUIC_TRACELITERAL("msg 2 ignored");
+ }
+
+ EXPECT_THAT(static_cast<TraceCollector*>(tracer.get())->trace(),
+ ElementsAre("msg 1 recorded"));
+}
+
+TEST_F(QuicConnectionContextTest, TestSimpleSwitch) {
+ RunInThreads<SimpleSwitch>(10);
+}
+
+TEST_F(QuicConnectionContextTest, TestNestedSwitch) {
+ RunInThreads<NestedSwitch>(10);
+}
+
+TEST_F(QuicConnectionContextTest, TestAlternatingSwitch) {
+ RunInThreads<AlternatingSwitch>(10);
+}
+
+} // namespace
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc
index fe2ff6cab6e..69666301c59 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc
@@ -23,6 +23,7 @@
#include "quic/core/frames/quic_connection_close_frame.h"
#include "quic/core/frames/quic_new_connection_id_frame.h"
#include "quic/core/frames/quic_path_response_frame.h"
+#include "quic/core/frames/quic_rst_stream_frame.h"
#include "quic/core/quic_connection_id.h"
#include "quic/core/quic_constants.h"
#include "quic/core/quic_error_codes.h"
@@ -33,6 +34,7 @@
#include "quic/core/quic_types.h"
#include "quic/core/quic_utils.h"
#include "quic/core/quic_versions.h"
+#include "quic/platform/api/quic_error_code_wrappers.h"
#include "quic/platform/api/quic_expect_bug.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_ip_address.h"
@@ -1102,12 +1104,9 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> {
MessageStatus SendMessage(absl::string_view message) {
connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
- QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
- return connection_.SendMessage(
- 1,
- MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(), message,
- &storage),
- false);
+ QuicMemSlice slice(QuicBuffer::Copy(
+ connection_.helper()->GetStreamSendBufferAllocator(), message));
+ return connection_.SendMessage(1, absl::MakeSpan(&slice, 1), false);
}
void ProcessAckPacket(uint64_t packet_number, QuicAckFrame* frame) {
@@ -1179,7 +1178,12 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> {
}
if (peer_framer_.version().HasIetfInvariantHeader() &&
peer_framer_.perspective() == Perspective::IS_SERVER) {
- header.destination_connection_id_included = CONNECTION_ID_ABSENT;
+ if (!connection_.client_connection_id().IsEmpty()) {
+ header.destination_connection_id = connection_.client_connection_id();
+ header.destination_connection_id_included = CONNECTION_ID_PRESENT;
+ } else {
+ header.destination_connection_id_included = CONNECTION_ID_ABSENT;
+ }
if (header.version_flag) {
header.source_connection_id = connection_id_;
header.source_connection_id_included = CONNECTION_ID_PRESENT;
@@ -2171,6 +2175,10 @@ TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) {
EXPECT_EQ(IPV6_TO_IPV4_CHANGE,
connection_.active_effective_peer_migration_type());
+ // Make sure anti-amplification limit is not reached.
+ ProcessFramesPacketWithAddresses(
+ {QuicFrame(QuicPingFrame()), QuicFrame(QuicPaddingFrame())}, kSelfAddress,
+ kNewPeerAddress, ENCRYPTION_FORWARD_SECURE);
SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr);
EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
@@ -3754,11 +3762,9 @@ TEST_P(QuicConnectionTest, OnCanWrite) {
TEST_P(QuicConnectionTest, RetransmitOnNack) {
QuicPacketNumber last_packet;
- QuicByteCount second_packet_size;
- SendStreamDataToPeer(3, "foo", 0, NO_FIN, &last_packet); // Packet 1
- second_packet_size =
- SendStreamDataToPeer(3, "foos", 3, NO_FIN, &last_packet); // Packet 2
- SendStreamDataToPeer(3, "fooos", 7, NO_FIN, &last_packet); // Packet 3
+ SendStreamDataToPeer(3, "foo", 0, NO_FIN, &last_packet);
+ SendStreamDataToPeer(3, "foos", 3, NO_FIN, &last_packet);
+ SendStreamDataToPeer(3, "fooos", 7, NO_FIN, &last_packet);
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
@@ -8775,8 +8781,7 @@ TEST_P(QuicConnectionTest, SendMessage) {
connection_.SetFromConfig(config);
}
std::string message(connection_.GetCurrentLargestMessagePayload() * 2, 'a');
- absl::string_view message_data(message);
- QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
+ QuicMemSlice slice;
{
QuicConnection::ScopedPacketFlusher flusher(&connection_);
connection_.SendStreamData3();
@@ -8784,36 +8789,23 @@ TEST_P(QuicConnectionTest, SendMessage) {
// get sent, one contains stream frame, and the other only contains the
// message frame.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ slice = MemSliceFromString(absl::string_view(
+ message.data(), connection_.GetCurrentLargestMessagePayload()));
EXPECT_EQ(MESSAGE_STATUS_SUCCESS,
- connection_.SendMessage(
- 1,
- MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
- absl::string_view(
- message_data.data(),
- connection_.GetCurrentLargestMessagePayload()),
- &storage),
- false));
+ connection_.SendMessage(1, absl::MakeSpan(&slice, 1), false));
}
// Fail to send a message if connection is congestion control blocked.
EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false));
+ slice = MemSliceFromString("message");
EXPECT_EQ(MESSAGE_STATUS_BLOCKED,
- connection_.SendMessage(
- 2,
- MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
- "message", &storage),
- false));
+ connection_.SendMessage(2, absl::MakeSpan(&slice, 1), false));
// Always fail to send a message which cannot fit into one packet.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ slice = MemSliceFromString(absl::string_view(
+ message.data(), connection_.GetCurrentLargestMessagePayload() + 1));
EXPECT_EQ(MESSAGE_STATUS_TOO_LARGE,
- connection_.SendMessage(
- 3,
- MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
- absl::string_view(
- message_data.data(),
- connection_.GetCurrentLargestMessagePayload() + 1),
- &storage),
- false));
+ connection_.SendMessage(3, absl::MakeSpan(&slice, 1), false));
}
TEST_P(QuicConnectionTest, GetCurrentLargestMessagePayload) {
@@ -10545,16 +10537,16 @@ void QuicConnectionTest::TestClientRetryHandling(
}
// These values come from draft-ietf-quic-tls Appendix A.4.
- char retry_packet_rfcv1[] = {
+ uint8_t retry_packet_rfcv1[] = {
0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a,
0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x04, 0xa2, 0x65, 0xba,
0x2e, 0xff, 0x4d, 0x82, 0x90, 0x58, 0xfb, 0x3f, 0x0f, 0x24, 0x96, 0xba};
- char retry_packet29[] = {
+ uint8_t retry_packet29[] = {
0xff, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a,
0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0xd1, 0x69, 0x26, 0xd8,
0x1f, 0x6f, 0x9c, 0xa2, 0x95, 0x3a, 0x8a, 0xa4, 0x57, 0x5e, 0x1e, 0x49};
- char* retry_packet;
+ uint8_t* retry_packet;
size_t retry_packet_length;
if (version() == ParsedQuicVersion::RFCv1()) {
retry_packet = retry_packet_rfcv1;
@@ -10568,19 +10560,21 @@ void QuicConnectionTest::TestClientRetryHandling(
return;
}
- char original_connection_id_bytes[] = {0x83, 0x94, 0xc8, 0xf0,
- 0x3e, 0x51, 0x57, 0x08};
- char new_connection_id_bytes[] = {0xf0, 0x67, 0xa5, 0x50,
- 0x2a, 0x42, 0x62, 0xb5};
- char retry_token_bytes[] = {0x74, 0x6f, 0x6b, 0x65, 0x6e};
+ uint8_t original_connection_id_bytes[] = {0x83, 0x94, 0xc8, 0xf0,
+ 0x3e, 0x51, 0x57, 0x08};
+ uint8_t new_connection_id_bytes[] = {0xf0, 0x67, 0xa5, 0x50,
+ 0x2a, 0x42, 0x62, 0xb5};
+ uint8_t retry_token_bytes[] = {0x74, 0x6f, 0x6b, 0x65, 0x6e};
QuicConnectionId original_connection_id(
- original_connection_id_bytes,
+ reinterpret_cast<char*>(original_connection_id_bytes),
ABSL_ARRAYSIZE(original_connection_id_bytes));
- QuicConnectionId new_connection_id(new_connection_id_bytes,
- ABSL_ARRAYSIZE(new_connection_id_bytes));
+ QuicConnectionId new_connection_id(
+ reinterpret_cast<char*>(new_connection_id_bytes),
+ ABSL_ARRAYSIZE(new_connection_id_bytes));
- std::string retry_token(retry_token_bytes, ABSL_ARRAYSIZE(retry_token_bytes));
+ std::string retry_token(reinterpret_cast<char*>(retry_token_bytes),
+ ABSL_ARRAYSIZE(retry_token_bytes));
if (invalid_retry_tag) {
// Flip the last bit of the retry packet to prevent the integrity tag
@@ -10611,7 +10605,8 @@ void QuicConnectionTest::TestClientRetryHandling(
// Process the RETRY packet.
connection_.ProcessUdpPacket(
kSelfAddress, kPeerAddress,
- QuicReceivedPacket(retry_packet, retry_packet_length, clock_.Now()));
+ QuicReceivedPacket(reinterpret_cast<char*>(retry_packet),
+ retry_packet_length, clock_.Now()));
if (invalid_retry_tag) {
// Make sure we refuse to process a RETRY with invalid tag.
@@ -11572,6 +11567,67 @@ TEST_P(QuicConnectionTest, CoalscingPacketCausesInfiniteLoop) {
connection_.GetRetransmissionAlarm()->Fire();
}
+TEST_P(QuicConnectionTest, ClientAckDelayForAsyncPacketProcessing) {
+ if (!version().HasIetfQuicFrames()) {
+ return;
+ }
+ // SetFromConfig is always called after construction from InitializeSession.
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(AnyNumber());
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ std::make_unique<TaggingEncrypter>(0x01));
+ peer_framer_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(0x01));
+ EXPECT_EQ(0u, QuicConnectionPeer::NumUndecryptablePackets(&connection_));
+
+ // Received undecryptable HANDSHAKE 2.
+ ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_HANDSHAKE);
+ ASSERT_EQ(1u, QuicConnectionPeer::NumUndecryptablePackets(&connection_));
+ // Received INITIAL 4 (which is retransmission of INITIAL 1) after 100ms.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ ProcessDataPacketAtLevel(4, !kHasStopWaiting, ENCRYPTION_INITIAL);
+ // Generate HANDSHAKE key.
+ SetDecrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<StrictTaggingDecrypter>(0x01));
+ EXPECT_TRUE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet());
+ connection_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
+ // Verify HANDSHAKE packet gets processed.
+ connection_.GetProcessUndecryptablePacketsAlarm()->Fire();
+ ASSERT_TRUE(connection_.HasPendingAcks());
+ // Send ACKs.
+ clock_.AdvanceTime(connection_.GetAckAlarm()->deadline() - clock_.Now());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ connection_.GetAckAlarm()->Fire();
+ ASSERT_FALSE(writer_->ack_frames().empty());
+ // Verify the ack_delay_time in the INITIAL ACK frame is 1ms.
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1),
+ writer_->ack_frames()[0].ack_delay_time);
+ // Process the coalesced HANDSHAKE packet.
+ ASSERT_TRUE(writer_->coalesced_packet() != nullptr);
+ auto packet = writer_->coalesced_packet()->Clone();
+ writer_->framer()->ProcessPacket(*packet);
+ ASSERT_FALSE(writer_->ack_frames().empty());
+ if (GetQuicReloadableFlag(
+ quic_reset_per_packet_state_for_undecryptable_packets)) {
+ // Verify the ack_delay_time in the HANDSHAKE ACK frame includes the
+ // buffering time.
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(101),
+ writer_->ack_frames()[0].ack_delay_time);
+ } else {
+ // This ack_delay_time is wrong.
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1),
+ writer_->ack_frames()[0].ack_delay_time);
+ }
+ ASSERT_TRUE(writer_->coalesced_packet() == nullptr);
+}
+
TEST_P(QuicConnectionTest, TestingLiveness) {
const size_t kMinRttMs = 40;
RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
@@ -12017,7 +12073,7 @@ TEST_P(QuicConnectionTest, PathValidationReceivesStatelessReset) {
// writer.
TEST_P(QuicConnectionTest, SendPathChallengeUsingBlockedNewSocket) {
if (!VersionHasIetfQuicFrames(connection_.version().transport_version) ||
- !connection_.use_path_validator()) {
+ !connection_.connection_migration_use_new_cid()) {
return;
}
PathProbeTestInit(Perspective::IS_CLIENT);
@@ -12027,6 +12083,7 @@ TEST_P(QuicConnectionTest, SendPathChallengeUsingBlockedNewSocket) {
new_writer.BlockOnNextWrite();
EXPECT_CALL(visitor_, OnWriteBlocked()).Times(0);
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AtLeast(1))
.WillOnce(Invoke([&]() {
// Even though the socket is blocked, the PATH_CHALLENGE should still be
// treated as sent.
@@ -12047,7 +12104,13 @@ TEST_P(QuicConnectionTest, SendPathChallengeUsingBlockedNewSocket) {
new_writer.SetWritable();
// Write event on the default socket shouldn't make any difference.
connection_.OnCanWrite();
- EXPECT_EQ(0u, writer_->packets_write_attempts());
+ if (GetQuicReloadableFlag(quic_ack_cid_frames)) {
+ // A NEW_CONNECTION_ID frame is received in PathProbeTestInit and OnCanWrite
+ // will write a acking packet.
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ } else {
+ EXPECT_EQ(0u, writer_->packets_write_attempts());
+ }
EXPECT_EQ(1u, new_writer.packets_write_attempts());
}
@@ -12232,9 +12295,12 @@ TEST_P(QuicConnectionTest,
return;
}
PathProbeTestInit(Perspective::IS_CLIENT);
+ // Make sure there is no outstanding ACK_FRAME to write.
+ connection_.OnCanWrite();
+ uint32_t num_packets_write_attempts = writer_->packets_write_attempts();
writer_->SetShouldWriteFail();
- writer_->SetWriteError(EMSGSIZE);
+ writer_->SetWriteError(QUIC_EMSGSIZE);
const QuicSocketAddress kNewPeerAddress(QuicIpAddress::Any4(), 12345);
EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
.Times(0u);
@@ -12250,7 +12316,7 @@ TEST_P(QuicConnectionTest,
EXPECT_TRUE(connection_.HasPendingPathValidation());
// Connection shouldn't be closed.
EXPECT_TRUE(connection_.connected());
- EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_EQ(++num_packets_write_attempts, writer_->packets_write_attempts());
EXPECT_EQ(1u, writer_->path_challenge_frames().size());
EXPECT_EQ(1u, writer_->padding_frames().size());
EXPECT_EQ(kNewPeerAddress, writer_->last_write_peer_address());
@@ -12656,10 +12722,6 @@ TEST_P(QuicConnectionTest, ZeroRttRejectionAndMissingInitialKeys) {
connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
std::make_unique<TaggingEncrypter>(0x04));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
- if (!GetQuicReloadableFlag(quic_donot_write_mid_packet_processing)) {
- // Retransmit rejected 0-RTT packets.
- connection_.OnCanWrite();
- }
// Advance INITIAL ack delay to trigger initial ACK to be sent AFTER
// the retransmission of rejected 0-RTT packets while the HANDSHAKE
// packet is still in the coalescer, such that the INITIAL key gets
@@ -13576,19 +13638,33 @@ TEST_P(QuicConnectionTest, ServerHelloGetsReordered) {
TEST_P(QuicConnectionTest, MigratePath) {
EXPECT_CALL(visitor_, GetHandshakeState())
- .Times(testing::AtMost(1))
- .WillOnce(Return(HANDSHAKE_CONFIRMED));
+ .Times(testing::AtMost(2))
+ .WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
EXPECT_CALL(visitor_, OnPathDegrading());
connection_.OnPathDegradingDetected();
const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Any4(), 12345);
EXPECT_NE(kNewSelfAddress, connection_.self_address());
+
+ // Buffer a packet.
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1);
+ writer_->SetWriteBlocked();
+ connection_.SendMtuDiscoveryPacket(kMaxOutgoingPacketSize);
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
EXPECT_CALL(visitor_, OnForwardProgressMadeAfterPathDegrading());
connection_.MigratePath(kNewSelfAddress, connection_.peer_address(),
&new_writer, /*owns_writer=*/false);
+
EXPECT_EQ(kNewSelfAddress, connection_.self_address());
EXPECT_EQ(&new_writer, QuicConnectionPeer::GetWriter(&connection_));
EXPECT_FALSE(connection_.IsPathDegrading());
+ // Buffered packet on the old path should be discarded.
+ if (connection_.connection_migration_use_new_cid()) {
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ } else {
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+ }
}
TEST_P(QuicConnectionTest, MigrateToNewPathDuringProbing) {
@@ -14453,9 +14529,11 @@ TEST_P(QuicConnectionTest,
// would fail due to lack of client connection ID.
const QuicSocketAddress kSelfAddress2(QuicIpAddress::Loopback4(),
/*port=*/45678);
- ASSERT_FALSE(connection_.MigratePath(kSelfAddress2,
- connection_.peer_address(), &new_writer,
- /*owns_writer=*/false));
+ auto new_writer2 = std::make_unique<TestPacketWriter>(version(), &clock_,
+ Perspective::IS_CLIENT);
+ ASSERT_FALSE(connection_.MigratePath(
+ kSelfAddress2, connection_.peer_address(), new_writer2.release(),
+ /*owns_writer=*/true));
}
TEST_P(QuicConnectionTest,
@@ -14587,7 +14665,6 @@ TEST_P(
QuicConnectionTest,
ReplacePeerIssuedConnectionIdOnBothPathsTriggeredByNewConnectionIdFrame) {
if (!version().HasIetfQuicFrames() || !connection_.use_path_validator() ||
- !connection_.use_connection_id_on_default_path() ||
!connection_.count_bytes_on_alternative_path_separately()) {
return;
}
@@ -14647,7 +14724,7 @@ TEST_P(
TEST_P(QuicConnectionTest,
CloseConnectionAfterReceiveRetireConnectionIdWhenNoCIDIssued) {
if (!version().HasIetfQuicFrames() ||
- GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2)) {
+ !connection_.connection_migration_use_new_cid()) {
return;
}
QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_);
@@ -14668,7 +14745,7 @@ TEST_P(QuicConnectionTest,
TEST_P(QuicConnectionTest, RetireConnectionIdFrameResultsInError) {
if (!version().HasIetfQuicFrames() ||
- GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2)) {
+ !connection_.connection_migration_use_new_cid()) {
return;
}
QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_);
@@ -14708,24 +14785,16 @@ TEST_P(QuicConnectionTest,
QuicConnectionId cid0 = connection_id_;
QuicRetireConnectionIdFrame frame;
frame.sequence_number = 0u;
- if (!GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2) ||
- connection_.connection_migration_use_new_cid()) {
+ if (connection_.connection_migration_use_new_cid()) {
EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)).Times(2);
EXPECT_CALL(visitor_, SendNewConnectionId(_)).Times(2);
}
EXPECT_TRUE(connection_.OnRetireConnectionIdFrame(frame));
- if (!GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2)) {
- ASSERT_TRUE(retire_self_issued_cid_alarm->IsSet());
- // cid0 is retired when the retire CID alarm fires.
- if (!GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2))
- EXPECT_CALL(visitor_, OnServerConnectionIdRetired(cid0));
- retire_self_issued_cid_alarm->Fire();
- }
}
TEST_P(QuicConnectionTest, ServerRetireSelfIssuedConnectionId) {
if (!version().HasIetfQuicFrames() ||
- GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2)) {
+ !connection_.connection_migration_use_new_cid()) {
return;
}
QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_);
@@ -14770,8 +14839,7 @@ TEST_P(QuicConnectionTest, ServerRetireSelfIssuedConnectionId) {
TEST_P(QuicConnectionTest, PatchMissingClientConnectionIdOntoAlternativePath) {
if (!version().HasIetfQuicFrames() ||
- !connection_.support_multiple_connection_ids() ||
- !connection_.use_connection_id_on_default_path()) {
+ !connection_.support_multiple_connection_ids()) {
return;
}
set_perspective(Perspective::IS_SERVER);
@@ -14807,8 +14875,7 @@ TEST_P(QuicConnectionTest, PatchMissingClientConnectionIdOntoAlternativePath) {
TEST_P(QuicConnectionTest, PatchMissingClientConnectionIdOntoDefaultPath) {
if (!version().HasIetfQuicFrames() ||
- !connection_.support_multiple_connection_ids() ||
- !connection_.use_connection_id_on_default_path()) {
+ !connection_.support_multiple_connection_ids()) {
return;
}
set_perspective(Perspective::IS_SERVER);
@@ -14923,23 +14990,13 @@ TEST_P(QuicConnectionTest, LostDataThenGetAcknowledged) {
InvokeWithoutArgs(&notifier_, &SimpleSessionNotifier::OnCanWrite));
QuicIpAddress ip_address;
ASSERT_TRUE(ip_address.FromString("127.0.52.223"));
- if (GetQuicReloadableFlag(quic_donot_write_mid_packet_processing)) {
- EXPECT_QUIC_BUG(
- ProcessFramesPacketWithAddresses(frames, kSelfAddress,
- QuicSocketAddress(ip_address, 1000),
- ENCRYPTION_FORWARD_SECURE),
- "Try to write mid packet processing");
- EXPECT_EQ(1u, writer_->path_challenge_frames().size());
- // Verify stream frame will not be retransmitted.
- EXPECT_TRUE(writer_->stream_frames().empty());
- } else {
- ProcessFramesPacketWithAddresses(frames, kSelfAddress,
- QuicSocketAddress(ip_address, 1000),
- ENCRYPTION_FORWARD_SECURE);
- // In prod, this would cause FAILED_TO_SERIALIZE_PACKET since the stream
- // data has been freed, but simple_data_producer does not free data.
- EXPECT_EQ(1u, writer_->stream_frames().size());
- }
+ EXPECT_QUIC_BUG(ProcessFramesPacketWithAddresses(
+ frames, kSelfAddress, QuicSocketAddress(ip_address, 1000),
+ ENCRYPTION_FORWARD_SECURE),
+ "Try to write mid packet processing");
+ EXPECT_EQ(1u, writer_->path_challenge_frames().size());
+ // Verify stream frame will not be retransmitted.
+ EXPECT_TRUE(writer_->stream_frames().empty());
}
TEST_P(QuicConnectionTest, PtoSendStreamData) {
@@ -14980,12 +15037,320 @@ TEST_P(QuicConnectionTest, PtoSendStreamData) {
ASSERT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
connection_.GetRetransmissionAlarm()->Fire();
- if (GetQuicReloadableFlag(quic_donot_pto_half_rtt_data)) {
- // Verify INITIAL and HANDSHAKE get retransmitted.
- EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+ // Verify INITIAL and HANDSHAKE get retransmitted.
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+}
+
+TEST_P(QuicConnectionTest, SendingZeroRttPacketsDoesNotPostponePTO) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ std::make_unique<TaggingEncrypter>(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ // Send CHLO.
+ connection_.SendCryptoStreamData();
+ ASSERT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ // Install 0-RTT keys.
+ connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ std::make_unique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+
+ // CHLO gets acknowledged after 10ms.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ QuicAckFrame frame1 = InitAckFrame(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _));
+ ProcessFramePacketAtLevel(1, QuicFrame(&frame1), ENCRYPTION_INITIAL);
+ // Verify PTO is still armed since address validation is not finished yet.
+ ASSERT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ QuicTime pto_deadline = connection_.GetRetransmissionAlarm()->deadline();
+
+ // Send 0-RTT packet.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ std::make_unique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ connection_.SendStreamDataWithString(2, "foo", 0, NO_FIN);
+ ASSERT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ if (GetQuicReloadableFlag(
+ quic_donot_rearm_pto_on_application_data_during_handshake)) {
+ // PTO deadline should be unchanged.
+ EXPECT_EQ(pto_deadline, connection_.GetRetransmissionAlarm()->deadline());
} else {
- // Application data preempts handshake data when PTO fires.
- EXPECT_EQ(0x03030303u, writer_->final_bytes_of_last_packet());
+ // PTO gets re-armed.
+ EXPECT_NE(pto_deadline, connection_.GetRetransmissionAlarm()->deadline());
+ }
+}
+
+TEST_P(QuicConnectionTest, QueueingUndecryptablePacketsDoesntPostponePTO) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ config.set_max_undecryptable_packets(3);
+ connection_.SetFromConfig(config);
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ std::make_unique<TaggingEncrypter>(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ connection_.RemoveDecrypter(ENCRYPTION_FORWARD_SECURE);
+ // Send CHLO.
+ connection_.SendCryptoStreamData();
+
+ // Send 0-RTT packet.
+ connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ std::make_unique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ connection_.SendStreamDataWithString(2, "foo", 0, NO_FIN);
+
+ // CHLO gets acknowledged after 10ms.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ QuicAckFrame frame1 = InitAckFrame(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _));
+ ProcessFramePacketAtLevel(1, QuicFrame(&frame1), ENCRYPTION_INITIAL);
+ // Verify PTO is still armed since address validation is not finished yet.
+ ASSERT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ QuicTime pto_deadline = connection_.GetRetransmissionAlarm()->deadline();
+
+ // Receive an undecryptable packets.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(0xFF));
+ ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE);
+ // Verify PTO deadline is sooner.
+ EXPECT_GT(pto_deadline, connection_.GetRetransmissionAlarm()->deadline());
+ pto_deadline = connection_.GetRetransmissionAlarm()->deadline();
+
+ // PTO fires.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ clock_.AdvanceTime(pto_deadline - clock_.ApproximateNow());
+ connection_.GetRetransmissionAlarm()->Fire();
+ // Verify PTO is still armed since address validation is not finished yet.
+ ASSERT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ pto_deadline = connection_.GetRetransmissionAlarm()->deadline();
+
+ // Verify PTO deadline does not change.
+ ProcessDataPacketAtLevel(4, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE);
+ EXPECT_EQ(pto_deadline, connection_.GetRetransmissionAlarm()->deadline());
+}
+
+TEST_P(QuicConnectionTest, QueueUndecryptableHandshakePackets) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ config.set_max_undecryptable_packets(3);
+ connection_.SetFromConfig(config);
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ std::make_unique<TaggingEncrypter>(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ connection_.RemoveDecrypter(ENCRYPTION_HANDSHAKE);
+ // Send CHLO.
+ connection_.SendCryptoStreamData();
+
+ // Send 0-RTT packet.
+ connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ std::make_unique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ connection_.SendStreamDataWithString(2, "foo", 0, NO_FIN);
+ EXPECT_EQ(0u, QuicConnectionPeer::NumUndecryptablePackets(&connection_));
+
+ // Receive an undecryptable handshake packet.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ peer_framer_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(0xFF));
+ ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_HANDSHAKE);
+ // Verify this handshake packet gets queued.
+ EXPECT_EQ(1u, QuicConnectionPeer::NumUndecryptablePackets(&connection_));
+}
+
+TEST_P(QuicConnectionTest, PingNotSentAt0RTTLevelWhenInitialAvailable) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ std::make_unique<TaggingEncrypter>(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ // Send CHLO.
+ connection_.SendCryptoStreamData();
+ // Send 0-RTT packet.
+ connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ std::make_unique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ connection_.SendStreamDataWithString(2, "foo", 0, NO_FIN);
+
+ // CHLO gets acknowledged after 10ms.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ QuicAckFrame frame1 = InitAckFrame(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _));
+ ProcessFramePacketAtLevel(1, QuicFrame(&frame1), ENCRYPTION_INITIAL);
+ // Verify PTO is still armed since address validation is not finished yet.
+ ASSERT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ QuicTime pto_deadline = connection_.GetRetransmissionAlarm()->deadline();
+
+ // PTO fires.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ clock_.AdvanceTime(pto_deadline - clock_.ApproximateNow());
+ connection_.GetRetransmissionAlarm()->Fire();
+ // Verify the PING gets sent in ENCRYPTION_INITIAL.
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
+}
+
+TEST_P(QuicConnectionTest, AckElicitingFrames) {
+ QuicConfig config;
+ config.SetConnectionOptionsToSend({kRVCM});
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ if (!version().HasIetfQuicFrames() ||
+ !connection_.connection_migration_use_new_cid() ||
+ !GetQuicReloadableFlag(quic_ack_cid_frames) ||
+ !GetQuicReloadableFlag(quic_add_missing_update_ack_timeout)) {
+ return;
+ }
+ EXPECT_CALL(visitor_, SendNewConnectionId(_)).Times(2);
+ EXPECT_CALL(visitor_, OnRstStream(_));
+ EXPECT_CALL(visitor_, OnWindowUpdateFrame(_));
+ EXPECT_CALL(visitor_, OnBlockedFrame(_));
+ EXPECT_CALL(visitor_, OnHandshakeDoneReceived());
+ EXPECT_CALL(visitor_, OnStreamFrame(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _));
+ EXPECT_CALL(visitor_, OnMaxStreamsFrame(_));
+ EXPECT_CALL(visitor_, OnStreamsBlockedFrame(_));
+ EXPECT_CALL(visitor_, OnStopSendingFrame(_));
+ EXPECT_CALL(visitor_, OnMessageReceived(""));
+ EXPECT_CALL(visitor_, OnNewTokenReceived(""));
+
+ SetClientConnectionId(TestConnectionId(12));
+ connection_.CreateConnectionIdManager();
+ QuicConnectionPeer::GetSelfIssuedConnectionIdManager(&connection_)
+ ->MaybeSendNewConnectionIds();
+ connection_.set_can_receive_ack_frequency_frame();
+
+ QuicAckFrame ack_frame = InitAckFrame(1);
+ QuicRstStreamFrame rst_stream_frame;
+ QuicWindowUpdateFrame window_update_frame;
+ QuicPathChallengeFrame path_challenge_frame;
+ QuicNewConnectionIdFrame new_connection_id_frame;
+ QuicRetireConnectionIdFrame retire_connection_id_frame;
+ retire_connection_id_frame.sequence_number = 1u;
+ QuicStopSendingFrame stop_sending_frame;
+ QuicPathResponseFrame path_response_frame;
+ QuicMessageFrame message_frame;
+ QuicNewTokenFrame new_token_frame;
+ QuicAckFrequencyFrame ack_frequency_frame;
+ QuicBlockedFrame blocked_frame;
+ size_t packet_number = 1;
+
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+
+ for (uint8_t i = 0; i < NUM_FRAME_TYPES; ++i) {
+ QuicFrameType frame_type = static_cast<QuicFrameType>(i);
+ bool skipped = false;
+ QuicFrame frame;
+ QuicFrames frames;
+ // Add some padding to fullfill the min size requirement of header
+ // protection.
+ frames.push_back(QuicFrame(QuicPaddingFrame(10)));
+ switch (frame_type) {
+ case PADDING_FRAME:
+ frame = QuicFrame(QuicPaddingFrame(10));
+ break;
+ case MTU_DISCOVERY_FRAME:
+ frame = QuicFrame(QuicMtuDiscoveryFrame());
+ break;
+ case PING_FRAME:
+ frame = QuicFrame(QuicPingFrame());
+ break;
+ case MAX_STREAMS_FRAME:
+ frame = QuicFrame(QuicMaxStreamsFrame());
+ break;
+ case STOP_WAITING_FRAME:
+ // Not supported.
+ skipped = true;
+ break;
+ case STREAMS_BLOCKED_FRAME:
+ frame = QuicFrame(QuicStreamsBlockedFrame());
+ break;
+ case STREAM_FRAME:
+ frame = QuicFrame(QuicStreamFrame());
+ break;
+ case HANDSHAKE_DONE_FRAME:
+ frame = QuicFrame(QuicHandshakeDoneFrame());
+ break;
+ case ACK_FRAME:
+ frame = QuicFrame(&ack_frame);
+ break;
+ case RST_STREAM_FRAME:
+ frame = QuicFrame(&rst_stream_frame);
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ // Do not test connection close.
+ skipped = true;
+ break;
+ case GOAWAY_FRAME:
+ // Does not exist in IETF QUIC.
+ skipped = true;
+ break;
+ case BLOCKED_FRAME:
+ frame = QuicFrame(&blocked_frame);
+ break;
+ case WINDOW_UPDATE_FRAME:
+ frame = QuicFrame(&window_update_frame);
+ break;
+ case PATH_CHALLENGE_FRAME:
+ frame = QuicFrame(&path_challenge_frame);
+ break;
+ case STOP_SENDING_FRAME:
+ frame = QuicFrame(&stop_sending_frame);
+ break;
+ case NEW_CONNECTION_ID_FRAME:
+ frame = QuicFrame(&new_connection_id_frame);
+ break;
+ case RETIRE_CONNECTION_ID_FRAME:
+ frame = QuicFrame(&retire_connection_id_frame);
+ break;
+ case PATH_RESPONSE_FRAME:
+ frame = QuicFrame(&path_response_frame);
+ break;
+ case MESSAGE_FRAME:
+ frame = QuicFrame(&message_frame);
+ break;
+ case CRYPTO_FRAME:
+ // CRYPTO_FRAME is ack eliciting is covered by other tests.
+ skipped = true;
+ break;
+ case NEW_TOKEN_FRAME:
+ frame = QuicFrame(&new_token_frame);
+ break;
+ case ACK_FREQUENCY_FRAME:
+ frame = QuicFrame(&ack_frequency_frame);
+ break;
+ case NUM_FRAME_TYPES:
+ skipped = true;
+ break;
+ }
+ if (skipped) {
+ continue;
+ }
+ ASSERT_EQ(frame_type, frame.type);
+ frames.push_back(frame);
+ EXPECT_FALSE(connection_.HasPendingAcks());
+ // Process frame.
+ ProcessFramesPacketAtLevel(packet_number++, frames,
+ ENCRYPTION_FORWARD_SECURE);
+ if (QuicUtils::IsAckElicitingFrame(frame_type)) {
+ ASSERT_TRUE(connection_.HasPendingAcks()) << frame;
+ // Flush ACK.
+ clock_.AdvanceTime(DefaultDelayedAckTime());
+ connection_.GetAckAlarm()->Fire();
+ }
+ EXPECT_FALSE(connection_.HasPendingAcks());
+ ASSERT_TRUE(connection_.connected());
}
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_constants.h b/chromium/net/third_party/quiche/src/quic/core/quic_constants.h
index b750abb3a4f..162de8909bd 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_constants.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_constants.h
@@ -295,10 +295,10 @@ QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd;
QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd0;
// HTTP/3 Datagrams.
-enum : QuicDatagramFlowId {
- kFirstDatagramFlowIdClient = 0,
- kFirstDatagramFlowIdServer = 1,
- kDatagramFlowIdIncrement = 2,
+enum : QuicDatagramContextId {
+ kFirstDatagramContextIdClient = 0,
+ kFirstDatagramContextIdServer = 1,
+ kDatagramContextIdIncrement = 2,
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc
index f41a3560009..42862494745 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc
@@ -17,7 +17,6 @@
#include "quic/core/quic_utils.h"
#include "quic/platform/api/quic_bug_tracker.h"
#include "quic/platform/api/quic_flag_utils.h"
-#include "quic/platform/api/quic_map_util.h"
namespace quic {
@@ -167,14 +166,14 @@ void QuicControlFrameManager::OnControlFrameSent(const QuicFrame& frame) {
}
if (frame.type == WINDOW_UPDATE_FRAME) {
QuicStreamId stream_id = frame.window_update_frame->stream_id;
- if (QuicContainsKey(window_update_frames_, stream_id) &&
+ if (window_update_frames_.contains(stream_id) &&
id > window_update_frames_[stream_id]) {
// Consider the older window update of the same stream as acked.
OnControlFrameIdAcked(window_update_frames_[stream_id]);
}
window_update_frames_[stream_id] = id;
}
- if (QuicContainsKey(pending_retransmissions_, id)) {
+ if (pending_retransmissions_.contains(id)) {
// This is retransmitted control frame.
pending_retransmissions_.erase(id);
return;
@@ -197,7 +196,7 @@ bool QuicControlFrameManager::OnControlFrameAcked(const QuicFrame& frame) {
}
if (frame.type == WINDOW_UPDATE_FRAME) {
QuicStreamId stream_id = frame.window_update_frame->stream_id;
- if (QuicContainsKey(window_update_frames_, stream_id) &&
+ if (window_update_frames_.contains(stream_id) &&
window_update_frames_[stream_id] == id) {
window_update_frames_.erase(stream_id);
}
@@ -223,7 +222,7 @@ void QuicControlFrameManager::OnControlFrameLost(const QuicFrame& frame) {
// This frame has already been acked.
return;
}
- if (!QuicContainsKey(pending_retransmissions_, id)) {
+ if (!pending_retransmissions_.contains(id)) {
pending_retransmissions_[id] = true;
QUIC_BUG_IF(quic_bug_12727_2,
pending_retransmissions_.size() > control_frames_.size())
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h
index 48d228bff31..962733a147d 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h
@@ -13,6 +13,7 @@
#include "quic/core/quic_connection_id.h"
#include "quic/core/quic_types.h"
#include "common/quiche_circular_deque.h"
+#include "common/quiche_linked_hash_map.h"
namespace quic {
@@ -178,7 +179,8 @@ class QUIC_EXPORT_PRIVATE QuicControlFrameManager {
// TODO(fayang): switch to linked_hash_set when chromium supports it. The bool
// is not used here.
// Lost control frames waiting to be retransmitted.
- QuicLinkedHashMap<QuicControlFrameId, bool> pending_retransmissions_;
+ quiche::QuicheLinkedHashMap<QuicControlFrameId, bool>
+ pending_retransmissions_;
DelegateInterface* delegate_;
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc
index 660663a9acd..ed5da6cb544 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc
@@ -105,6 +105,11 @@ class DummyProofSource : public ProofSource {
callback->Run(true, "Dummy signature", /*details=*/nullptr);
}
+ absl::InlinedVector<uint16_t, 8> SupportedTlsSignatureAlgorithms()
+ const override {
+ return {};
+ }
+
TicketCrypter* GetTicketCrypter() override { return nullptr; }
};
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc
index 25bf9dcdf15..79142c4681e 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc
@@ -27,10 +27,7 @@ void QuicCryptoHandshaker::SendHandshakeMessage(
session()->OnCryptoHandshakeMessageSent(message);
last_sent_handshake_message_tag_ = message.tag();
const QuicData& data = message.GetSerialized();
- stream_->WriteCryptoData(session_->use_write_or_buffer_data_at_level()
- ? level
- : session_->connection()->encryption_level(),
- data.AsStringPiece());
+ stream_->WriteCryptoData(level, data.AsStringPiece());
}
void QuicCryptoHandshaker::OnError(CryptoFramer* framer) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc
index 1dd68995e2f..9bd9c7bc2b9 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc
@@ -287,15 +287,8 @@ void QuicCryptoServerStream::FinishSendServerConfigUpdate(
QUIC_DVLOG(1) << "Server: Sending server config update: "
<< message.DebugString();
- if (!session()->use_write_or_buffer_data_at_level() &&
- !QuicVersionUsesCryptoFrames(transport_version())) {
- const QuicData& data = message.GetSerialized();
- WriteOrBufferData(absl::string_view(data.data(), data.length()), false,
- nullptr);
- } else {
- // Send server config update in ENCRYPTION_FORWARD_SECURE.
- SendHandshakeMessage(message, ENCRYPTION_FORWARD_SECURE);
- }
+ // Send server config update in ENCRYPTION_FORWARD_SECURE.
+ SendHandshakeMessage(message, ENCRYPTION_FORWARD_SECURE);
++num_server_config_update_messages_sent_;
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc
index f8f34dfb22c..a14cef07f27 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc
@@ -144,15 +144,8 @@ bool QuicCryptoStream::ExportKeyingMaterial(absl::string_view label,
void QuicCryptoStream::WriteCryptoData(EncryptionLevel level,
absl::string_view data) {
if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
- if (session()->use_write_or_buffer_data_at_level()) {
- WriteOrBufferDataAtLevel(data, /*fin=*/false, level,
- /*ack_listener=*/nullptr);
- return;
- }
- // The QUIC crypto handshake takes care of setting the appropriate
- // encryption level before writing data. Since that is the only handshake
- // supported in versions less than 47, |level| can be ignored here.
- WriteOrBufferData(data, /* fin */ false, /* ack_listener */ nullptr);
+ WriteOrBufferDataAtLevel(data, /*fin=*/false, level,
+ /*ack_listener=*/nullptr);
return;
}
if (data.empty()) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.cc b/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.cc
index d4513e48f69..e6f3e6f427a 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.cc
@@ -4,6 +4,7 @@
#include "quic/core/quic_datagram_queue.h"
+#include "absl/types/span.h"
#include "quic/core/quic_constants.h"
#include "quic/core/quic_session.h"
#include "quic/core/quic_time.h"
@@ -30,8 +31,7 @@ MessageStatus QuicDatagramQueue::SendOrQueueDatagram(QuicMemSlice datagram) {
// the datagrams are sent in the same order that they were sent by the
// application.
if (queue_.empty()) {
- QuicMemSliceSpan span(&datagram);
- MessageResult result = session_->SendMessage(span);
+ MessageResult result = session_->SendMessage(absl::MakeSpan(&datagram, 1));
if (result.status != MESSAGE_STATUS_BLOCKED) {
if (observer_) {
observer_->OnDatagramProcessed(result.status);
@@ -51,8 +51,8 @@ absl::optional<MessageStatus> QuicDatagramQueue::TrySendingNextDatagram() {
return absl::nullopt;
}
- QuicMemSliceSpan span(&queue_.front().datagram);
- MessageResult result = session_->SendMessage(span);
+ MessageResult result =
+ session_->SendMessage(absl::MakeSpan(&queue_.front().datagram, 1));
if (result.status != MESSAGE_STATUS_BLOCKED) {
queue_.pop_front();
if (observer_) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue_test.cc
index f47bbb883cb..68a876b4cd0 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue_test.cc
@@ -176,8 +176,9 @@ TEST_F(QuicDatagramQueueTest, Expiry) {
std::vector<std::string> messages;
EXPECT_CALL(*connection_, SendMessage(_, _, _))
.WillRepeatedly([&messages](QuicMessageId /*id*/,
- QuicMemSliceSpan message, bool /*flush*/) {
- messages.push_back(std::string(message.GetData(0)));
+ absl::Span<QuicMemSlice> message,
+ bool /*flush*/) {
+ messages.push_back(std::string(message[0].AsStringView()));
return MESSAGE_STATUS_SUCCESS;
});
EXPECT_EQ(2u, queue_.SendDatagrams());
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc
index e1a999c706a..11f4bcd4b3d 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc
@@ -205,6 +205,10 @@ class ChloAlpnSniExtractor : public ChloExtractor::Delegate {
if (chlo.GetStringPiece(quic::kSNI, &sni)) {
sni_ = std::string(sni);
}
+ absl::string_view uaid_value;
+ if (chlo.GetStringPiece(quic::kUAID, &uaid_value)) {
+ uaid_ = std::string(uaid_value);
+ }
if (version == LegacyVersionForEncapsulation().transport_version) {
absl::string_view qlve_value;
if (chlo.GetStringPiece(kQLVE, &qlve_value)) {
@@ -217,6 +221,8 @@ class ChloAlpnSniExtractor : public ChloExtractor::Delegate {
std::string&& ConsumeSni() { return std::move(sni_); }
+ std::string&& ConsumeUaid() { return std::move(uaid_); }
+
std::string&& ConsumeLegacyVersionEncapsulationInnerPacket() {
return std::move(legacy_version_encapsulation_inner_packet_);
}
@@ -224,15 +230,14 @@ class ChloAlpnSniExtractor : public ChloExtractor::Delegate {
private:
std::string alpn_;
std::string sni_;
+ std::string uaid_;
std::string legacy_version_encapsulation_inner_packet_;
};
bool MaybeHandleLegacyVersionEncapsulation(
QuicDispatcher* dispatcher,
- ChloAlpnSniExtractor* alpn_extractor,
+ std::string legacy_version_encapsulation_inner_packet,
const ReceivedPacketInfo& packet_info) {
- std::string legacy_version_encapsulation_inner_packet =
- alpn_extractor->ConsumeLegacyVersionEncapsulationInnerPacket();
if (legacy_version_encapsulation_inner_packet.empty()) {
// This CHLO did not contain the Legacy Version Encapsulation tag.
return false;
@@ -335,9 +340,14 @@ QuicDispatcher::QuicDispatcher(
<< "Trying to create dispatcher without any supported versions";
QUIC_DLOG(INFO) << "Created QuicDispatcher with versions: "
<< ParsedQuicVersionVectorToString(GetSupportedVersions());
+ QUIC_RESTART_FLAG_COUNT(quic_alarm_add_permanent_cancel);
}
QuicDispatcher::~QuicDispatcher() {
+ if (GetQuicRestartFlag(quic_alarm_add_permanent_cancel) &&
+ delete_sessions_alarm_ != nullptr) {
+ delete_sessions_alarm_->PermanentCancel();
+ }
reference_counted_session_map_.clear();
closed_ref_counted_session_list_.clear();
if (support_multiple_cid_per_connection_) {
@@ -532,8 +542,10 @@ bool QuicDispatcher::MaybeDispatchPacket(
config_->create_session_tag_indicators(),
&alpn_extractor,
server_connection_id.length())) {
- if (MaybeHandleLegacyVersionEncapsulation(this, &alpn_extractor,
- packet_info)) {
+ if (MaybeHandleLegacyVersionEncapsulation(
+ this,
+ alpn_extractor.ConsumeLegacyVersionEncapsulationInnerPacket(),
+ packet_info)) {
return true;
}
}
@@ -647,76 +659,38 @@ void QuicDispatcher::ProcessHeader(ReceivedPacketInfo* packet_info) {
packet_info->destination_connection_id;
// Packet's connection ID is unknown. Apply the validity checks.
QuicPacketFate fate = ValidityChecks(*packet_info);
- ChloAlpnSniExtractor alpn_extractor;
- switch (fate) {
- case kFateProcess: {
- if (packet_info->version.handshake_protocol == PROTOCOL_TLS1_3) {
- bool has_full_tls_chlo = false;
- std::string sni;
- std::vector<std::string> alpns;
- if (buffered_packets_.HasBufferedPackets(
- packet_info->destination_connection_id)) {
- // If we already have buffered packets for this connection ID,
- // use the associated TlsChloExtractor to parse this packet.
- has_full_tls_chlo =
- buffered_packets_.IngestPacketForTlsChloExtraction(
- packet_info->destination_connection_id, packet_info->version,
- packet_info->packet, &alpns, &sni);
- } else {
- // If we do not have a BufferedPacketList for this connection ID,
- // create a single-use one to check whether this packet contains a
- // full single-packet CHLO.
- TlsChloExtractor tls_chlo_extractor;
- tls_chlo_extractor.IngestPacket(packet_info->version,
- packet_info->packet);
- if (tls_chlo_extractor.HasParsedFullChlo()) {
- // This packet contains a full single-packet CHLO.
- has_full_tls_chlo = true;
- alpns = tls_chlo_extractor.alpns();
- sni = tls_chlo_extractor.server_name();
- }
- }
- if (has_full_tls_chlo) {
- ProcessChlo(alpns, sni, packet_info);
- } else {
- // This packet does not contain a full CHLO. It could be a 0-RTT
- // packet that arrived before the CHLO (due to loss or reordering),
- // or it could be a fragment of a multi-packet CHLO.
- BufferEarlyPacket(*packet_info);
- }
- break;
- }
- if (GetQuicFlag(FLAGS_quic_allow_chlo_buffering) &&
- !ChloExtractor::Extract(packet_info->packet, packet_info->version,
- config_->create_session_tag_indicators(),
- &alpn_extractor,
- server_connection_id.length())) {
- // Buffer non-CHLO packets.
- BufferEarlyPacket(*packet_info);
- break;
- }
- // We only apply this check for versions that do not use the IETF
- // invariant header because those versions are already checked in
- // QuicDispatcher::MaybeDispatchPacket.
- if (packet_info->version_flag &&
- !packet_info->version.HasIetfInvariantHeader() &&
- crypto_config()->validate_chlo_size() &&
- packet_info->packet.length() < kMinClientInitialPacketLength) {
- QUIC_DVLOG(1) << "Dropping CHLO packet which is too short, length: "
- << packet_info->packet.length();
- QUIC_CODE_COUNT(quic_drop_small_chlo_packets);
- break;
- }
+ if (fate == kFateProcess) {
+ std::string sni, uaid, legacy_version_encapsulation_inner_packet;
+ std::vector<std::string> alpns;
+ if (!TryExtractChloOrBufferEarlyPacket(
+ *packet_info, &sni, &uaid, &alpns,
+ &legacy_version_encapsulation_inner_packet)) {
+ // Client Hello incomplete. Packet has been buffered or (rarely) dropped.
+ return;
+ }
+
+ // Client Hello fully received.
+ fate = ValidityChecksOnFullChlo(*packet_info, sni, uaid, alpns);
- if (MaybeHandleLegacyVersionEncapsulation(this, &alpn_extractor,
- *packet_info)) {
- break;
+ if (fate == kFateProcess) {
+ QUICHE_DCHECK(legacy_version_encapsulation_inner_packet.empty() ||
+ !packet_info->version.UsesTls());
+ if (MaybeHandleLegacyVersionEncapsulation(
+ this, legacy_version_encapsulation_inner_packet, *packet_info)) {
+ return;
}
- ProcessChlo({alpn_extractor.ConsumeAlpn()}, alpn_extractor.ConsumeSni(),
- packet_info);
- } break;
+ ProcessChlo(alpns, sni, packet_info);
+ return;
+ }
+ }
+
+ switch (fate) {
+ case kFateProcess:
+ // kFateProcess have been processed above.
+ QUIC_BUG(quic_dispatcher_bad_packet_fate) << fate;
+ break;
case kFateTimeWait:
// Add this connection_id to the time-wait state, to safely reject
// future packets.
@@ -743,6 +717,81 @@ void QuicDispatcher::ProcessHeader(ReceivedPacketInfo* packet_info) {
}
}
+bool QuicDispatcher::TryExtractChloOrBufferEarlyPacket(
+ const ReceivedPacketInfo& packet_info,
+ std::string* sni,
+ std::string* uaid,
+ std::vector<std::string>* alpns,
+ std::string* legacy_version_encapsulation_inner_packet) {
+ sni->clear();
+ uaid->clear();
+ alpns->clear();
+ legacy_version_encapsulation_inner_packet->clear();
+
+ if (packet_info.version.UsesTls()) {
+ bool has_full_tls_chlo = false;
+ if (buffered_packets_.HasBufferedPackets(
+ packet_info.destination_connection_id)) {
+ // If we already have buffered packets for this connection ID,
+ // use the associated TlsChloExtractor to parse this packet.
+ has_full_tls_chlo = buffered_packets_.IngestPacketForTlsChloExtraction(
+ packet_info.destination_connection_id, packet_info.version,
+ packet_info.packet, alpns, sni);
+ } else {
+ // If we do not have a BufferedPacketList for this connection ID,
+ // create a single-use one to check whether this packet contains a
+ // full single-packet CHLO.
+ TlsChloExtractor tls_chlo_extractor;
+ tls_chlo_extractor.IngestPacket(packet_info.version, packet_info.packet);
+ if (tls_chlo_extractor.HasParsedFullChlo()) {
+ // This packet contains a full single-packet CHLO.
+ has_full_tls_chlo = true;
+ *alpns = tls_chlo_extractor.alpns();
+ *sni = tls_chlo_extractor.server_name();
+ }
+ }
+ if (!has_full_tls_chlo) {
+ // This packet does not contain a full CHLO. It could be a 0-RTT
+ // packet that arrived before the CHLO (due to loss or reordering),
+ // or it could be a fragment of a multi-packet CHLO.
+ BufferEarlyPacket(packet_info);
+ }
+
+ return has_full_tls_chlo;
+ }
+
+ ChloAlpnSniExtractor alpn_extractor;
+ if (GetQuicFlag(FLAGS_quic_allow_chlo_buffering) &&
+ !ChloExtractor::Extract(packet_info.packet, packet_info.version,
+ config_->create_session_tag_indicators(),
+ &alpn_extractor,
+ packet_info.destination_connection_id.length())) {
+ // Buffer non-CHLO packets.
+ BufferEarlyPacket(packet_info);
+ return false;
+ }
+
+ // We only apply this check for versions that do not use the IETF
+ // invariant header because those versions are already checked in
+ // QuicDispatcher::MaybeDispatchPacket.
+ if (packet_info.version_flag &&
+ !packet_info.version.HasIetfInvariantHeader() &&
+ crypto_config()->validate_chlo_size() &&
+ packet_info.packet.length() < kMinClientInitialPacketLength) {
+ QUIC_DVLOG(1) << "Dropping CHLO packet which is too short, length: "
+ << packet_info.packet.length();
+ QUIC_CODE_COUNT(quic_drop_small_chlo_packets);
+ return false;
+ }
+
+ *legacy_version_encapsulation_inner_packet =
+ alpn_extractor.ConsumeLegacyVersionEncapsulationInnerPacket();
+ *sni = alpn_extractor.ConsumeSni();
+ *uaid = alpn_extractor.ConsumeUaid();
+ *alpns = {alpn_extractor.ConsumeAlpn()};
+ return true;
+}
+
std::string QuicDispatcher::SelectAlpn(const std::vector<std::string>& alpns) {
if (alpns.empty()) {
return "";
@@ -1015,7 +1064,8 @@ void QuicDispatcher::OnNewConnectionIdSent(
<< server_connection_id << " new_connection_id: " << new_connection_id;
return;
}
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 5, 5);
+ // Count new connection ID added to the dispatcher map.
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 6, 6);
auto insertion_result = reference_counted_session_map_.insert(
std::make_pair(new_connection_id, it->second));
QUICHE_DCHECK(insertion_result.second);
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h
index 053f856da98..a6ba1a6e975 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h
@@ -27,9 +27,9 @@
#include "quic/core/quic_session.h"
#include "quic/core/quic_time_wait_list_manager.h"
#include "quic/core/quic_version_manager.h"
-#include "quic/platform/api/quic_containers.h"
#include "quic/platform/api/quic_reference_counted.h"
#include "quic/platform/api/quic_socket_address.h"
+#include "common/quiche_linked_hash_map.h"
namespace quic {
namespace test {
@@ -45,7 +45,8 @@ class QUIC_NO_EXPORT QuicDispatcher
public QuicBufferedPacketStore::VisitorInterface {
public:
// Ideally we'd have a linked_hash_set: the boolean is unused.
- using WriteBlockedList = QuicLinkedHashMap<QuicBlockedWriterInterface*, bool>;
+ using WriteBlockedList =
+ quiche::QuicheLinkedHashMap<QuicBlockedWriterInterface*, bool>;
QuicDispatcher(
const QuicConfig* config,
@@ -120,10 +121,6 @@ class QUIC_NO_EXPORT QuicDispatcher
void OnConnectionAddedToTimeWaitList(
QuicConnectionId server_connection_id) override;
- using SessionMap = absl::flat_hash_map<QuicConnectionId,
- std::unique_ptr<QuicSession>,
- QuicConnectionIdHash>;
-
using ReferenceCountedSessionMap =
absl::flat_hash_map<QuicConnectionId,
std::shared_ptr<QuicSession>,
@@ -131,8 +128,6 @@ class QUIC_NO_EXPORT QuicDispatcher
size_t NumSessions() const;
- const SessionMap& session_map() const { return session_map_; }
-
// Deletes all sessions on the closed session list and clears the list.
virtual void DeleteSessions();
@@ -229,6 +224,17 @@ class QUIC_NO_EXPORT QuicDispatcher
// TODO(fayang): Merge ValidityChecks into MaybeDispatchPacket.
virtual QuicPacketFate ValidityChecks(const ReceivedPacketInfo& packet_info);
+ // Extra validity checks after the full Client Hello is parsed, this allows
+ // subclasses to reject a connection based on sni or alpn.
+ // Only called if ValidityChecks returns kFateProcess.
+ virtual QuicPacketFate ValidityChecksOnFullChlo(
+ const ReceivedPacketInfo& /*packet_info*/,
+ const std::string& /*sni*/,
+ const std::string& /*uaid*/,
+ const std::vector<std::string>& /*alpns*/) const {
+ return kFateProcess;
+ }
+
// 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();
@@ -268,6 +274,10 @@ class QUIC_NO_EXPORT QuicDispatcher
return session_helper_.get();
}
+ const QuicCryptoServerStreamBase::Helper* session_helper() const {
+ return session_helper_.get();
+ }
+
QuicAlarmFactory* alarm_factory() { return alarm_factory_.get(); }
QuicPacketWriter* writer() { return writer_.get(); }
@@ -371,6 +381,21 @@ class QUIC_NO_EXPORT QuicDispatcher
// ProcessValidatedPacketWithUnknownConnectionId.
void ProcessHeader(ReceivedPacketInfo* packet_info);
+ // Try to extract information(sni, alpns, ...) if the full Client Hello has
+ // been parsed.
+ //
+ // If the full Client Hello has been parsed, return true and set |sni|,
+ // |alpns| and |legacy_version_encapsulation_inner_packet|. |uaid| will be
+ // populated for QUIC_CRYPTO only.
+ //
+ // Otherwise return false and either buffer or (rarely) drop the packet.
+ bool TryExtractChloOrBufferEarlyPacket(
+ const ReceivedPacketInfo& packet_info,
+ std::string* sni,
+ std::string* uaid,
+ std::vector<std::string>* alpns,
+ std::string* legacy_version_encapsulation_inner_packet);
+
// Deliver |packets| to |session| for further processing.
void DeliverPacketsToSession(
const std::list<QuicBufferedPacketStore::BufferedPacket>& packets,
@@ -389,7 +414,6 @@ class QUIC_NO_EXPORT QuicDispatcher
// The list of connections waiting to write.
WriteBlockedList write_blocked_list_;
- SessionMap session_map_;
ReferenceCountedSessionMap reference_counted_session_map_;
// Entity that manages connection_ids in time wait state.
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc
index 5a7a709f91b..3a98be3df90 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc
@@ -924,10 +924,12 @@ TEST_P(QuicDispatcherTestAllVersions,
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
- char short_packet[21] = {0x70, 0xa7, 0x02, 0x6b};
- QuicReceivedPacket packet(short_packet, 21, QuicTime::Zero());
- char valid_size_packet[23] = {0x70, 0xa7, 0x02, 0x6c};
- QuicReceivedPacket packet2(valid_size_packet, 23, QuicTime::Zero());
+ uint8_t short_packet[21] = {0x70, 0xa7, 0x02, 0x6b};
+ QuicReceivedPacket packet(reinterpret_cast<char*>(short_packet), 21,
+ QuicTime::Zero());
+ uint8_t valid_size_packet[23] = {0x70, 0xa7, 0x02, 0x6c};
+ QuicReceivedPacket packet2(reinterpret_cast<char*>(valid_size_packet), 23,
+ QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _))
.Times(0);
@@ -1186,10 +1188,10 @@ TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionDraft28WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
- char packet[kMinPacketSizeForVersionNegotiation] = {
+ uint8_t packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 0xFF, 0x00, 0x00, 28, /*destination connection ID length*/ 0x08};
- QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
- QuicTime::Zero());
+ QuicReceivedPacket received_packet(reinterpret_cast<char*>(packet),
+ ABSL_ARRAYSIZE(packet), QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
@@ -1203,10 +1205,10 @@ TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionDraft27WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
- char packet[kMinPacketSizeForVersionNegotiation] = {
+ uint8_t packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 0xFF, 0x00, 0x00, 27, /*destination connection ID length*/ 0x08};
- QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
- QuicTime::Zero());
+ QuicReceivedPacket received_packet(reinterpret_cast<char*>(packet),
+ ABSL_ARRAYSIZE(packet), QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
@@ -1220,10 +1222,10 @@ TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionDraft25WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
- char packet[kMinPacketSizeForVersionNegotiation] = {
+ uint8_t packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 0xFF, 0x00, 0x00, 25, /*destination connection ID length*/ 0x08};
- QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
- QuicTime::Zero());
+ QuicReceivedPacket received_packet(reinterpret_cast<char*>(packet),
+ ABSL_ARRAYSIZE(packet), QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
@@ -1237,10 +1239,10 @@ TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionT050WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
- char packet[kMinPacketSizeForVersionNegotiation] = {
+ uint8_t packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 'T', '0', '5', '0', /*destination connection ID length*/ 0x08};
- QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
- QuicTime::Zero());
+ QuicReceivedPacket received_packet(reinterpret_cast<char*>(packet),
+ ABSL_ARRAYSIZE(packet), QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
@@ -1254,10 +1256,10 @@ TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionQ049WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
- char packet[kMinPacketSizeForVersionNegotiation] = {
+ uint8_t packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 'Q', '0', '4', '9', /*destination connection ID length*/ 0x08};
- QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
- QuicTime::Zero());
+ QuicReceivedPacket received_packet(reinterpret_cast<char*>(packet),
+ ABSL_ARRAYSIZE(packet), QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
@@ -1271,10 +1273,10 @@ TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionQ048WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
- char packet[kMinPacketSizeForVersionNegotiation] = {
+ uint8_t packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 'Q', '0', '4', '8', /*connection ID length byte*/ 0x50};
- QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
- QuicTime::Zero());
+ QuicReceivedPacket received_packet(reinterpret_cast<char*>(packet),
+ ABSL_ARRAYSIZE(packet), QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
@@ -1288,10 +1290,10 @@ TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionQ047WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
- char packet[kMinPacketSizeForVersionNegotiation] = {
+ uint8_t packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 'Q', '0', '4', '7', /*connection ID length byte*/ 0x50};
- QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
- QuicTime::Zero());
+ QuicReceivedPacket received_packet(reinterpret_cast<char*>(packet),
+ ABSL_ARRAYSIZE(packet), QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
@@ -1305,10 +1307,10 @@ TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionQ045WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
- char packet[kMinPacketSizeForVersionNegotiation] = {
+ uint8_t packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 'Q', '0', '4', '5', /*connection ID length byte*/ 0x50};
- QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
- QuicTime::Zero());
+ QuicReceivedPacket received_packet(reinterpret_cast<char*>(packet),
+ ABSL_ARRAYSIZE(packet), QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
@@ -1322,10 +1324,11 @@ TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionQ044WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
- char packet44[kMinPacketSizeForVersionNegotiation] = {
+ uint8_t packet44[kMinPacketSizeForVersionNegotiation] = {
0xFF, 'Q', '0', '4', '4', /*connection ID length byte*/ 0x50};
- QuicReceivedPacket received_packet44(
- packet44, kMinPacketSizeForVersionNegotiation, QuicTime::Zero());
+ QuicReceivedPacket received_packet44(reinterpret_cast<char*>(packet44),
+ kMinPacketSizeForVersionNegotiation,
+ QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
@@ -1549,7 +1552,7 @@ TEST_P(QuicDispatcherTestAllVersions, ProcessSmallCoalescedPacket) {
EXPECT_CALL(*time_wait_list_manager_, SendPacket(_, _, _)).Times(0);
// clang-format off
- char coalesced_packet[1200] = {
+ uint8_t coalesced_packet[1200] = {
// first coalesced packet
// public flags (long header with packet type INITIAL and
// 4-byte packet number)
@@ -1586,7 +1589,8 @@ TEST_P(QuicDispatcherTestAllVersions, ProcessSmallCoalescedPacket) {
0x12, 0x34, 0x56, 0x79,
};
// clang-format on
- QuicReceivedPacket packet(coalesced_packet, 1200, QuicTime::Zero());
+ QuicReceivedPacket packet(reinterpret_cast<char*>(coalesced_packet), 1200,
+ QuicTime::Zero());
dispatcher_->ProcessPacket(server_address_, client_address, packet);
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc
index e82e7cb2eab..c3fcb887066 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc
@@ -238,6 +238,7 @@ const char* QuicErrorCodeToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_HTTP_GOAWAY_ID_LARGER_THAN_PREVIOUS);
RETURN_STRING_LITERAL(QUIC_HTTP_RECEIVE_SPDY_SETTING);
RETURN_STRING_LITERAL(QUIC_HTTP_RECEIVE_SPDY_FRAME);
+ RETURN_STRING_LITERAL(QUIC_HTTP_RECEIVE_SERVER_PUSH);
RETURN_STRING_LITERAL(QUIC_HPACK_INDEX_VARINT_ERROR);
RETURN_STRING_LITERAL(QUIC_HPACK_NAME_LENGTH_VARINT_ERROR);
RETURN_STRING_LITERAL(QUIC_HPACK_VALUE_LENGTH_VARINT_ERROR);
@@ -678,6 +679,9 @@ QuicErrorCodeToIetfMapping QuicErrorCodeToTransportErrorCode(
case QUIC_HTTP_STREAM_LIMIT_TOO_LOW:
return {false, static_cast<uint64_t>(
QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR)};
+ case QUIC_HTTP_RECEIVE_SERVER_PUSH:
+ return {false, static_cast<uint64_t>(
+ QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR)};
case QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH:
return {false, static_cast<uint64_t>(QuicHttp3ErrorCode::SETTINGS_ERROR)};
case QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH:
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h
index 9fa422f578c..53a881014ce 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h
@@ -510,6 +510,9 @@ enum QuicErrorCode {
QUIC_HTTP_RECEIVE_SPDY_SETTING = 169,
// HTTP/3 session received an HTTP/2 only frame.
QUIC_HTTP_RECEIVE_SPDY_FRAME = 171,
+ // HTTP/3 session received SERVER_PUSH stream, which is an error because
+ // PUSH_PROMISE is not accepted.
+ QUIC_HTTP_RECEIVE_SERVER_PUSH = 205,
// HPACK header block decoding errors.
// Index varint beyond implementation limit.
@@ -597,7 +600,7 @@ enum QuicErrorCode {
QUIC_TLS_CERTIFICATE_REQUIRED = 202,
// No error. Used as bound while iterating.
- QUIC_LAST_ERROR = 205,
+ QUIC_LAST_ERROR = 206,
};
// QuicErrorCodes is encoded as four octets on-the-wire when doing Google QUIC,
// or a varint62 when doing IETF QUIC. Ensure that its value does not exceed
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h b/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h
index df0df50f3fc..9bb54b26abd 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h
@@ -17,30 +17,30 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_true, true)
QUIC_FLAG(FLAGS_quic_restart_flag_quic_testonly_default_false, false)
// A testonly restart flag that will always default to true.
QUIC_FLAG(FLAGS_quic_restart_flag_quic_testonly_default_true, true)
-// Fix QUIC BBRv2\'s bandwidth_lo modes to better approximate packet conservation.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_fix_bw_lo_mode2, false)
-// If true, GFE will consider SNI values which do not contain dots (instead of throwing them away - see b/176998377).
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_and_tls_allow_sni_without_dots, true)
-// If true, QUIC BBRv2\'s PROBE_BW mode will not reduce cwnd below BDP+ack_height.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_avoid_too_low_probe_bw_cwnd, false)
+// If true and a QUIC connection is traced, add ssl events to the trace.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_trace_ssl_events, true)
+// If true, GFE will explicitly configure its signature algorithm preference.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_set_signature_algorithm_prefs, false)
// If true, QUIC will default enable MTU discovery at server, with a target of 1450 bytes.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_mtu_discovery_at_server, false)
-// If true, QuicBatchWriterBase will mark the writer as blocked when the write status is WRITE_STATUS_BLOCKED_DATA_BUFFERED.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_batch_writer_fix_write_blocked, true)
// If true, QuicGsoBatchWriter will support release time if it is available and the process has the permission to do so.
QUIC_FLAG(FLAGS_quic_restart_flag_quic_support_release_time_for_gso, false)
-// If true, TlsHandshaker::AdvanceHandshake will retry SSL_do_handshake when entered early data.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_retry_handshake_on_early_data, true)
-// If true, TlsServerHandshaker will install a packet flusher when async operation completes.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_packet_flusher_on_async_op_done, true)
-// If true, TlsServerHandshaker will use handshake hints(if present) to speed up handshakes.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_server_use_handshake_hints, true)
+// If true, QuicIdleNetworkDetector::SetAlarm will become a noop if dectection has been stopped by QuicIdleNetworkDetector::StopDetection.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_idle_network_detector_no_alarm_after_stopped, true)
+// If true, TlsServerHandshaker::DefaultProofSourceHandle::DefaultSignatureCallback will run at most once.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_run_default_signature_callback_once, true)
// If true, abort async QPACK header decompression in QuicSpdyStream::Reset() and in QuicSpdyStream::OnStreamReset().
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_abort_qpack_on_stream_reset, true)
// If true, ack frequency frame can be sent from server to client.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_can_send_ack_frequency, true)
+// If true, add missing MaybeUpdateAckTimeout for ack-eliciting frames.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_missing_update_ack_timeout, true)
+// If true, allow QuicAlarm to be permanently cancelled.
+QUIC_FLAG(FLAGS_quic_restart_flag_quic_alarm_add_permanent_cancel, false)
// If true, allow client to enable BBRv2 on server via connection option \'B2ON\'.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allow_client_enabled_bbr_v2, false)
+// If true, allow ticket open to be ignored in TlsServerHandshaker. Also fixes TlsServerHandshaker::ResumptionAttempted when handshake hints is used.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_allow_ignore_ticket_open, true)
// If true, close read side but not write side in QuicSpdyStream::OnStreamReset().
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, true)
// If true, default on PTO which unifies TLP + RTO loss recovery.
@@ -48,13 +48,15 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_on_pto, false)
// If true, default-enable 5RTO blachole detection.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_enable_5rto_blackhole_detection2, true)
// If true, determine stateless reset packet length based on the received packet length.
-QUIC_FLAG(FLAGS_quic_restart_flag_quic_fix_stateless_reset2, false)
+QUIC_FLAG(FLAGS_quic_restart_flag_quic_fix_stateless_reset2, true)
// If true, disable QUIC version Q043.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_q043, false)
// If true, disable QUIC version Q046.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_q046, false)
// If true, disable QUIC version Q050.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_q050, false)
+// If true, disable QUIC version h3 (RFCv1).
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_rfcv1, false)
// If true, disable QUIC version h3-29.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_draft_29, false)
// If true, disable QUIC version h3-T051.
@@ -67,70 +69,62 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_discard_initial_packet_with_key_droppe
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2, false)
// If true, do not count bytes sent/received on the alternative path into the bytes sent/received on the default path.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_count_bytes_on_alternative_path_seperately, true)
-// If true, do not send control frames before encryption is established.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_control_frames, false)
-// If true, do not send stream data when PTO fires.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_pto_half_rtt_data, true)
-// If true, do not write stream data and control frames in the middle of packet processing.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_write_mid_packet_processing, true)
+// If true, do not re-arm PTO while sending application data during handshake.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_rearm_pto_on_application_data_during_handshake, true)
// If true, drop unsent PATH_RESPONSEs and rely on peer\'s retry.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_drop_unsent_path_response, true)
-// If true, enable QUIC version h3 (RFCv1).
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_version_rfcv1, false)
// If true, enable server retransmittable on wire PING.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_server_on_wire_ping, true)
+// If true, ignore peer_max_ack_delay during handshake.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ignore_peer_max_ack_delay_during_handshake, true)
+// If true, include non-default port in the origin field of the ACCEPT_CH frame in ALPS.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_include_port_in_alps_origin, true)
// If true, include stream information in idle timeout connection close detail.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_stream_info_to_idle_close_detail, true)
// If true, increase the size of stream sequencer buffer block container on demand.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allocate_stream_sequencer_buffer_blocks_on_demand, false)
// If true, pass the received PATH_RESPONSE payload to path validator to move forward the path validation.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator, true)
// If true, quic connection sends/recieives NewConnectionId & RetireConnectionId frames.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_support_multiple_cids_v4, true)
// If true, quic dispatcher discards packets with invalid server connection ID.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_discard_packets_with_invalid_cid, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_discard_packets_with_invalid_cid, true)
// If true, quic dispatcher supports one connection to use multiple connection IDs.
QUIC_FLAG(FLAGS_quic_restart_flag_quic_dispatcher_support_multiple_cid_per_connection_v2, true)
+// If true, receiving server push stream will trigger QUIC connection close.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_decline_server_push_stream, false)
// If true, require handshake confirmation for QUIC connections, functionally disabling 0-rtt handshakes.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_require_handshake_confirmation, false)
+// If true, reset per packet state before processing undecryptable packets.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_reset_per_packet_state_for_undecryptable_packets, true)
// If true, send PATH_RESPONSE upon receiving PATH_CHALLENGE regardless of perspective. --gfe2_reloadable_flag_quic_start_peer_migration_earlier has to be true before turn on this flag.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_path_response2, true)
// If true, set burst token to 2 in cwnd bootstrapping experiment.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_bursts, false)
-// If true, signal error in HttpDecoder upon receiving a PUSH_PROMISE or CANCEL_PUSH frame.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_error_on_http3_push, true)
// If true, stop resetting ideal_next_packet_send_time_ in pacing sender.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_reset_ideal_next_packet_send_time, false)
// If true, time_wait_list can support multiple connection IDs.
QUIC_FLAG(FLAGS_quic_restart_flag_quic_time_wait_list_support_multiple_cid_v2, true)
-// If true, treat old (pre-draft02) PRIORITY_UPDATE frame as unknown frame.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ignore_old_priority_update_frame, true)
+// If true, update ACK timeout for NEW_CONNECTION_ID and RETIRE_CONNECTION_ID frames.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ack_cid_frames, true)
// If true, upon receiving path challenge, send path response and reverse path challenge in the same function.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_group_path_response_and_challenge_sending_closer, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_group_path_response_and_challenge_sending_closer, true)
// If true, use BBRv2 as the default congestion controller. Takes precedence over --quic_default_to_bbr.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr_v2, false)
-// If true, use ScopedEncryptionLevelContext when sending data.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_encryption_level_context, true)
-// If true, use WriteOrBufferDataAtLevel to send crypto data. Existing WriteOrBufferData is used to send application data.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_write_or_buffer_data_at_level, false)
// If true, use new connection ID in connection migration.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid_v2, false)
-// If true, use the connection ID and stateless reset token on default PathState.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_connection_id_on_default_path_v2, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid_v2, true)
// If true, uses conservative cwnd gain and pacing gain when cwnd gets bootstrapped.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false)
// If true, validate that peer owns the new address once the server detects peer migration or is probed from that address, and also apply anti-amplification limit while sending to that address.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path3, false)
-// If ture, replace the incoming_connection_ids check with original_destination_connection_id check.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_deprecate_incoming_connection_ids, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path3, true)
+// Queue packets to attempt decryption later until the handshake is complete.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_queue_until_handshake_complete, true)
// When the STMP connection option is sent by the client, timestamps in the QUIC ACK frame are sent and processed.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_timestamps, false)
-// When true, QuicSpdySession supports draft-ietf-masque-h3-datagram.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_h3_datagram, false)
// When true, defaults to BBR congestion control instead of Cubic.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr, false)
-// When true, makes the QUIC BBRv2 bw_lo modes more similar to standard BBRv2.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_fix_bw_lo_mode, true)
+// When true, prevents QUIC\'s PacingSender from generating bursts when the congestion controller is CWND limited and not pacing limited.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_pacing_sender_bursts, false)
// When true, set the initial congestion control window from connection options in QuicSentPacketManager rather than TcpCubicSenderBytes.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, false)
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc
index e186ce33ecb..24b2be86484 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc
@@ -47,7 +47,6 @@
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/platform/api/quic_stack_trace.h"
#include "common/quiche_text_utils.h"
@@ -399,8 +398,7 @@ std::string GenerateErrorString(std::string initial_error_string,
} // namespace
QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions,
- QuicTime creation_time,
- Perspective perspective,
+ QuicTime creation_time, Perspective perspective,
uint8_t expected_server_connection_id_length)
: visitor_(nullptr),
error_(QUIC_NO_ERROR),
@@ -430,7 +428,8 @@ QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions,
last_written_packet_number_length_(0),
peer_ack_delay_exponent_(kDefaultAckDelayExponent),
local_ack_delay_exponent_(kDefaultAckDelayExponent),
- current_received_frame_type_(0) {
+ current_received_frame_type_(0),
+ previously_received_frame_type_(0) {
QUICHE_DCHECK(!supported_versions.empty());
version_ = supported_versions_[0];
QUICHE_DCHECK(version_.IsKnown())
@@ -1503,6 +1502,7 @@ QuicFramer::BuildIetfVersionNegotiationPacket(
}
bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
+ QUICHE_DCHECK(!is_processing_packet_) << ENDPOINT << "Nested ProcessPacket";
is_processing_packet_ = true;
bool result = ProcessPacketInternal(packet);
is_processing_packet_ = false;
@@ -1964,8 +1964,10 @@ bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader,
// Handle the payload.
if (VersionHasIetfQuicFrames(version_.transport_version)) {
current_received_frame_type_ = 0;
+ previously_received_frame_type_ = 0;
if (!ProcessIetfFrameData(&reader, *header, decrypted_level)) {
current_received_frame_type_ = 0;
+ previously_received_frame_type_ = 0;
QUICHE_DCHECK_NE(QUIC_NO_ERROR,
error_); // ProcessIetfFrameData sets the error.
QUICHE_DCHECK_NE("", detailed_error_);
@@ -1974,6 +1976,7 @@ bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader,
return false;
}
current_received_frame_type_ = 0;
+ previously_received_frame_type_ = 0;
} else {
if (!ProcessFrameData(&reader, *header)) {
QUICHE_DCHECK_NE(QUIC_NO_ERROR,
@@ -3215,6 +3218,7 @@ bool QuicFramer::ProcessIetfFrameData(QuicDataReader* reader,
EncryptionLevelToString(decrypted_level)));
return RaiseError(IETF_QUIC_PROTOCOL_VIOLATION);
}
+ previously_received_frame_type_ = current_received_frame_type_;
current_received_frame_type_ = frame_type;
// Is now the number of bytes into which the frame type was encoded.
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer.h b/chromium/net/third_party/quiche/src/quic/core/quic_framer.h
index 2920db351c4..31cc756990b 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_framer.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer.h
@@ -650,6 +650,10 @@ class QUIC_EXPORT_PRIVATE QuicFramer {
return current_received_frame_type_;
}
+ uint64_t previously_received_frame_type() const {
+ return previously_received_frame_type_;
+ }
+
// The connection ID length the framer expects on incoming IETF short headers
// on the server.
uint8_t GetExpectedServerConnectionIdLength() {
@@ -1190,6 +1194,11 @@ class QUIC_EXPORT_PRIVATE QuicFramer {
// the Transport Connection Close when there is an error during frame
// processing.
uint64_t current_received_frame_type_;
+
+ // TODO(haoyuewang) Remove this debug utility.
+ // The type of the IETF frame preceding the frame currently being processed. 0
+ // when not processing a frame or only 1 frame has been processed.
+ uint64_t previously_received_frame_type_;
};
// Look for and parse the error code from the "<quic_error_code>:" text that
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc
index 63a1ad5ed4f..f36bf5c78c7 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc
@@ -61,9 +61,10 @@ QuicConnectionId FramerTestConnectionIdPlusOne() {
}
QuicConnectionId FramerTestConnectionIdNineBytes() {
- char connection_id_bytes[9] = {0xFE, 0xDC, 0xBA, 0x98, 0x76,
- 0x54, 0x32, 0x10, 0x42};
- return QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes));
+ uint8_t connection_id_bytes[9] = {0xFE, 0xDC, 0xBA, 0x98, 0x76,
+ 0x54, 0x32, 0x10, 0x42};
+ return QuicConnectionId(reinterpret_cast<char*>(connection_id_bytes),
+ sizeof(connection_id_bytes));
}
const QuicPacketNumber kPacketNumber = QuicPacketNumber(UINT64_C(0x12345678));
@@ -8946,10 +8947,9 @@ TEST_P(QuicFramerTest, BuildMessagePacket) {
header.reset_flag = false;
header.version_flag = false;
header.packet_number = kPacketNumber;
- QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
- QuicMessageFrame frame(1, MakeSpan(&allocator_, "message", &storage));
- QuicMessageFrame frame2(2, MakeSpan(&allocator_, "message2", &storage));
+ QuicMessageFrame frame(1, MemSliceFromString("message"));
+ QuicMessageFrame frame2(2, MemSliceFromString("message2"));
QuicFrames frames = {QuicFrame(&frame), QuicFrame(&frame2)};
// clang-format off
@@ -13730,9 +13730,9 @@ TEST_P(QuicFramerTest, PacketHeaderWithVariableLengthConnectionId) {
return;
}
SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
- char connection_id_bytes[9] = {0xFE, 0xDC, 0xBA, 0x98, 0x76,
- 0x54, 0x32, 0x10, 0x42};
- QuicConnectionId connection_id(connection_id_bytes,
+ uint8_t connection_id_bytes[9] = {0xFE, 0xDC, 0xBA, 0x98, 0x76,
+ 0x54, 0x32, 0x10, 0x42};
+ QuicConnectionId connection_id(reinterpret_cast<char*>(connection_id_bytes),
sizeof(connection_id_bytes));
QuicFramerPeer::SetLargestPacketNumber(&framer_, kPacketNumber - 2);
QuicFramerPeer::SetExpectedServerConnectionIDLength(&framer_,
@@ -14031,7 +14031,7 @@ TEST_P(QuicFramerTest, ProcessMismatchedHeaderVersion) {
TEST_P(QuicFramerTest, WriteClientVersionNegotiationProbePacket) {
// clang-format off
- static const char expected_packet[1200] = {
+ static const uint8_t expected_packet[1200] = {
// IETF long header with fixed bit set, type initial, all-0 encrypted bits.
0xc0,
// Version, part of the IETF space reserved for negotiation.
@@ -14080,9 +14080,9 @@ TEST_P(QuicFramerTest, WriteClientVersionNegotiationProbePacket) {
EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket(
packet, sizeof(packet), destination_connection_id_bytes,
sizeof(destination_connection_id_bytes)));
- quiche::test::CompareCharArraysWithHexError("constructed packet", packet,
- sizeof(packet), expected_packet,
- sizeof(expected_packet));
+ quiche::test::CompareCharArraysWithHexError(
+ "constructed packet", packet, sizeof(packet),
+ reinterpret_cast<const char*>(expected_packet), sizeof(expected_packet));
QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet),
sizeof(packet), false);
if (!framer_.version().HasLengthPrefixedConnectionIds()) {
@@ -14102,7 +14102,7 @@ TEST_P(QuicFramerTest, WriteClientVersionNegotiationProbePacket) {
TEST_P(QuicFramerTest, DispatcherParseOldClientVersionNegotiationProbePacket) {
// clang-format off
- static const char packet[1200] = {
+ static const uint8_t packet[1200] = {
// IETF long header with fixed bit set, type initial, all-0 encrypted bits.
0xc0,
// Version, part of the IETF space reserved for negotiation.
@@ -14179,7 +14179,7 @@ TEST_P(QuicFramerTest, DispatcherParseOldClientVersionNegotiationProbePacket) {
TEST_P(QuicFramerTest, DispatcherParseClientVersionNegotiationProbePacket) {
// clang-format off
- static const char packet[1200] = {
+ static const uint8_t packet[1200] = {
// IETF long header with fixed bit set, type initial, all-0 encrypted bits.
0xc0,
// Version, part of the IETF space reserved for negotiation.
@@ -14257,7 +14257,7 @@ TEST_P(QuicFramerTest, DispatcherParseClientVersionNegotiationProbePacket) {
TEST_P(QuicFramerTest, ParseServerVersionNegotiationProbeResponse) {
// clang-format off
- const char packet[] = {
+ const uint8_t packet[] = {
// IETF long header with fixed bit set, type initial, all-0 encrypted bits.
0xc0,
// Version of 0, indicating version negotiation.
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.cc b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.cc
index 8046e446cee..ac6799ee46d 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.cc
@@ -5,6 +5,7 @@
#include "quic/core/quic_idle_network_detector.h"
#include "quic/core/quic_constants.h"
+#include "quic/platform/api/quic_flag_utils.h"
namespace quic {
@@ -26,9 +27,7 @@ class AlarmDelegate : public QuicAlarm::Delegate {
} // namespace
QuicIdleNetworkDetector::QuicIdleNetworkDetector(
- Delegate* delegate,
- QuicTime now,
- QuicConnectionArena* arena,
+ Delegate* delegate, QuicTime now, QuicConnectionArena* arena,
QuicAlarmFactory* alarm_factory)
: delegate_(delegate),
start_time_(now),
@@ -37,7 +36,12 @@ QuicIdleNetworkDetector::QuicIdleNetworkDetector(
time_of_first_packet_sent_after_receiving_(QuicTime::Zero()),
idle_network_timeout_(QuicTime::Delta::Infinite()),
alarm_(
- alarm_factory->CreateAlarm(arena->New<AlarmDelegate>(this), arena)) {}
+ alarm_factory->CreateAlarm(arena->New<AlarmDelegate>(this), arena)) {
+ if (no_alarm_after_stopped_) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(
+ quic_idle_network_detector_no_alarm_after_stopped, 1, 2);
+ }
+}
void QuicIdleNetworkDetector::OnAlarm() {
if (handshake_timeout_.IsInfinite()) {
@@ -66,9 +70,10 @@ void QuicIdleNetworkDetector::SetTimeouts(
}
void QuicIdleNetworkDetector::StopDetection() {
- alarm_->Cancel();
+ alarm_->PermanentCancel();
handshake_timeout_ = QuicTime::Delta::Infinite();
idle_network_timeout_ = QuicTime::Delta::Infinite();
+ stopped_ = true;
}
void QuicIdleNetworkDetector::OnPacketSent(QuicTime now,
@@ -94,6 +99,17 @@ void QuicIdleNetworkDetector::OnPacketReceived(QuicTime now) {
}
void QuicIdleNetworkDetector::SetAlarm() {
+ if (no_alarm_after_stopped_ && stopped_) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(
+ quic_idle_network_detector_no_alarm_after_stopped, 2, 2);
+
+ // TODO(wub): If this QUIC_BUG fires, it indicates a problem in the
+ // QuicConnection, which somehow called this function while disconnected.
+ // That problem needs to be fixed.
+ QUIC_BUG(quic_idle_detector_set_alarm_after_stopped)
+ << "SetAlarm called after stopped";
+ return;
+ }
// Set alarm to the nearer deadline.
QuicTime new_deadline = QuicTime::Zero();
if (!handshake_timeout_.IsInfinite()) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.h b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.h
index e138d6a068e..1665104a186 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.h
@@ -10,6 +10,7 @@
#include "quic/core/quic_one_block_arena.h"
#include "quic/core/quic_time.h"
#include "quic/platform/api/quic_export.h"
+#include "quic/platform/api/quic_flags.h"
namespace quic {
@@ -46,6 +47,7 @@ class QUIC_EXPORT_PRIVATE QuicIdleNetworkDetector {
void SetTimeouts(QuicTime::Delta handshake_timeout,
QuicTime::Delta idle_network_timeout);
+ // Stop the detection once and for all.
void StopDetection();
// Called when a packet gets sent.
@@ -106,6 +108,12 @@ class QUIC_EXPORT_PRIVATE QuicIdleNetworkDetector {
QuicArenaScopedPtr<QuicAlarm> alarm_;
bool shorter_idle_timeout_on_sent_packet_ = false;
+
+ // Whether |StopDetection| has been called.
+ bool stopped_ = false;
+
+ const bool no_alarm_after_stopped_ =
+ GetQuicReloadableFlag(quic_idle_network_detector_no_alarm_after_stopped);
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector_test.cc
index 271247808c9..11d0cf76777 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector_test.cc
@@ -5,6 +5,7 @@
#include "quic/core/quic_idle_network_detector.h"
#include "quic/core/quic_one_block_arena.h"
+#include "quic/platform/api/quic_expect_bug.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/quic_test_utils.h"
@@ -181,6 +182,25 @@ TEST_F(QuicIdleNetworkDetectorTest, ShorterIdleTimeoutOnSentPacket) {
EXPECT_EQ(clock_.Now() + QuicTime::Delta::FromSeconds(2), alarm_->deadline());
}
+TEST_F(QuicIdleNetworkDetectorTest, NoAlarmAfterStopped) {
+ detector_->StopDetection();
+
+ if (GetQuicReloadableFlag(
+ quic_idle_network_detector_no_alarm_after_stopped)) {
+ EXPECT_QUIC_BUG(
+ detector_->SetTimeouts(
+ /*handshake_timeout=*/QuicTime::Delta::FromSeconds(30),
+ /*idle_network_timeout=*/QuicTime::Delta::FromSeconds(20)),
+ "SetAlarm called after stopped");
+ EXPECT_FALSE(alarm_->IsSet());
+ } else {
+ detector_->SetTimeouts(
+ /*handshake_timeout=*/QuicTime::Delta::FromSeconds(30),
+ /*idle_network_timeout=*/QuicTime::Delta::FromSeconds(20));
+ EXPECT_TRUE(alarm_->IsSet());
+ }
+}
+
} // namespace
} // namespace test
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_interval_set.h b/chromium/net/third_party/quiche/src/quic/core/quic_interval_set.h
index fef089313fa..d69fa710604 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_interval_set.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_interval_set.h
@@ -85,9 +85,7 @@ class QUIC_NO_EXPORT QuicIntervalSet {
bool operator()(T&& point, const value_type& a) const;
};
- using Set = QuicOrderedSet<value_type,
- IntervalLess,
- QuicInlinedVector<value_type, 10>>;
+ using Set = QuicSmallOrderedSet<value_type, IntervalLess>;
public:
using const_iterator = typename Set::const_iterator;
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_lru_cache.h b/chromium/net/third_party/quiche/src/quic/core/quic_lru_cache.h
index d4b11e0ce5f..67541ee3a23 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_lru_cache.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_lru_cache.h
@@ -7,11 +7,11 @@
#include <memory>
-#include "quic/platform/api/quic_containers.h"
#include "quic/platform/api/quic_export.h"
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
+#include "common/quiche_linked_hash_map.h"
namespace quic {
@@ -67,7 +67,7 @@ class QUIC_NO_EXPORT QuicLRUCache {
size_t Size() const { return cache_.size(); }
private:
- QuicLinkedHashMap<K, std::unique_ptr<V>> cache_;
+ quiche::QuicheLinkedHashMap<K, std::unique_ptr<V>> cache_;
const size_t capacity_;
};
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.cc b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.cc
index 5281ffb980a..4dca50832ac 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.cc
@@ -64,8 +64,12 @@ void QuicNetworkBlackholeDetector::OnAlarm() {
UpdateAlarm();
}
-void QuicNetworkBlackholeDetector::StopDetection() {
- alarm_->Cancel();
+void QuicNetworkBlackholeDetector::StopDetection(bool permanent) {
+ if (permanent) {
+ alarm_->PermanentCancel();
+ } else {
+ alarm_->Cancel();
+ }
path_degrading_deadline_ = QuicTime::Zero();
blackhole_deadline_ = QuicTime::Zero();
path_mtu_reduction_deadline_ = QuicTime::Zero();
@@ -108,6 +112,12 @@ QuicTime QuicNetworkBlackholeDetector::GetLastDeadline() const {
}
void QuicNetworkBlackholeDetector::UpdateAlarm() const {
+ // If called after OnBlackholeDetected(), the alarm may have been permanently
+ // cancelled and is not safe to be armed again.
+ if (alarm_->IsPermanentlyCancelled()) {
+ return;
+ }
+
QuicTime next_deadline = GetEarliestDeadline();
QUIC_DVLOG(1) << "Updating alarm. next_deadline:" << next_deadline
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h
index 7010eb581a8..82753f934a6 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h
@@ -44,8 +44,9 @@ class QUIC_EXPORT_PRIVATE QuicNetworkBlackholeDetector {
QuicConnectionArena* arena,
QuicAlarmFactory* alarm_factory);
- // Called to stop all detections.
- void StopDetection();
+ // Called to stop all detections. If |permanent|, the alarm will be cancelled
+ // permanently and future calls to RestartDetection will be no-op.
+ void StopDetection(bool permanent);
// Called to restart path degrading, path mtu reduction and blackhole
// detections. Please note, if |blackhole_deadline| is set, it must be the
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector_test.cc
index 3bc6ad73cbb..66bebfd455f 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector_test.cc
@@ -106,7 +106,7 @@ TEST_F(QuicNetworkBlackholeDetectorTest, RestartAndStop) {
RestartDetection();
EXPECT_EQ(clock_.Now() + path_degrading_delay_, alarm_->deadline());
- detector_.StopDetection();
+ detector_.StopDetection(/*permanent=*/false);
EXPECT_FALSE(detector_.IsDetectionInProgress());
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc
index cbca89dae55..94cf48a1d51 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc
@@ -34,6 +34,7 @@
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
#include "quic/platform/api/quic_server_stats.h"
+#include "common/print_elements.h"
namespace quic {
namespace {
@@ -696,6 +697,10 @@ bool QuicPacketCreator::HasPendingFrames() const {
return !queued_frames_.empty();
}
+std::string QuicPacketCreator::GetPendingFramesInfo() const {
+ return QuicFramesToString(queued_frames_);
+}
+
bool QuicPacketCreator::HasPendingRetransmittableFrames() const {
return !packet_.retransmittable_frames.empty();
}
@@ -1522,7 +1527,7 @@ bool QuicPacketCreator::FlushAckFrame(const QuicFrames& frames) {
QUIC_BUG_IF(quic_bug_12398_18,
GetQuicReloadableFlag(quic_single_ack_in_packet2) &&
!frames.empty() && has_ack())
- << ENDPOINT << "Trying to flush " << frames
+ << ENDPOINT << "Trying to flush " << quiche::PrintElements(frames)
<< " when there is ACK queued";
for (const auto& frame : frames) {
QUICHE_DCHECK(frame.type == ACK_FRAME || frame.type == STOP_WAITING_FRAME)
@@ -1596,14 +1601,14 @@ void QuicPacketCreator::SetTransmissionType(TransmissionType type) {
next_transmission_type_ = type;
}
-MessageStatus QuicPacketCreator::AddMessageFrame(QuicMessageId message_id,
- QuicMemSliceSpan message) {
+MessageStatus QuicPacketCreator::AddMessageFrame(
+ QuicMessageId message_id, absl::Span<QuicMemSlice> message) {
QUIC_BUG_IF(quic_bug_10752_33, !flusher_attached_)
<< ENDPOINT
<< "Packet flusher is not attached when "
"generator tries to add message frame.";
MaybeBundleAckOpportunistically();
- const QuicByteCount message_length = message.total_length();
+ const QuicByteCount message_length = MemSliceSpanTotalSize(message);
if (message_length > GetCurrentLargestMessagePayload()) {
return MESSAGE_STATUS_TOO_LARGE;
}
@@ -1618,6 +1623,8 @@ MessageStatus QuicPacketCreator::AddMessageFrame(QuicMessageId message_id,
delete frame;
return MESSAGE_STATUS_INTERNAL_ERROR;
}
+ QUICHE_DCHECK_EQ(MemSliceSpanTotalSize(message),
+ 0u); // Ensure the old slices are empty.
return MESSAGE_STATUS_SUCCESS;
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h
index d4a58055282..9cc4bcae92e 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h
@@ -205,6 +205,10 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator {
// Returns true if there are frames pending to be serialized.
bool HasPendingFrames() const;
+ // TODO(haoyuewang) Remove this debug utility.
+ // Returns the information of pending frames as a string.
+ std::string GetPendingFramesInfo() const;
+
// Returns true if there are retransmittable frames pending to be serialized.
bool HasPendingRetransmittableFrames() const;
@@ -417,7 +421,7 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator {
// Tries to add a message frame containing |message| and returns the status.
MessageStatus AddMessageFrame(QuicMessageId message_id,
- QuicMemSliceSpan message);
+ absl::Span<QuicMemSlice> message);
// Returns the largest payload that will fit into a single MESSAGE frame.
QuicPacketLength GetCurrentLargestMessagePayload() const;
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc
index 57d1018d94c..3dc1a4b184b 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc
@@ -423,6 +423,8 @@ TEST_P(QuicPacketCreatorTest, ConsumeDataFinOnly) {
EXPECT_EQ(0u, consumed);
CheckStreamFrame(frame, stream_id, std::string(), 0u, true);
EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_TRUE(absl::StartsWith(creator_.GetPendingFramesInfo(),
+ "type { STREAM_FRAME }"));
}
TEST_P(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
@@ -1767,25 +1769,24 @@ TEST_P(QuicPacketCreatorTest, AddMessageFrame) {
.Times(3)
.WillRepeatedly(
Invoke(this, &QuicPacketCreatorTest::ClearSerializedPacketForTests));
- QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
// Verify that there is enough room for the largest message payload.
EXPECT_TRUE(creator_.HasRoomForMessageFrame(
creator_.GetCurrentLargestMessagePayload()));
- std::string message(creator_.GetCurrentLargestMessagePayload(), 'a');
+ std::string large_message(creator_.GetCurrentLargestMessagePayload(), 'a');
QuicMessageFrame* message_frame =
- new QuicMessageFrame(1, MakeSpan(&allocator_, message, &storage));
+ new QuicMessageFrame(1, MemSliceFromString(large_message));
EXPECT_TRUE(creator_.AddFrame(QuicFrame(message_frame), NOT_RETRANSMISSION));
EXPECT_TRUE(creator_.HasPendingFrames());
creator_.FlushCurrentPacket();
QuicMessageFrame* frame2 =
- new QuicMessageFrame(2, MakeSpan(&allocator_, "message", &storage));
+ new QuicMessageFrame(2, MemSliceFromString("message"));
EXPECT_TRUE(creator_.AddFrame(QuicFrame(frame2), NOT_RETRANSMISSION));
EXPECT_TRUE(creator_.HasPendingFrames());
// Verify if a new frame is added, 1 byte message length will be added.
EXPECT_EQ(1u, creator_.ExpansionOnNewFrame());
QuicMessageFrame* frame3 =
- new QuicMessageFrame(3, MakeSpan(&allocator_, "message2", &storage));
+ new QuicMessageFrame(3, MemSliceFromString("message2"));
EXPECT_TRUE(creator_.AddFrame(QuicFrame(frame3), NOT_RETRANSMISSION));
EXPECT_EQ(1u, creator_.ExpansionOnNewFrame());
creator_.FlushCurrentPacket();
@@ -1798,14 +1799,14 @@ TEST_P(QuicPacketCreatorTest, AddMessageFrame) {
stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false,
NOT_RETRANSMISSION, &frame));
QuicMessageFrame* frame4 =
- new QuicMessageFrame(4, MakeSpan(&allocator_, "message", &storage));
+ new QuicMessageFrame(4, MemSliceFromString("message"));
EXPECT_TRUE(creator_.AddFrame(QuicFrame(frame4), NOT_RETRANSMISSION));
EXPECT_TRUE(creator_.HasPendingFrames());
// Verify there is not enough room for largest payload.
EXPECT_FALSE(creator_.HasRoomForMessageFrame(
creator_.GetCurrentLargestMessagePayload()));
// Add largest message will causes the flush of the stream frame.
- QuicMessageFrame frame5(5, MakeSpan(&allocator_, message, &storage));
+ QuicMessageFrame frame5(5, MemSliceFromString(large_message));
EXPECT_FALSE(creator_.AddFrame(QuicFrame(&frame5), NOT_RETRANSMISSION));
EXPECT_FALSE(creator_.HasPendingFrames());
}
@@ -1818,8 +1819,6 @@ TEST_P(QuicPacketCreatorTest, MessageFrameConsumption) {
creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize);
}
std::string message_data(kDefaultMaxPacketSize, 'a');
- absl::string_view message_buffer(message_data);
- QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
// Test all possible encryption levels of message frames.
for (EncryptionLevel level :
{ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
@@ -1828,10 +1827,9 @@ TEST_P(QuicPacketCreatorTest, MessageFrameConsumption) {
for (size_t message_size = 0;
message_size <= creator_.GetCurrentLargestMessagePayload();
++message_size) {
- QuicMessageFrame* frame = new QuicMessageFrame(
- 0, MakeSpan(&allocator_,
- absl::string_view(message_buffer.data(), message_size),
- &storage));
+ QuicMessageFrame* frame =
+ new QuicMessageFrame(0, MemSliceFromString(absl::string_view(
+ message_data.data(), message_size)));
EXPECT_TRUE(creator_.AddFrame(QuicFrame(frame), NOT_RETRANSMISSION));
EXPECT_TRUE(creator_.HasPendingFrames());
@@ -2450,12 +2448,13 @@ class MultiplePacketsTestPacketCreator : public QuicPacketCreator {
}
MessageStatus AddMessageFrame(QuicMessageId message_id,
- QuicMemSliceSpan message) {
+ QuicMemSlice message) {
if (!has_ack() && delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
NOT_HANDSHAKE)) {
EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1);
}
- return QuicPacketCreator::AddMessageFrame(message_id, message);
+ return QuicPacketCreator::AddMessageFrame(message_id,
+ absl::MakeSpan(&message, 1));
}
size_t ConsumeCryptoData(EncryptionLevel level,
@@ -3792,7 +3791,6 @@ TEST_F(QuicPacketCreatorMultiplePacketsTest, AddMessageFrame) {
if (framer_.version().UsesTls()) {
creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize);
}
- quic::QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
delegate_.SetCanWriteAnything();
EXPECT_CALL(delegate_, OnSerializedPacket(_))
.WillOnce(
@@ -3802,30 +3800,23 @@ TEST_F(QuicPacketCreatorMultiplePacketsTest, AddMessageFrame) {
creator_.ConsumeData(QuicUtils::GetFirstBidirectionalStreamId(
framer_.transport_version(), Perspective::IS_CLIENT),
&iov_, 1u, iov_.iov_len, 0, FIN);
- EXPECT_EQ(
- MESSAGE_STATUS_SUCCESS,
- creator_.AddMessageFrame(1, MakeSpan(&allocator_, "message", &storage)));
+ EXPECT_EQ(MESSAGE_STATUS_SUCCESS,
+ creator_.AddMessageFrame(1, MemSliceFromString("message")));
EXPECT_TRUE(creator_.HasPendingFrames());
EXPECT_TRUE(creator_.HasPendingRetransmittableFrames());
// Add a message which causes the flush of current packet.
- EXPECT_EQ(
- MESSAGE_STATUS_SUCCESS,
- creator_.AddMessageFrame(
- 2,
- MakeSpan(&allocator_,
- std::string(creator_.GetCurrentLargestMessagePayload(), 'a'),
- &storage)));
+ EXPECT_EQ(MESSAGE_STATUS_SUCCESS,
+ creator_.AddMessageFrame(
+ 2, MemSliceFromString(std::string(
+ creator_.GetCurrentLargestMessagePayload(), 'a'))));
EXPECT_TRUE(creator_.HasPendingRetransmittableFrames());
// Failed to send messages which cannot fit into one packet.
- EXPECT_EQ(
- MESSAGE_STATUS_TOO_LARGE,
- creator_.AddMessageFrame(
- 3, MakeSpan(&allocator_,
- std::string(
- creator_.GetCurrentLargestMessagePayload() + 10, 'a'),
- &storage)));
+ EXPECT_EQ(MESSAGE_STATUS_TOO_LARGE,
+ creator_.AddMessageFrame(
+ 3, MemSliceFromString(std::string(
+ creator_.GetCurrentLargestMessagePayload() + 10, 'a'))));
}
TEST_F(QuicPacketCreatorMultiplePacketsTest, ConnectionId) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.h b/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.h
index ccd3a6cbcaf..9ea1554efa4 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.h
@@ -7,6 +7,7 @@
#include <ostream>
+#include "absl/container/inlined_vector.h"
#include "quic/core/crypto/quic_random.h"
#include "quic/core/quic_alarm.h"
#include "quic/core/quic_alarm_factory.h"
@@ -145,7 +146,7 @@ class QUIC_EXPORT_PRIVATE QuicPathValidator {
void ResetPathValidation();
// Has at most 3 entries due to validation timeout.
- QuicInlinedVector<QuicPathFrameBuffer, 3> probing_data_;
+ absl::InlinedVector<QuicPathFrameBuffer, 3> probing_data_;
SendDelegate* send_delegate_;
QuicRandom* random_;
std::unique_ptr<QuicPathValidationContext> path_context_;
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc
index 53d9f0eca22..fd1190bde7b 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc
@@ -24,7 +24,7 @@
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
-#include "quic/platform/api/quic_map_util.h"
+#include "common/print_elements.h"
namespace quic {
@@ -117,7 +117,8 @@ QuicSentPacketManager::QuicSentPacketManager(
use_standard_deviation_for_pto_(false),
pto_multiplier_without_rtt_samples_(3),
num_ptos_for_path_degrading_(0),
- ignore_pings_(false) {
+ ignore_pings_(false),
+ ignore_ack_delay_(false) {
SetSendAlgorithm(congestion_control_type);
if (pto_enabled_) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_default_on_pto, 1, 2);
@@ -158,7 +159,7 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) {
}
}
if (config.HasClientSentConnectionOption(kMAD0, perspective)) {
- rtt_stats_.set_ignore_max_ack_delay(true);
+ ignore_ack_delay_ = true;
}
if (config.HasClientSentConnectionOption(kMAD2, perspective)) {
// Set the minimum to the alarm granularity.
@@ -1526,8 +1527,18 @@ void QuicSentPacketManager::OnAckFrameStart(QuicPacketNumber largest_acked,
QuicTime ack_receive_time) {
QUICHE_DCHECK(packets_acked_.empty());
QUICHE_DCHECK_LE(largest_acked, unacked_packets_.largest_sent_packet());
- if (ack_delay_time > peer_max_ack_delay()) {
- ack_delay_time = peer_max_ack_delay();
+ if (GetQuicReloadableFlag(quic_ignore_peer_max_ack_delay_during_handshake) &&
+ supports_multiple_packet_number_spaces() && !handshake_finished_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_ignore_peer_max_ack_delay_during_handshake);
+ // Ignore peer_max_ack_delay and use received ack_delay during
+ // handshake.
+ } else {
+ if (ack_delay_time > peer_max_ack_delay()) {
+ ack_delay_time = peer_max_ack_delay();
+ }
+ if (ignore_ack_delay_) {
+ ack_delay_time = QuicTime::Delta::Zero();
+ }
}
rtt_updated_ =
MaybeUpdateRTT(largest_acked, ack_delay_time, ack_receive_time);
@@ -1601,7 +1612,7 @@ AckResult QuicSentPacketManager::OnAckFrameEnd(
<< acked_packet.packet_number
<< ", last_ack_frame_: " << last_ack_frame_
<< ", least_unacked: " << unacked_packets_.GetLeastUnacked()
- << ", packets_acked_: " << packets_acked_;
+ << ", packets_acked_: " << quiche::PrintElements(packets_acked_);
} else {
QUIC_PEER_BUG(quic_peer_bug_10750_6)
<< "Received " << ack_decrypted_level
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h
index 781f6f95b90..eeb474f9f04 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h
@@ -745,6 +745,9 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager {
// If true, do not use PING only packets for RTT measurement or congestion
// control.
bool ignore_pings_;
+
+ // Whether to ignore the ack_delay in received ACKs.
+ bool ignore_ack_delay_;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc
index 388a662096e..e457e09884f 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc
@@ -4637,8 +4637,7 @@ TEST_F(QuicSentPacketManagerTest, ClearDataInMessageFrameAfterPacketSent) {
QuicMessageFrame* message_frame = nullptr;
{
QuicMemSlice slice(MakeUniqueBuffer(&allocator_, 1024), 1024);
- message_frame =
- new QuicMessageFrame(/*message_id=*/1, QuicMemSliceSpan(&slice));
+ message_frame = new QuicMessageFrame(/*message_id=*/1, std::move(slice));
EXPECT_FALSE(message_frame->message_data.empty());
EXPECT_EQ(message_frame->message_length, 1024);
@@ -4684,6 +4683,125 @@ TEST_F(QuicSentPacketManagerTest, BuildAckFrequencyFrame) {
EXPECT_EQ(frame.packet_tolerance, 10u);
}
+TEST_F(QuicSentPacketManagerTest, SmoothedRttIgnoreAckDelay) {
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kMAD0);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillRepeatedly(Return(10 * kDefaultTCPMSS));
+ manager_.SetFromConfig(config);
+
+ SendDataPacket(1);
+ // Ack 1.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(300));
+ ExpectAck(1);
+ manager_.OnAckFrameStart(QuicPacketNumber(1),
+ QuicTime::Delta::FromMilliseconds(100),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1),
+ ENCRYPTION_INITIAL));
+ // Verify that ack_delay is ignored in the first measurement.
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300),
+ manager_.GetRttStats()->latest_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300),
+ manager_.GetRttStats()->smoothed_rtt());
+
+ SendDataPacket(2);
+ // Ack 2.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(300));
+ ExpectAck(2);
+ manager_.OnAckFrameStart(QuicPacketNumber(2),
+ QuicTime::Delta::FromMilliseconds(100),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(2),
+ ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300),
+ manager_.GetRttStats()->latest_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300),
+ manager_.GetRttStats()->smoothed_rtt());
+
+ SendDataPacket(3);
+ // Ack 3.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(300));
+ ExpectAck(3);
+ manager_.OnAckFrameStart(QuicPacketNumber(3),
+ QuicTime::Delta::FromMilliseconds(50), clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(3),
+ ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300),
+ manager_.GetRttStats()->latest_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(300),
+ manager_.GetRttStats()->smoothed_rtt());
+
+ SendDataPacket(4);
+ // Ack 4.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(200));
+ ExpectAck(4);
+ manager_.OnAckFrameStart(QuicPacketNumber(4),
+ QuicTime::Delta::FromMilliseconds(300),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(5));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(4),
+ ENCRYPTION_INITIAL));
+ // Verify that large erroneous ack_delay does not change Smoothed RTT.
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200),
+ manager_.GetRttStats()->latest_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(287500),
+ manager_.GetRttStats()->smoothed_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, IgnorePeerMaxAckDelayDuringHandshake) {
+ manager_.EnableMultiplePacketNumberSpacesSupport();
+ // 100ms RTT.
+ const QuicTime::Delta kTestRTT = QuicTime::Delta::FromMilliseconds(100);
+
+ // Server sends INITIAL 1 and HANDSHAKE 2.
+ SendDataPacket(1, ENCRYPTION_INITIAL);
+ SendDataPacket(2, ENCRYPTION_HANDSHAKE);
+
+ // Receive client ACK for INITIAL 1 after one RTT.
+ clock_.AdvanceTime(kTestRTT);
+ ExpectAck(1);
+ manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1),
+ ENCRYPTION_INITIAL));
+ EXPECT_EQ(kTestRTT, manager_.GetRttStats()->latest_rtt());
+
+ // Assume the cert verification on client takes 50ms, such that the HANDSHAKE
+ // packet is queued for 50ms.
+ const QuicTime::Delta queuing_delay = QuicTime::Delta::FromMilliseconds(50);
+ clock_.AdvanceTime(queuing_delay);
+ // Ack 2.
+ ExpectAck(2);
+ manager_.OnAckFrameStart(QuicPacketNumber(2), queuing_delay, clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(2),
+ ENCRYPTION_HANDSHAKE));
+ if (GetQuicReloadableFlag(quic_ignore_peer_max_ack_delay_during_handshake)) {
+ EXPECT_EQ(kTestRTT, manager_.GetRttStats()->latest_rtt());
+ } else {
+ // Verify the ack_delay gets capped by the peer_max_ack_delay.
+ EXPECT_EQ(kTestRTT + queuing_delay -
+ QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs),
+ manager_.GetRttStats()->latest_rtt());
+ }
+}
+
TEST_F(QuicSentPacketManagerTest, BuildAckFrequencyFrameWithSRTT) {
SetQuicReloadableFlag(quic_can_send_ack_frequency, true);
EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_server_id.cc b/chromium/net/third_party/quiche/src/quic/core/quic_server_id.cc
index 1d2a2adb8c9..0c034514ef4 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_server_id.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_server_id.cc
@@ -7,8 +7,6 @@
#include <string>
#include <tuple>
-#include "quic/platform/api/quic_estimate_memory_usage.h"
-
namespace quic {
QuicServerId::QuicServerId() : QuicServerId("", 0, false) {}
@@ -33,8 +31,8 @@ bool QuicServerId::operator==(const QuicServerId& other) const {
host_ == other.host_ && port_ == other.port_;
}
-size_t QuicServerId::EstimateMemoryUsage() const {
- return QuicEstimateMemoryUsage(host_);
+bool QuicServerId::operator!=(const QuicServerId& other) const {
+ return !(*this == other);
}
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_server_id.h b/chromium/net/third_party/quiche/src/quic/core/quic_server_id.h
index 01320014585..fa6b3c3cfdb 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_server_id.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_server_id.h
@@ -23,18 +23,18 @@ class QUIC_EXPORT_PRIVATE QuicServerId {
bool privacy_mode_enabled);
~QuicServerId();
- // Needed to be an element of std::set.
+ // Needed to be an element of an ordered container.
bool operator<(const QuicServerId& other) const;
bool operator==(const QuicServerId& other) const;
+ bool operator!=(const QuicServerId& other) const;
+
const std::string& host() const { return host_; }
uint16_t port() const { return port_; }
bool privacy_mode_enabled() const { return privacy_mode_enabled_; }
- size_t EstimateMemoryUsage() const;
-
private:
std::string host_;
uint16_t port_;
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_server_id_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_server_id_test.cc
index 67231abf866..1ad425abbd8 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_server_id_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_server_id_test.cc
@@ -6,7 +6,6 @@
#include <string>
-#include "quic/platform/api/quic_estimate_memory_usage.h"
#include "quic/platform/api/quic_test.h"
namespace quic {
@@ -87,6 +86,10 @@ TEST_F(QuicServerIdTest, Equals) {
QuicServerId b_10_https_right_private("b.com", 10, right_privacy);
QuicServerId b_11_https_right_private("b.com", 11, right_privacy);
+ EXPECT_NE(a_10_https_right_private, a_11_https_right_private);
+ EXPECT_NE(a_10_https_right_private, b_10_https_right_private);
+ EXPECT_NE(a_10_https_right_private, b_11_https_right_private);
+
QuicServerId new_a_10_https_left_private("a.com", 10, left_privacy);
QuicServerId new_a_11_https_left_private("a.com", 11, left_privacy);
QuicServerId new_b_10_https_left_private("b.com", 10, left_privacy);
@@ -107,21 +110,13 @@ TEST_F(QuicServerIdTest, Equals) {
QuicServerId new_a_10_https_left_private("a.com", 10, false);
- EXPECT_FALSE(new_a_10_https_left_private == a_11_https_right_private);
- EXPECT_FALSE(new_a_10_https_left_private == b_10_https_right_private);
- EXPECT_FALSE(new_a_10_https_left_private == b_11_https_right_private);
+ EXPECT_NE(new_a_10_https_left_private, a_11_https_right_private);
+ EXPECT_NE(new_a_10_https_left_private, b_10_https_right_private);
+ EXPECT_NE(new_a_10_https_left_private, b_11_https_right_private);
}
QuicServerId a_10_https_private("a.com", 10, true);
QuicServerId new_a_10_https_no_private("a.com", 10, false);
- EXPECT_FALSE(new_a_10_https_no_private == a_10_https_private);
-}
-
-TEST_F(QuicServerIdTest, EstimateMemoryUsage) {
- std::string host = "this is a rather very quite long hostname";
- uint16_t port = 10;
- bool privacy_mode_enabled = true;
- QuicServerId server_id(host, port, privacy_mode_enabled);
- EXPECT_EQ(QuicEstimateMemoryUsage(host), QuicEstimateMemoryUsage(server_id));
+ EXPECT_NE(new_a_10_https_no_private, a_10_https_private);
}
} // namespace
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_session.cc b/chromium/net/third_party/quiche/src/quic/core/quic_session.cc
index 1bcb9dcb7bd..aef3fc255a8 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_session.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_session.cc
@@ -13,6 +13,7 @@
#include "absl/strings/string_view.h"
#include "quic/core/frames/quic_ack_frequency_frame.h"
#include "quic/core/quic_connection.h"
+#include "quic/core/quic_connection_context.h"
#include "quic/core/quic_error_codes.h"
#include "quic/core/quic_flow_controller.h"
#include "quic/core/quic_types.h"
@@ -22,7 +23,6 @@
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/platform/api/quic_server_stats.h"
#include "quic/platform/api/quic_stack_trace.h"
#include "common/quiche_text_utils.h"
@@ -135,11 +135,15 @@ void QuicSession::Initialize() {
connection_->SetDataProducer(this);
connection_->SetUnackedMapInitialCapacity();
connection_->SetFromConfig(config_);
- if (perspective_ == Perspective::IS_CLIENT &&
- config_.HasClientRequestedIndependentOption(kAFFE, perspective_) &&
- version().HasIetfQuicFrames()) {
- connection_->set_can_receive_ack_frequency_frame();
- config_.SetMinAckDelayMs(kDefaultMinAckDelayTimeMs);
+ if (perspective_ == Perspective::IS_CLIENT) {
+ if (config_.HasClientRequestedIndependentOption(kAFFE, perspective_) &&
+ version().HasIetfQuicFrames()) {
+ connection_->set_can_receive_ack_frequency_frame();
+ config_.SetMinAckDelayMs(kDefaultMinAckDelayTimeMs);
+ }
+ if (config_.HasClientRequestedIndependentOption(kBPTE, perspective_)) {
+ permutes_tls_extensions_ = true;
+ }
}
connection_->CreateConnectionIdManager();
@@ -162,7 +166,12 @@ void QuicSession::Initialize() {
GetMutableCryptoStream()->id());
}
-QuicSession::~QuicSession() {}
+QuicSession::~QuicSession() {
+ if (GetQuicRestartFlag(quic_alarm_add_permanent_cancel) &&
+ closed_streams_clean_up_alarm_ != nullptr) {
+ closed_streams_clean_up_alarm_->PermanentCancel();
+ }
+}
void QuicSession::PendingStreamOnStreamFrame(const QuicStreamFrame& frame) {
QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
@@ -563,17 +572,14 @@ bool QuicSession::CheckStreamWriteBlocked(QuicStream* stream) const {
}
void QuicSession::OnCanWrite() {
- if (connection_->donot_write_mid_packet_processing()) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_donot_write_mid_packet_processing, 1, 3);
- if (connection_->framer().is_processing_packet()) {
- // Do not write data in the middle of packet processing because rest
- // frames in the packet may change the data to write. For example, lost
- // data could be acknowledged. Also, connection is going to emit
- // OnCanWrite signal post packet processing.
- QUIC_BUG(session_write_mid_packet_processing)
- << ENDPOINT << "Try to write mid packet processing.";
- return;
- }
+ if (connection_->framer().is_processing_packet()) {
+ // Do not write data in the middle of packet processing because rest
+ // frames in the packet may change the data to write. For example, lost
+ // data could be acknowledged. Also, connection is going to emit
+ // OnCanWrite signal post packet processing.
+ QUIC_BUG(session_write_mid_packet_processing)
+ << ENDPOINT << "Try to write mid packet processing.";
+ return;
}
if (!RetransmitLostData()) {
// Cannot finish retransmitting lost data, connection is write blocked.
@@ -739,8 +745,8 @@ bool QuicSession::HasPendingHandshake() const {
return GetCryptoStream()->HasPendingCryptoRetransmission() ||
GetCryptoStream()->HasBufferedCryptoFrames();
}
- return QuicContainsKey(streams_with_pending_retransmission_,
- QuicUtils::GetCryptoStreamId(transport_version())) ||
+ return streams_with_pending_retransmission_.contains(
+ QuicUtils::GetCryptoStreamId(transport_version())) ||
write_blocked_streams_.IsStreamBlocked(
QuicUtils::GetCryptoStreamId(transport_version()));
}
@@ -748,19 +754,17 @@ bool QuicSession::HasPendingHandshake() const {
void QuicSession::ProcessUdpPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) {
+ QuicConnectionContextSwitcher cs(connection_->context());
connection_->ProcessUdpPacket(self_address, peer_address, packet);
}
-QuicConsumedData QuicSession::WritevData(
- QuicStreamId id,
- size_t write_length,
- QuicStreamOffset offset,
- StreamSendingState state,
- TransmissionType type,
- absl::optional<EncryptionLevel> level) {
+QuicConsumedData QuicSession::WritevData(QuicStreamId id, size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state,
+ TransmissionType type,
+ EncryptionLevel level) {
QUICHE_DCHECK(connection_->connected())
<< ENDPOINT << "Try to write stream data when connection is closed.";
- QUICHE_DCHECK(!use_write_or_buffer_data_at_level_ || level.has_value());
if (!IsEncryptionEstablished() &&
!QuicUtils::IsCryptoStreamId(transport_version(), id)) {
// Do not let streams write without encryption. The calling stream will end
@@ -768,11 +772,10 @@ QuicConsumedData QuicSession::WritevData(
if (was_zero_rtt_rejected_ && !OneRttKeysAvailable()) {
QUICHE_DCHECK(version().UsesTls() &&
perspective() == Perspective::IS_CLIENT);
- QUIC_BUG_IF(quic_bug_12435_3, type == NOT_RETRANSMISSION)
- << ENDPOINT << "Try to send new data on stream " << id
- << "before 1-RTT keys are available while 0-RTT is rejected. "
- "Version: "
- << ParsedQuicVersionToString(version());
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Suppress the write while 0-RTT gets rejected and "
+ "1-RTT keys are not available. Version: "
+ << ParsedQuicVersionToString(version());
} else if (version().UsesTls() || perspective() == Perspective::IS_SERVER) {
QUIC_BUG(quic_bug_10866_2)
<< ENDPOINT << "Try to send data of stream " << id
@@ -793,15 +796,7 @@ QuicConsumedData QuicSession::WritevData(
}
SetTransmissionType(type);
- const auto current_level = connection()->encryption_level();
- if (!use_encryption_level_context()) {
- if (level.has_value()) {
- connection()->SetDefaultEncryptionLevel(level.value());
- }
- }
- QuicConnection::ScopedEncryptionLevelContext context(
- use_encryption_level_context() ? connection() : nullptr,
- use_encryption_level_context() ? level.value() : NUM_ENCRYPTION_LEVELS);
+ QuicConnection::ScopedEncryptionLevelContext context(connection(), level);
QuicConsumedData data =
connection_->SendStreamData(id, write_length, offset, state);
@@ -810,14 +805,6 @@ QuicConsumedData QuicSession::WritevData(
write_blocked_streams_.UpdateBytesForStream(id, data.bytes_consumed);
}
- // Restore the encryption level.
- if (!use_encryption_level_context()) {
- // Restore the encryption level.
- if (level.has_value()) {
- connection()->SetDefaultEncryptionLevel(current_level);
- }
- }
-
return data;
}
@@ -837,18 +824,9 @@ size_t QuicSession::SendCryptoData(EncryptionLevel level,
return 0;
}
SetTransmissionType(type);
- const auto current_level = connection()->encryption_level();
- if (!use_encryption_level_context()) {
- connection_->SetDefaultEncryptionLevel(level);
- }
- QuicConnection::ScopedEncryptionLevelContext context(
- use_encryption_level_context() ? connection() : nullptr, level);
+ QuicConnection::ScopedEncryptionLevelContext context(connection(), level);
const auto bytes_consumed =
connection_->SendCryptoData(level, write_length, offset);
- if (!use_encryption_level_context()) {
- // Restores encryption level.
- connection_->SetDefaultEncryptionLevel(current_level);
- }
return bytes_consumed;
}
@@ -863,21 +841,13 @@ bool QuicSession::WriteControlFrame(const QuicFrame& frame,
TransmissionType type) {
QUICHE_DCHECK(connection()->connected())
<< ENDPOINT << "Try to write control frames when connection is closed.";
- if (connection_->encrypted_control_frames()) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_encrypted_control_frames);
- if (!IsEncryptionEstablished()) {
- QUIC_BUG(quic_bug_10866_4)
- << ENDPOINT << "Tried to send control frame " << frame
- << " before encryption is established. Last decrypted level: "
- << EncryptionLevelToString(connection_->last_decrypted_level());
- return false;
- }
+ if (!IsEncryptionEstablished()) {
+ // Suppress the write before encryption gets established.
+ return false;
}
SetTransmissionType(type);
QuicConnection::ScopedEncryptionLevelContext context(
- use_encryption_level_context() ? connection() : nullptr,
- use_encryption_level_context() ? GetEncryptionLevelToSendApplicationData()
- : NUM_ENCRYPTION_LEVELS);
+ connection(), GetEncryptionLevelToSendApplicationData());
return connection_->SendControlFrame(frame);
}
@@ -1329,8 +1299,7 @@ void QuicSession::OnConfigNegotiated() {
// Or if this session is configured on TLS enabled QUIC versions,
// attempt to retransmit 0-RTT data if there's any.
// TODO(fayang): consider removing this OnCanWrite call.
- if ((!connection_->donot_write_mid_packet_processing() ||
- !connection_->framer().is_processing_packet()) &&
+ if (!connection_->framer().is_processing_packet() &&
(connection_->version().AllowsLowFlowControlLimits() ||
version().UsesTls())) {
QUIC_CODE_COUNT(quic_session_on_can_write_on_config_negotiated);
@@ -1637,8 +1606,7 @@ void QuicSession::SetDefaultEncryptionLevel(EncryptionLevel level) {
// Retransmit old 0-RTT data (if any) with the new 0-RTT keys, since
// they can't be decrypted by the server.
connection_->MarkZeroRttPacketsForRetransmission(0);
- if (!connection_->donot_write_mid_packet_processing() ||
- !connection_->framer().is_processing_packet()) {
+ if (!connection_->framer().is_processing_packet()) {
// TODO(fayang): consider removing this OnCanWrite call.
// Given any streams blocked by encryption a chance to write.
QUIC_CODE_COUNT(
@@ -1817,7 +1785,7 @@ void QuicSession::ActivateStream(std::unique_ptr<QuicStream> stream) {
bool is_static = stream->is_static();
QUIC_DVLOG(1) << ENDPOINT << "num_streams: " << stream_map_.size()
<< ". activating stream " << stream_id;
- QUICHE_DCHECK(!QuicContainsKey(stream_map_, stream_id));
+ QUICHE_DCHECK(!stream_map_.contains(stream_id));
stream_map_[stream_id] = std::move(stream);
if (is_static) {
++num_static_streams_;
@@ -1897,7 +1865,7 @@ QuicStreamCount QuicSession::GetAdvertisedMaxIncomingBidirectionalStreams()
}
QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) {
- QUICHE_DCHECK(!QuicContainsKey(pending_stream_map_, stream_id));
+ QUICHE_DCHECK(!pending_stream_map_.contains(stream_id));
if (QuicUtils::IsCryptoStreamId(transport_version(), stream_id)) {
return GetMutableCryptoStream();
}
@@ -1936,7 +1904,7 @@ QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) {
}
void QuicSession::StreamDraining(QuicStreamId stream_id, bool unidirectional) {
- QUICHE_DCHECK(QuicContainsKey(stream_map_, stream_id));
+ QUICHE_DCHECK(stream_map_.contains(stream_id));
QUIC_DVLOG(1) << ENDPOINT << "Stream " << stream_id << " is draining";
if (VersionHasIetfQuicFrames(transport_version())) {
ietf_streamid_manager_.OnStreamClosed(stream_id);
@@ -2052,7 +2020,7 @@ bool QuicSession::IsOpenStream(QuicStreamId id) {
if (it != stream_map_.end()) {
return !it->second->IsZombie();
}
- if (QuicContainsKey(pending_stream_map_, id) ||
+ if (pending_stream_map_.contains(id) ||
QuicUtils::IsCryptoStreamId(transport_version(), id)) {
// Stream is active
return true;
@@ -2106,14 +2074,16 @@ void QuicSession::SendAckFrequency(const QuicAckFrequencyFrame& frame) {
}
void QuicSession::SendNewConnectionId(const QuicNewConnectionIdFrame& frame) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 1, 5);
+ // Count NEW_CONNECTION_ID frames sent to client.
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 1, 6);
control_frame_manager_.WriteOrBufferNewConnectionId(
frame.connection_id, frame.sequence_number, frame.retire_prior_to,
frame.stateless_reset_token);
}
void QuicSession::SendRetireConnectionId(uint64_t sequence_number) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 2, 5);
+ // Count RETIRE_CONNECTION_ID frames sent to client.
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 2, 6);
control_frame_manager_.WriteOrBufferRetireConnectionId(sequence_number);
}
@@ -2272,8 +2242,8 @@ void QuicSession::OnFrameLost(const QuicFrame& frame) {
frame.stream_frame.data_length,
frame.stream_frame.fin);
if (stream->HasPendingRetransmission() &&
- !QuicContainsKey(streams_with_pending_retransmission_,
- frame.stream_frame.stream_id)) {
+ !streams_with_pending_retransmission_.contains(
+ frame.stream_frame.stream_id)) {
streams_with_pending_retransmission_.insert(
std::make_pair(frame.stream_frame.stream_id, true));
}
@@ -2397,8 +2367,8 @@ bool QuicSession::RetransmitLostData() {
}
// Retransmit crypto data in stream 1 frames (version < 47).
if (!uses_crypto_frames &&
- QuicContainsKey(streams_with_pending_retransmission_,
- QuicUtils::GetCryptoStreamId(transport_version()))) {
+ streams_with_pending_retransmission_.contains(
+ QuicUtils::GetCryptoStreamId(transport_version()))) {
// Retransmit crypto data first.
QuicStream* crypto_stream =
GetStream(QuicUtils::GetCryptoStreamId(transport_version()));
@@ -2463,20 +2433,23 @@ void QuicSession::SetTransmissionType(TransmissionType type) {
connection_->SetTransmissionType(type);
}
-MessageResult QuicSession::SendMessage(QuicMemSliceSpan message) {
+MessageResult QuicSession::SendMessage(absl::Span<QuicMemSlice> message) {
return SendMessage(message, /*flush=*/false);
}
-MessageResult QuicSession::SendMessage(QuicMemSliceSpan message, bool flush) {
+MessageResult QuicSession::SendMessage(QuicMemSlice message) {
+ return SendMessage(absl::MakeSpan(&message, 1), /*flush=*/false);
+}
+
+MessageResult QuicSession::SendMessage(absl::Span<QuicMemSlice> message,
+ bool flush) {
QUICHE_DCHECK(connection_->connected())
<< ENDPOINT << "Try to write messages when connection is closed.";
if (!IsEncryptionEstablished()) {
return {MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, 0};
}
QuicConnection::ScopedEncryptionLevelContext context(
- use_encryption_level_context() ? connection() : nullptr,
- use_encryption_level_context() ? GetEncryptionLevelToSendApplicationData()
- : NUM_ENCRYPTION_LEVELS);
+ connection(), GetEncryptionLevelToSendApplicationData());
MessageStatus result =
connection_->SendMessage(last_message_id_ + 1, message, flush);
if (result == MESSAGE_STATUS_SUCCESS) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_session.h b/chromium/net/third_party/quiche/src/quic/core/quic_session.h
index b6c1367019f..9eb7bd1614f 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_session.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_session.h
@@ -17,6 +17,8 @@
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
+#include "absl/types/span.h"
+#include "quic/core/crypto/tls_connection.h"
#include "quic/core/frames/quic_ack_frequency_frame.h"
#include "quic/core/handshaker_delegate_interface.h"
#include "quic/core/legacy_quic_stream_id_manager.h"
@@ -35,10 +37,11 @@
#include "quic/core/session_notifier_interface.h"
#include "quic/core/stream_delegate_interface.h"
#include "quic/core/uber_quic_stream_id_manager.h"
-#include "quic/platform/api/quic_containers.h"
#include "quic/platform/api/quic_export.h"
#include "quic/platform/api/quic_flags.h"
+#include "quic/platform/api/quic_mem_slice.h"
#include "quic/platform/api/quic_socket_address.h"
+#include "common/quiche_linked_hash_map.h"
namespace quic {
@@ -208,31 +211,39 @@ class QUIC_EXPORT_PRIVATE QuicSession
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet);
- // Called by application to send |message|. Data copy can be avoided if
- // |message| is provided in reference counted memory.
- // Please note, |message| provided in reference counted memory would be moved
- // internally when message is successfully sent. Thereafter, it would be
- // undefined behavior if callers try to access the slices through their own
- // copy of the span object.
- // Returns the message result which includes the message status and message ID
- // (valid if the write succeeds). SendMessage flushes a message packet even it
- // is not full. If the application wants to bundle other data in the same
- // packet, please consider adding a packet flusher around the SendMessage
- // and/or WritevData calls.
+ // Sends |message| as a QUIC DATAGRAM frame (QUIC MESSAGE frame in gQUIC).
+ // See <https://datatracker.ietf.org/doc/html/draft-ietf-quic-datagram> for
+ // more details.
//
- // OnMessageAcked and OnMessageLost are called when a particular message gets
- // acked or lost.
+ // Returns a MessageResult struct which includes the status of the write
+ // operation and a message ID. The message ID (not sent on the wire) can be
+ // used to track the message; OnMessageAcked and OnMessageLost are called when
+ // a specific message gets acked or lost.
+ //
+ // If the write operation is successful, all of the slices in |message| are
+ // consumed, leaving them empty. If MESSAGE_STATUS_INTERNAL_ERROR is
+ // returned, the slices in question may or may not be consumed; it is no
+ // longer safe to access those. For all other status codes, |message| is kept
+ // intact.
//
// Note that SendMessage will fail with status = MESSAGE_STATUS_BLOCKED
- // if connection is congestion control blocked or underlying socket is write
- // blocked. In this case the caller can retry sending message again when
+ // if the connection is congestion control blocked or the underlying socket is
+ // write blocked. In this case the caller can retry sending message again when
// connection becomes available, for example after getting OnCanWrite()
// callback.
- MessageResult SendMessage(QuicMemSliceSpan message);
+ //
+ // SendMessage flushes the current packet even it is not full; if the
+ // application needs to bundle other data in the same packet, consider using
+ // QuicConnection::ScopedPacketFlusher around the relevant write operations.
+ MessageResult SendMessage(absl::Span<QuicMemSlice> message);
// Same as above SendMessage, except caller can specify if the given |message|
// should be flushed even if the underlying connection is deemed unwritable.
- MessageResult SendMessage(QuicMemSliceSpan message, bool flush);
+ MessageResult SendMessage(absl::Span<QuicMemSlice> message, bool flush);
+
+ // Single-slice version of SendMessage(). Unlike the version above, this
+ // version always takes ownership of the slice.
+ MessageResult SendMessage(QuicMemSlice message);
// Called when message with |message_id| gets acked.
virtual void OnMessageAcked(QuicMessageId message_id,
@@ -327,12 +338,10 @@ class QUIC_EXPORT_PRIVATE QuicSession
// indicating if the fin bit was consumed. This does not indicate the data
// has been sent on the wire: it may have been turned into a packet and queued
// if the socket was unexpectedly blocked.
- QuicConsumedData WritevData(QuicStreamId id,
- size_t write_length,
- QuicStreamOffset offset,
- StreamSendingState state,
+ QuicConsumedData WritevData(QuicStreamId id, size_t write_length,
+ QuicStreamOffset offset, StreamSendingState state,
TransmissionType type,
- absl::optional<EncryptionLevel> level) override;
+ EncryptionLevel level) override;
size_t SendCryptoData(EncryptionLevel level,
size_t write_length,
@@ -610,14 +619,9 @@ class QUIC_EXPORT_PRIVATE QuicSession
return liveness_testing_in_progress_;
}
- bool use_write_or_buffer_data_at_level() const {
- return use_write_or_buffer_data_at_level_;
- }
+ bool permutes_tls_extensions() const { return permutes_tls_extensions_; }
- bool use_encryption_level_context() const {
- return connection_->use_encryption_level_context() &&
- use_write_or_buffer_data_at_level_;
- }
+ virtual QuicSSLConfig GetSSLConfig() const { return QuicSSLConfig(); }
protected:
using StreamMap =
@@ -925,7 +929,8 @@ class QUIC_EXPORT_PRIVATE QuicSession
// TODO(fayang): switch to linked_hash_set when chromium supports it. The bool
// is not used here.
// List of streams with pending retransmissions.
- QuicLinkedHashMap<QuicStreamId, bool> streams_with_pending_retransmission_;
+ quiche::QuicheLinkedHashMap<QuicStreamId, bool>
+ streams_with_pending_retransmission_;
// Clean up closed_streams_ when this alarm fires.
std::unique_ptr<QuicAlarm> closed_streams_clean_up_alarm_;
@@ -947,8 +952,8 @@ class QUIC_EXPORT_PRIVATE QuicSession
// creation of new outgoing bidirectional streams.
bool liveness_testing_in_progress_;
- const bool use_write_or_buffer_data_at_level_ =
- GetQuicReloadableFlag(quic_use_write_or_buffer_data_at_level);
+ // Whether BoringSSL randomizes the order of TLS extensions.
+ bool permutes_tls_extensions_ = false;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc
index 81282fe9eeb..84b9e2edc7a 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc
@@ -27,7 +27,6 @@
#include "quic/core/quic_versions.h"
#include "quic/platform/api/quic_expect_bug.h"
#include "quic/platform/api/quic_flags.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/platform/api/quic_mem_slice_storage.h"
#include "quic/platform/api/quic_test.h"
#include "quic/platform/api/quic_test_mem_slice_vector.h"
@@ -314,12 +313,10 @@ class TestSession : public QuicSession {
return GetNumActiveStreams() > 0;
}
- QuicConsumedData WritevData(QuicStreamId id,
- size_t write_length,
- QuicStreamOffset offset,
- StreamSendingState state,
+ QuicConsumedData WritevData(QuicStreamId id, size_t write_length,
+ QuicStreamOffset offset, StreamSendingState state,
TransmissionType type,
- absl::optional<EncryptionLevel> level) override {
+ EncryptionLevel level) override {
bool fin = state != NO_FIN;
QuicConsumedData consumed(write_length, fin);
if (!writev_consumes_all_data_) {
@@ -452,7 +449,7 @@ class QuicSessionTestBase : public QuicTestWithParam<ParsedQuicVersion> {
QuicUtils::GetCryptoStreamId(connection_->transport_version());
}
for (QuicStreamId i = first_stream_id; i < 100; i++) {
- if (!QuicContainsKey(closed_streams_, i)) {
+ if (closed_streams_.find(i) == closed_streams_.end()) {
EXPECT_FALSE(session_.IsClosedStream(i)) << " stream id: " << i;
} else {
EXPECT_TRUE(session_.IsClosedStream(i)) << " stream id: " << i;
@@ -1529,56 +1526,6 @@ TEST_P(QuicSessionTestServer, HandshakeUnblocksFlowControlBlockedStream) {
EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
}
-TEST_P(QuicSessionTestServer, HandshakeUnblocksFlowControlBlockedCryptoStream) {
- if (QuicVersionUsesCryptoFrames(GetParam().transport_version) ||
- connection_->encrypted_control_frames()) {
- // QUIC version 47 onwards uses CRYPTO frames for the handshake, so this
- // test doesn't make sense for those versions since CRYPTO frames aren't
- // flow controlled.
- return;
- }
- // Test that if the crypto stream is flow control blocked, then if the SHLO
- // contains a larger send window offset, the stream becomes unblocked.
- session_.set_writev_consumes_all_data(true);
- TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
- EXPECT_FALSE(crypto_stream->IsFlowControlBlocked());
- EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
- EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
- EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
- EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
- EXPECT_CALL(*connection_, SendControlFrame(_))
- .WillOnce(Invoke(&ClearControlFrame));
- for (QuicStreamId i = 0; !crypto_stream->IsFlowControlBlocked() && i < 1000u;
- i++) {
- EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
- EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
- QuicStreamOffset offset = crypto_stream->stream_bytes_written();
- QuicConfig config;
- CryptoHandshakeMessage crypto_message;
- config.ToHandshakeMessage(&crypto_message, transport_version());
- crypto_stream->SendHandshakeMessage(crypto_message, ENCRYPTION_INITIAL);
- char buf[1000];
- QuicDataWriter writer(1000, buf, quiche::NETWORK_BYTE_ORDER);
- crypto_stream->WriteStreamData(offset, crypto_message.size(), &writer);
- }
- EXPECT_TRUE(crypto_stream->IsFlowControlBlocked());
- EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
- EXPECT_TRUE(session_.IsStreamFlowControlBlocked());
- EXPECT_FALSE(session_.HasDataToWrite());
- EXPECT_TRUE(crypto_stream->HasBufferedData());
-
- // Now complete the crypto handshake, resulting in an increased flow control
- // send window.
- CompleteHandshake();
- EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked(
- &session_,
- QuicUtils::GetCryptoStreamId(connection_->transport_version())));
- // Stream is now unblocked and will no longer have buffered data.
- EXPECT_FALSE(crypto_stream->IsFlowControlBlocked());
- EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
- EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
-}
-
TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingRstOutOfOrder) {
CompleteHandshake();
// Test that when we receive an out of order stream RST we correctly adjust
@@ -2451,36 +2398,26 @@ TEST_P(QuicSessionTestServer, RetransmitLostDataCausesConnectionClose) {
TEST_P(QuicSessionTestServer, SendMessage) {
// Cannot send message when encryption is not established.
EXPECT_FALSE(session_.OneRttKeysAvailable());
- quic::QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
EXPECT_EQ(MessageResult(MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, 0),
- session_.SendMessage(
- MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
- "", &storage)));
+ session_.SendMessage(MemSliceFromString("")));
CompleteHandshake();
EXPECT_TRUE(session_.OneRttKeysAvailable());
- absl::string_view message;
EXPECT_CALL(*connection_, SendMessage(1, _, false))
.WillOnce(Return(MESSAGE_STATUS_SUCCESS));
EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 1),
- session_.SendMessage(
- MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
- message, &storage)));
+ session_.SendMessage(MemSliceFromString("")));
// Verify message_id increases.
EXPECT_CALL(*connection_, SendMessage(2, _, false))
.WillOnce(Return(MESSAGE_STATUS_TOO_LARGE));
EXPECT_EQ(MessageResult(MESSAGE_STATUS_TOO_LARGE, 0),
- session_.SendMessage(
- MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
- message, &storage)));
+ session_.SendMessage(MemSliceFromString("")));
// Verify unsent message does not consume a message_id.
EXPECT_CALL(*connection_, SendMessage(2, _, false))
.WillOnce(Return(MESSAGE_STATUS_SUCCESS));
EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 2),
- session_.SendMessage(
- MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
- message, &storage)));
+ session_.SendMessage(MemSliceFromString("")));
QuicMessageFrame frame(1);
QuicMessageFrame frame2(2);
@@ -2507,7 +2444,7 @@ TEST_P(QuicSessionTestServer, LocallyResetZombieStreams) {
EXPECT_TRUE(stream2->IsWaitingForAcks());
// Verify stream2 is a zombie streams.
auto& stream_map = QuicSessionPeer::stream_map(&session_);
- ASSERT_TRUE(QuicContainsKey(stream_map, stream2->id()));
+ ASSERT_TRUE(stream_map.contains(stream2->id()));
auto* stream = stream_map.find(stream2->id())->second.get();
EXPECT_TRUE(stream->IsZombie());
@@ -2556,7 +2493,7 @@ TEST_P(QuicSessionTestServer, WriteUnidirectionalStream) {
stream4->WriteOrBufferData(body, false, nullptr);
stream4->WriteOrBufferData(body, true, nullptr);
auto& stream_map = QuicSessionPeer::stream_map(&session_);
- ASSERT_TRUE(QuicContainsKey(stream_map, stream4->id()));
+ ASSERT_TRUE(stream_map.contains(stream4->id()));
auto* stream = stream_map.find(stream4->id())->second.get();
EXPECT_TRUE(stream->IsZombie());
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h b/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h
index cbf73b3dcf4..c2a800d5753 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h
@@ -12,6 +12,11 @@ namespace quic {
class QUIC_EXPORT_PRIVATE SimpleBufferAllocator : public QuicBufferAllocator {
public:
+ static SimpleBufferAllocator* Get() {
+ static SimpleBufferAllocator* singleton = new SimpleBufferAllocator();
+ return singleton;
+ }
+
char* New(size_t size) override;
char* New(size_t size, bool flag_enable) override;
void Delete(char* buffer) override;
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator_test.cc
index bc3f84437f8..5416cd424ef 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator_test.cc
@@ -24,5 +24,43 @@ TEST_F(SimpleBufferAllocatorTest, DeleteNull) {
alloc.Delete(nullptr);
}
+TEST_F(SimpleBufferAllocatorTest, MoveBuffersConstructor) {
+ SimpleBufferAllocator alloc;
+ QuicBuffer buffer1(&alloc, 16);
+
+ EXPECT_NE(buffer1.data(), nullptr);
+ EXPECT_EQ(buffer1.size(), 16u);
+
+ QuicBuffer buffer2(std::move(buffer1));
+ EXPECT_EQ(buffer1.data(), nullptr); // NOLINT(bugprone-use-after-move)
+ EXPECT_EQ(buffer1.size(), 0u);
+ EXPECT_NE(buffer2.data(), nullptr);
+ EXPECT_EQ(buffer2.size(), 16u);
+}
+
+TEST_F(SimpleBufferAllocatorTest, MoveBuffersAssignment) {
+ SimpleBufferAllocator alloc;
+ QuicBuffer buffer1(&alloc, 16);
+ QuicBuffer buffer2;
+
+ EXPECT_NE(buffer1.data(), nullptr);
+ EXPECT_EQ(buffer1.size(), 16u);
+ EXPECT_EQ(buffer2.data(), nullptr);
+ EXPECT_EQ(buffer2.size(), 0u);
+
+ buffer2 = std::move(buffer1);
+ EXPECT_EQ(buffer1.data(), nullptr); // NOLINT(bugprone-use-after-move)
+ EXPECT_EQ(buffer1.size(), 0u);
+ EXPECT_NE(buffer2.data(), nullptr);
+ EXPECT_EQ(buffer2.size(), 16u);
+}
+
+TEST_F(SimpleBufferAllocatorTest, CopyBuffer) {
+ SimpleBufferAllocator alloc;
+ const absl::string_view original = "Test string";
+ QuicBuffer copy = QuicBuffer::Copy(&alloc, original);
+ EXPECT_EQ(copy.AsStringView(), original);
+}
+
} // namespace
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc
index 3f1b7d94e18..cde50ef214d 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc
@@ -19,6 +19,7 @@
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
+#include "quic/platform/api/quic_mem_slice.h"
using spdy::SpdyPriority;
@@ -630,23 +631,18 @@ void QuicStream::WriteOrBufferData(
absl::string_view data,
bool fin,
QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
- if (session()->use_write_or_buffer_data_at_level()) {
- QUIC_BUG_IF(quic_bug_12570_4,
- QuicUtils::IsCryptoStreamId(transport_version(), id_))
- << ENDPOINT
- << "WriteOrBufferData is used to send application data, use "
- "WriteOrBufferDataAtLevel to send crypto data.";
- return WriteOrBufferDataAtLevel(
- data, fin, session()->GetEncryptionLevelToSendApplicationData(),
- ack_listener);
- }
- return WriteOrBufferDataInner(data, fin, absl::nullopt, ack_listener);
+ QUIC_BUG_IF(quic_bug_12570_4,
+ QuicUtils::IsCryptoStreamId(transport_version(), id_))
+ << ENDPOINT
+ << "WriteOrBufferData is used to send application data, use "
+ "WriteOrBufferDataAtLevel to send crypto data.";
+ return WriteOrBufferDataAtLevel(
+ data, fin, session()->GetEncryptionLevelToSendApplicationData(),
+ ack_listener);
}
-void QuicStream::WriteOrBufferDataInner(
- absl::string_view data,
- bool fin,
- absl::optional<EncryptionLevel> level,
+void QuicStream::WriteOrBufferDataAtLevel(
+ absl::string_view data, bool fin, EncryptionLevel level,
QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
if (data.empty() && !fin) {
QUIC_BUG(quic_bug_10586_2) << "data.empty() && !fin";
@@ -691,16 +687,6 @@ void QuicStream::WriteOrBufferDataInner(
}
}
-void QuicStream::WriteOrBufferDataAtLevel(
- absl::string_view data,
- bool fin,
- EncryptionLevel level,
- QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
- QUICHE_DCHECK(session()->use_write_or_buffer_data_at_level());
- QUIC_RELOADABLE_FLAG_COUNT(quic_use_write_or_buffer_data_at_level);
- return WriteOrBufferDataInner(data, fin, level, ack_listener);
-}
-
void QuicStream::OnCanWrite() {
if (HasDeadlinePassed()) {
OnDeadlinePassed();
@@ -720,11 +706,7 @@ void QuicStream::OnCanWrite() {
return;
}
if (HasBufferedData() || (fin_buffered_ && !fin_sent_)) {
- absl::optional<EncryptionLevel> send_level = absl::nullopt;
- if (session()->use_write_or_buffer_data_at_level()) {
- send_level = session()->GetEncryptionLevelToSendApplicationData();
- }
- WriteBufferedData(send_level);
+ WriteBufferedData(session()->GetEncryptionLevelToSendApplicationData());
}
if (!fin_buffered_ && !fin_sent_ && CanWriteNewData()) {
// Notify upper layer to write new data when buffered data size is below
@@ -759,6 +741,16 @@ void QuicStream::MaybeSendBlocked() {
}
QuicConsumedData QuicStream::WriteMemSlices(QuicMemSliceSpan span, bool fin) {
+ return WriteMemSlicesInner(MemSliceSpanWrapper(span), fin);
+}
+
+QuicConsumedData QuicStream::WriteMemSlices(absl::Span<QuicMemSlice> span,
+ bool fin) {
+ return WriteMemSlicesInner(MemSliceSpanWrapper(span), fin);
+}
+
+QuicConsumedData QuicStream::WriteMemSlicesInner(MemSliceSpanWrapper span,
+ bool fin) {
QuicConsumedData consumed_data(0, false);
if (span.empty() && !fin) {
QUIC_BUG(quic_bug_10586_6) << "span.empty() && !fin";
@@ -786,7 +778,7 @@ QuicConsumedData QuicStream::WriteMemSlices(QuicMemSliceSpan span, bool fin) {
if (!span.empty()) {
// Buffer all data if buffered data size is below limit.
QuicStreamOffset offset = send_buffer_.stream_offset();
- consumed_data.bytes_consumed = send_buffer_.SaveMemSliceSpan(span);
+ consumed_data.bytes_consumed = span.SaveTo(send_buffer_);
if (offset > send_buffer_.stream_offset() ||
kMaxStreamLength < send_buffer_.stream_offset()) {
QUIC_BUG(quic_bug_10586_8) << "Write too many data via stream " << id_;
@@ -802,11 +794,7 @@ QuicConsumedData QuicStream::WriteMemSlices(QuicMemSliceSpan span, bool fin) {
if (!had_buffered_data && (HasBufferedData() || fin_buffered_)) {
// Write data if there is no buffered data before.
- absl::optional<EncryptionLevel> send_level = absl::nullopt;
- if (session()->use_write_or_buffer_data_at_level()) {
- send_level = session()->GetEncryptionLevelToSendApplicationData();
- }
- WriteBufferedData(send_level);
+ WriteBufferedData(session()->GetEncryptionLevelToSendApplicationData());
}
return consumed_data;
@@ -1148,10 +1136,6 @@ bool QuicStream::RetransmitStreamData(QuicStreamOffset offset,
if (retransmission.Empty() && !retransmit_fin) {
return true;
}
- absl::optional<EncryptionLevel> send_level = absl::nullopt;
- if (session()->use_write_or_buffer_data_at_level()) {
- send_level = session()->GetEncryptionLevelToSendApplicationData();
- }
QuicConsumedData consumed(0, false);
for (const auto& interval : retransmission) {
QuicStreamOffset retransmission_offset = interval.min();
@@ -1161,7 +1145,8 @@ bool QuicStream::RetransmitStreamData(QuicStreamOffset offset,
stream_bytes_written());
consumed = stream_delegate_->WritevData(
id_, retransmission_length, retransmission_offset,
- can_bundle_fin ? FIN : NO_FIN, type, send_level);
+ can_bundle_fin ? FIN : NO_FIN, type,
+ session()->GetEncryptionLevelToSendApplicationData());
QUIC_DVLOG(1) << ENDPOINT << "stream " << id_
<< " is forced to retransmit stream data ["
<< retransmission_offset << ", "
@@ -1182,8 +1167,9 @@ bool QuicStream::RetransmitStreamData(QuicStreamOffset offset,
if (retransmit_fin) {
QUIC_DVLOG(1) << ENDPOINT << "stream " << id_
<< " retransmits fin only frame.";
- consumed = stream_delegate_->WritevData(id_, 0, stream_bytes_written(), FIN,
- type, send_level);
+ consumed = stream_delegate_->WritevData(
+ id_, 0, stream_bytes_written(), FIN, type,
+ session()->GetEncryptionLevelToSendApplicationData());
if (!consumed.fin_consumed) {
return false;
}
@@ -1209,7 +1195,7 @@ bool QuicStream::WriteStreamData(QuicStreamOffset offset,
return send_buffer_.WriteStreamData(offset, data_length, writer);
}
-void QuicStream::WriteBufferedData(absl::optional<EncryptionLevel> level) {
+void QuicStream::WriteBufferedData(EncryptionLevel level) {
QUICHE_DCHECK(!write_side_closed_ && (HasBufferedData() || fin_buffered_));
if (session_->ShouldYield(id())) {
@@ -1333,15 +1319,12 @@ void QuicStream::OnStreamDataConsumed(QuicByteCount bytes_consumed) {
void QuicStream::WritePendingRetransmission() {
while (HasPendingRetransmission()) {
QuicConsumedData consumed(0, false);
- absl::optional<EncryptionLevel> send_level = absl::nullopt;
- if (session()->use_write_or_buffer_data_at_level()) {
- send_level = session()->GetEncryptionLevelToSendApplicationData();
- }
if (!send_buffer_.HasPendingRetransmission()) {
QUIC_DVLOG(1) << ENDPOINT << "stream " << id_
<< " retransmits fin only frame.";
consumed = stream_delegate_->WritevData(
- id_, 0, stream_bytes_written(), FIN, LOSS_RETRANSMISSION, send_level);
+ id_, 0, stream_bytes_written(), FIN, LOSS_RETRANSMISSION,
+ session()->GetEncryptionLevelToSendApplicationData());
fin_lost_ = !consumed.fin_consumed;
if (fin_lost_) {
// Connection is write blocked.
@@ -1356,7 +1339,8 @@ void QuicStream::WritePendingRetransmission() {
(pending.offset + pending.length == stream_bytes_written());
consumed = stream_delegate_->WritevData(
id_, pending.length, pending.offset, can_bundle_fin ? FIN : NO_FIN,
- LOSS_RETRANSMISSION, send_level);
+ LOSS_RETRANSMISSION,
+ session()->GetEncryptionLevelToSendApplicationData());
QUIC_DVLOG(1) << ENDPOINT << "stream " << id_
<< " tries to retransmit stream data [" << pending.offset
<< ", " << pending.offset + pending.length
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream.h
index 6e6e623b9f2..6374d8ab6f6 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_stream.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream.h
@@ -24,6 +24,7 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
+#include "absl/types/span.h"
#include "quic/core/quic_flow_controller.h"
#include "quic/core/quic_packets.h"
#include "quic/core/quic_stream_send_buffer.h"
@@ -32,6 +33,7 @@
#include "quic/core/session_notifier_interface.h"
#include "quic/core/stream_delegate_interface.h"
#include "quic/platform/api/quic_export.h"
+#include "quic/platform/api/quic_mem_slice.h"
#include "quic/platform/api/quic_mem_slice_span.h"
#include "quic/platform/api/quic_reference_counted.h"
#include "spdy/core/spdy_protocol.h"
@@ -343,9 +345,13 @@ class QUIC_EXPORT_PRIVATE QuicStream
// succeeds.
bool MaybeSetTtl(QuicTime::Delta ttl);
- // Same as WritevData except data is provided in reference counted memory so
- // that data copy is avoided.
+ // Commits data into the stream write buffer, and potentially sends it over
+ // the wire. This method has all-or-nothing semantics: if the write buffer is
+ // not full, all of the memslices in |span| are moved into it; otherwise,
+ // nothing happens.
+ // TODO(vasilvv): deprecate and remove QuicMemSliceSpan version.
QuicConsumedData WriteMemSlices(QuicMemSliceSpan span, bool fin);
+ QuicConsumedData WriteMemSlices(absl::Span<QuicMemSlice> span, bool fin);
// Returns true if any stream data is lost (including fin) and needs to be
// retransmitted.
@@ -464,6 +470,26 @@ class QUIC_EXPORT_PRIVATE QuicStream
friend class test::QuicStreamPeer;
friend class QuicStreamUtils;
+ // Wraps around either QuicMemSliceSpan or absl::Span<QuicMemSlice>.
+ // TODO(vasilvv): delete this after QuicMemSliceSpan is gone.
+ class QUIC_EXPORT_PRIVATE MemSliceSpanWrapper {
+ public:
+ explicit MemSliceSpanWrapper(QuicMemSliceSpan span) : old_(span) {}
+ explicit MemSliceSpanWrapper(absl::Span<QuicMemSlice> span) : new_(span) {}
+
+ bool empty() { return old_.has_value() ? old_->empty() : new_.empty(); }
+ QuicByteCount SaveTo(QuicStreamSendBuffer& send_buffer) {
+ if (old_.has_value()) {
+ return send_buffer.SaveMemSliceSpan(*old_);
+ }
+ return send_buffer.SaveMemSliceSpan(new_);
+ }
+
+ private:
+ absl::optional<QuicMemSliceSpan> old_;
+ absl::Span<QuicMemSlice> new_;
+ };
+
QuicStream(QuicStreamId id,
QuicSession* session,
QuicStreamSequencer sequencer,
@@ -480,10 +506,8 @@ class QUIC_EXPORT_PRIVATE QuicStream
// controller, marks this stream as connection-level write blocked.
void MaybeSendBlocked();
- // Write buffered data in send buffer.
- // TODO(fayang): Change absl::optional<EncryptionLevel> to EncryptionLevel
- // when deprecating quic_use_write_or_buffer_data_at_level.
- void WriteBufferedData(absl::optional<EncryptionLevel> level);
+ // Write buffered data (in send buffer) at |level|.
+ void WriteBufferedData(EncryptionLevel level);
// Close the read side of the stream. May cause the stream to be closed.
void CloseReadSide();
@@ -491,17 +515,11 @@ class QUIC_EXPORT_PRIVATE QuicStream
// Called when bytes are sent to the peer.
void AddBytesSent(QuicByteCount bytes);
- // TODO(fayang): Inline this function when deprecating
- // quic_use_write_or_buffer_data_at_level.
- void WriteOrBufferDataInner(
- absl::string_view data,
- bool fin,
- absl::optional<EncryptionLevel> level,
- QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
-
// Returns true if deadline_ has passed.
bool HasDeadlinePassed() const;
+ QuicConsumedData WriteMemSlicesInner(MemSliceSpanWrapper span, bool fin);
+
QuicStreamSequencer sequencer_;
QuicStreamId id_;
// Pointer to the owning QuicSession object.
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc
index 05562125557..40aa3d0f949 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc
@@ -217,7 +217,7 @@ bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
return largest_peer_created_stream_id_ ==
QuicUtils::GetInvalidStreamId(version_.transport_version) ||
id > largest_peer_created_stream_id_ ||
- QuicContainsKey(available_streams_, id);
+ available_streams_.contains(id);
}
QuicStreamId QuicStreamIdManager::GetFirstOutgoingStreamId() const {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc
index 3b1b37bd1f3..1e32e544cd1 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc
@@ -12,6 +12,7 @@
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
+#include "quic/platform/api/quic_mem_slice.h"
namespace quic {
@@ -96,6 +97,20 @@ QuicByteCount QuicStreamSendBuffer::SaveMemSliceSpan(QuicMemSliceSpan span) {
[&](QuicMemSlice slice) { SaveMemSlice(std::move(slice)); });
}
+QuicByteCount QuicStreamSendBuffer::SaveMemSliceSpan(
+ absl::Span<QuicMemSlice> span) {
+ QuicByteCount total = 0;
+ for (QuicMemSlice& slice : span) {
+ if (slice.length() == 0) {
+ // Skip empty slices.
+ continue;
+ }
+ total += slice.length();
+ SaveMemSlice(std::move(slice));
+ }
+ return total;
+}
+
void QuicStreamSendBuffer::OnStreamDataConsumed(size_t bytes_consumed) {
stream_bytes_written_ += bytes_consumed;
stream_bytes_outstanding_ += bytes_consumed;
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h
index 4b23d3d82cd..f91e4760870 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h
@@ -5,6 +5,7 @@
#ifndef QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_H_
#define QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_H_
+#include "absl/types/span.h"
#include "quic/core/frames/quic_stream_frame.h"
#include "quic/core/quic_interval_deque.h"
#include "quic/core/quic_interval_set.h"
@@ -80,6 +81,7 @@ class QUIC_EXPORT_PRIVATE QuicStreamSendBuffer {
// Save all slices in |span| to send buffer. Return total bytes saved.
QuicByteCount SaveMemSliceSpan(QuicMemSliceSpan span);
+ QuicByteCount SaveMemSliceSpan(absl::Span<QuicMemSlice> span);
// Called when |bytes_consumed| bytes has been consumed by the stream.
void OnStreamDataConsumed(size_t bytes_consumed);
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc
index 25dda04c566..e1a2358acba 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc
@@ -39,12 +39,12 @@ class QuicStreamSendBufferTest : public QuicTest {
iov[0] = MakeIovec(absl::string_view(data1));
iov[1] = MakeIovec(absl::string_view(data2));
- QuicUniqueBufferPtr buffer1 = MakeUniqueBuffer(&allocator_, 1024);
- memset(buffer1.get(), 'c', 1024);
- QuicMemSlice slice1(std::move(buffer1), 1024);
- QuicUniqueBufferPtr buffer2 = MakeUniqueBuffer(&allocator_, 768);
- memset(buffer2.get(), 'd', 768);
- QuicMemSlice slice2(std::move(buffer2), 768);
+ QuicBuffer buffer1(&allocator_, 1024);
+ memset(buffer1.data(), 'c', buffer1.size());
+ QuicMemSlice slice1(std::move(buffer1));
+ QuicBuffer buffer2(&allocator_, 768);
+ memset(buffer2.data(), 'd', buffer2.size());
+ QuicMemSlice slice2(std::move(buffer2));
// The stream offset should be 0 since nothing is written.
EXPECT_EQ(0u, QuicStreamSendBufferPeer::EndOffset(&send_buffer_));
@@ -309,9 +309,9 @@ TEST_F(QuicStreamSendBufferTest, EndOffset) {
// Last offset is end offset of last slice.
EXPECT_EQ(3840u, QuicStreamSendBufferPeer::EndOffset(&send_buffer_));
- QuicUniqueBufferPtr buffer = MakeUniqueBuffer(&allocator_, 60);
- memset(buffer.get(), 'e', 60);
- QuicMemSlice slice(std::move(buffer), 60);
+ QuicBuffer buffer(&allocator_, 60);
+ memset(buffer.data(), 'e', buffer.size());
+ QuicMemSlice slice(std::move(buffer));
send_buffer_.SaveMemSlice(std::move(slice));
EXPECT_EQ(3840u, QuicStreamSendBufferPeer::EndOffset(&send_buffer_));
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc
index 2d3298b6fc9..e69b657177d 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc
@@ -1178,14 +1178,17 @@ TEST_P(QuicStreamTest, WriteMemSlices) {
SetQuicFlag(FLAGS_quic_buffered_data_threshold, 100);
Initialize();
- char data[1024];
- std::vector<std::pair<char*, size_t>> buffers;
- buffers.push_back(std::make_pair(data, ABSL_ARRAYSIZE(data)));
- buffers.push_back(std::make_pair(data, ABSL_ARRAYSIZE(data)));
- QuicTestMemSliceVector vector1(buffers);
- QuicTestMemSliceVector vector2(buffers);
- QuicMemSliceSpan span1 = vector1.span();
- QuicMemSliceSpan span2 = vector2.span();
+ constexpr QuicByteCount kDataSize = 1024;
+ QuicBufferAllocator* allocator =
+ connection_->helper()->GetStreamSendBufferAllocator();
+ std::vector<QuicMemSlice> vector1;
+ vector1.push_back(QuicMemSlice(QuicBuffer(allocator, kDataSize)));
+ vector1.push_back(QuicMemSlice(QuicBuffer(allocator, kDataSize)));
+ std::vector<QuicMemSlice> vector2;
+ vector2.push_back(QuicMemSlice(QuicBuffer(allocator, kDataSize)));
+ vector2.push_back(QuicMemSlice(QuicBuffer(allocator, kDataSize)));
+ absl::Span<QuicMemSlice> span1(vector1);
+ absl::Span<QuicMemSlice> span2(vector2);
EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
.WillOnce(InvokeWithoutArgs([this]() {
@@ -1196,7 +1199,7 @@ TEST_P(QuicStreamTest, WriteMemSlices) {
QuicConsumedData consumed = stream_->WriteMemSlices(span1, false);
EXPECT_EQ(2048u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
- EXPECT_EQ(2 * ABSL_ARRAYSIZE(data) - 100, stream_->BufferedDataBytes());
+ EXPECT_EQ(2 * kDataSize - 100, stream_->BufferedDataBytes());
EXPECT_FALSE(stream_->fin_buffered());
EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).Times(0);
@@ -1204,12 +1207,11 @@ TEST_P(QuicStreamTest, WriteMemSlices) {
consumed = stream_->WriteMemSlices(span2, true);
EXPECT_EQ(0u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
- EXPECT_EQ(2 * ABSL_ARRAYSIZE(data) - 100, stream_->BufferedDataBytes());
+ EXPECT_EQ(2 * kDataSize - 100, stream_->BufferedDataBytes());
EXPECT_FALSE(stream_->fin_buffered());
QuicByteCount data_to_write =
- 2 * ABSL_ARRAYSIZE(data) - 100 -
- GetQuicFlag(FLAGS_quic_buffered_data_threshold) + 1;
+ 2 * kDataSize - 100 - GetQuicFlag(FLAGS_quic_buffered_data_threshold) + 1;
EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
.WillOnce(InvokeWithoutArgs([this, data_to_write]() {
return session_->ConsumeData(stream_->id(), data_to_write, 100u, NO_FIN,
@@ -1225,8 +1227,7 @@ TEST_P(QuicStreamTest, WriteMemSlices) {
consumed = stream_->WriteMemSlices(span2, true);
EXPECT_EQ(2048u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
- EXPECT_EQ(2 * ABSL_ARRAYSIZE(data) +
- GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1,
+ EXPECT_EQ(2 * kDataSize + GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1,
stream_->BufferedDataBytes());
EXPECT_TRUE(stream_->fin_buffered());
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc
index dbaa38bdefe..12f5ef9aae2 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc
@@ -22,7 +22,6 @@
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/platform/api/quic_socket_address.h"
#include "common/quiche_text_utils.h"
@@ -174,7 +173,7 @@ bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
if (use_indirect_connection_id_map_) {
return indirect_connection_id_map_.contains(connection_id);
}
- return QuicContainsKey(connection_id_map_, connection_id);
+ return connection_id_map_.contains(connection_id);
}
void QuicTimeWaitListManager::OnBlockedWriterCanWrite() {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h
index ac371f2c180..fe15159d98c 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h
@@ -19,8 +19,8 @@
#include "quic/core/quic_packets.h"
#include "quic/core/quic_session.h"
#include "quic/core/quic_types.h"
-#include "quic/platform/api/quic_containers.h"
#include "quic/platform/api/quic_flags.h"
+#include "common/quiche_linked_hash_map.h"
namespace quic {
@@ -285,10 +285,11 @@ class QUIC_NO_EXPORT QuicTimeWaitListManager
TimeWaitConnectionInfo info;
};
- // QuicLinkedHashMap allows lookup by ConnectionId and traversal in add order.
- using ConnectionIdMap = QuicLinkedHashMap<QuicConnectionId,
- ConnectionIdData,
- QuicConnectionIdHash>;
+ // QuicheLinkedHashMap allows lookup by ConnectionId
+ // and traversal in add order.
+ using ConnectionIdMap = quiche::QuicheLinkedHashMap<QuicConnectionId,
+ ConnectionIdData,
+ QuicConnectionIdHash>;
// Do not use find/emplace/erase on this map directly. Use
// FindConnectionIdDataInMap, AddConnectionIdDateToMap,
// RemoveConnectionDataFromMap instead.
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc
index 4e9f957182b..f47bdf2fed8 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc
@@ -204,13 +204,13 @@ class QuicTimeWaitListManagerTest : public QuicTest {
bool ValidPublicResetPacketPredicate(
QuicConnectionId expected_connection_id,
- const testing::tuple<const char*, int>& packet_buffer) {
+ const std::tuple<const char*, int>& packet_buffer) {
FramerVisitorCapturingPublicReset visitor(expected_connection_id);
QuicFramer framer(AllSupportedVersions(), QuicTime::Zero(),
Perspective::IS_CLIENT, kQuicDefaultConnectionIdLength);
framer.set_visitor(&visitor);
- QuicEncryptedPacket encrypted(testing::get<0>(packet_buffer),
- testing::get<1>(packet_buffer));
+ QuicEncryptedPacket encrypted(std::get<0>(packet_buffer),
+ std::get<1>(packet_buffer));
framer.ProcessPacket(encrypted);
QuicPublicResetPacket packet = visitor.public_reset_packet();
bool public_reset_is_valid =
@@ -230,10 +230,10 @@ bool ValidPublicResetPacketPredicate(
return public_reset_is_valid || stateless_reset_is_valid;
}
-Matcher<const testing::tuple<const char*, int>> PublicResetPacketEq(
+Matcher<const std::tuple<const char*, int>> PublicResetPacketEq(
QuicConnectionId connection_id) {
return Truly(
- [connection_id](const testing::tuple<const char*, int> packet_buffer) {
+ [connection_id](const std::tuple<const char*, int> packet_buffer) {
return ValidPublicResetPacketPredicate(connection_id, packet_buffer);
});
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_types.cc b/chromium/net/third_party/quiche/src/quic/core/quic_types.cc
index 57163abbe42..f578bb46c36 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_types.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_types.cc
@@ -225,7 +225,6 @@ std::string TransmissionTypeToString(TransmissionType transmission_type) {
return "INVALID_TRANSMISSION_TYPE";
}
return absl::StrCat("Unknown(", static_cast<int>(transmission_type), ")");
- break;
}
}
@@ -267,7 +266,6 @@ std::string MessageStatusToString(MessageStatus message_status) {
RETURN_STRING_LITERAL(MESSAGE_STATUS_INTERNAL_ERROR);
default:
return absl::StrCat("Unknown(", static_cast<int>(message_status), ")");
- break;
}
}
@@ -292,7 +290,6 @@ std::string PacketNumberSpaceToString(PacketNumberSpace packet_number_space) {
default:
return absl::StrCat("Unknown(", static_cast<int>(packet_number_space),
")");
- break;
}
}
@@ -320,7 +317,6 @@ std::string EncryptionLevelToString(EncryptionLevel level) {
RETURN_STRING_LITERAL(ENCRYPTION_FORWARD_SECURE);
default:
return absl::StrCat("Unknown(", static_cast<int>(level), ")");
- break;
}
}
@@ -336,7 +332,6 @@ std::string QuicConnectionCloseTypeString(QuicConnectionCloseType type) {
RETURN_STRING_LITERAL(IETF_QUIC_APPLICATION_CONNECTION_CLOSE);
default:
return absl::StrCat("Unknown(", static_cast<int>(type), ")");
- break;
}
}
@@ -378,7 +373,6 @@ std::string KeyUpdateReasonString(KeyUpdateReason reason) {
RETURN_REASON_LITERAL(kLocalKeyUpdateLimitOverride);
default:
return absl::StrCat("Unknown(", static_cast<int>(reason), ")");
- break;
}
#undef RETURN_REASON_LITERAL
}
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_types.h b/chromium/net/third_party/quiche/src/quic/core/quic_types.h
index 2e65e5f12a4..eb4db074ef4 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_types.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_types.h
@@ -12,6 +12,7 @@
#include <ostream>
#include <vector>
+#include "absl/container/inlined_vector.h"
#include "quic/core/quic_connection_id.h"
#include "quic/core/quic_error_codes.h"
#include "quic/core/quic_packet_number.h"
@@ -25,7 +26,13 @@ namespace quic {
using QuicPacketLength = uint16_t;
using QuicControlFrameId = uint32_t;
using QuicMessageId = uint32_t;
-using QuicDatagramFlowId = uint64_t;
+
+// TODO(b/181256914) replace QuicDatagramStreamId with QuicStreamId once we
+// remove support for draft-ietf-masque-h3-datagram-00 in favor of later drafts.
+using QuicDatagramStreamId = uint64_t;
+using QuicDatagramContextId = uint64_t;
+// Note that for draft-ietf-masque-h3-datagram-00, we represent the flow ID as a
+// QuicDatagramStreamId.
// IMPORTANT: IETF QUIC defines stream IDs and stream counts as being unsigned
// 62-bit numbers. However, we have decided to only support up to 2^32-1 streams
@@ -580,7 +587,7 @@ struct QUIC_EXPORT_PRIVATE AckedPacket {
};
// A vector of acked packets.
-using AckedPacketVector = QuicInlinedVector<AckedPacket, 2>;
+using AckedPacketVector = absl::InlinedVector<AckedPacket, 2>;
// Information about a newly lost packet.
struct QUIC_EXPORT_PRIVATE LostPacket {
@@ -597,7 +604,7 @@ struct QUIC_EXPORT_PRIVATE LostPacket {
};
// A vector of lost packets.
-using LostPacketVector = QuicInlinedVector<LostPacket, 2>;
+using LostPacketVector = absl::InlinedVector<LostPacket, 2>;
// Please note, this value cannot used directly for packet serialization.
enum QuicLongHeaderType : uint8_t {
@@ -826,6 +833,16 @@ QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
QUIC_EXPORT_PRIVATE std::string KeyUpdateReasonString(KeyUpdateReason reason);
+// QuicSSLConfig contains configurations to be applied on a SSL object, which
+// overrides the configurations in SSL_CTX.
+struct QUIC_NO_EXPORT QuicSSLConfig {
+ // Whether TLS early data should be enabled. If not set, default to enabled.
+ absl::optional<bool> early_data_enabled;
+ // If set, used to configure the SSL object with
+ // SSL_set_signing_algorithm_prefs.
+ absl::optional<absl::InlinedVector<uint16_t, 8>> signing_algorithm_prefs;
+};
+
} // namespace quic
#endif // QUICHE_QUIC_CORE_QUIC_TYPES_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc
index fbdcd3eeb8a..9f9919c1be9 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc
@@ -8,6 +8,7 @@
#include <limits>
#include <type_traits>
+#include "absl/container/inlined_vector.h"
#include "quic/core/quic_connection_stats.h"
#include "quic/core/quic_packet_number.h"
#include "quic/core/quic_types.h"
@@ -174,7 +175,7 @@ void QuicUnackedPacketMap::AddSentPacket(SerializedPacket* mutable_packet,
last_inflight_packet_sent_time_ = sent_time;
last_inflight_packets_sent_time_[packet_number_space] = sent_time;
}
- unacked_packets_.push_back(info);
+ unacked_packets_.push_back(std::move(info));
// Swap the retransmittable frames to avoid allocations.
// TODO(ianswett): Could use emplace_back when Chromium can.
if (has_crypto_handshake) {
@@ -325,9 +326,9 @@ void QuicUnackedPacketMap::RemoveFromInFlight(QuicPacketNumber packet_number) {
RemoveFromInFlight(info);
}
-QuicInlinedVector<QuicPacketNumber, 2>
+absl::InlinedVector<QuicPacketNumber, 2>
QuicUnackedPacketMap::NeuterUnencryptedPackets() {
- QuicInlinedVector<QuicPacketNumber, 2> neutered_packets;
+ absl::InlinedVector<QuicPacketNumber, 2> neutered_packets;
QuicPacketNumber packet_number = GetLeastUnacked();
for (QuicUnackedPacketMap::iterator it = begin(); it != end();
++it, ++packet_number) {
@@ -353,9 +354,9 @@ QuicUnackedPacketMap::NeuterUnencryptedPackets() {
return neutered_packets;
}
-QuicInlinedVector<QuicPacketNumber, 2>
+absl::InlinedVector<QuicPacketNumber, 2>
QuicUnackedPacketMap::NeuterHandshakePackets() {
- QuicInlinedVector<QuicPacketNumber, 2> neutered_packets;
+ absl::InlinedVector<QuicPacketNumber, 2> neutered_packets;
QuicPacketNumber packet_number = GetLeastUnacked();
for (QuicUnackedPacketMap::iterator it = begin(); it != end();
++it, ++packet_number) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h
index 676f054b1fb..b8f8ffe9c87 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h
@@ -8,6 +8,7 @@
#include <cstddef>
#include <cstdint>
+#include "absl/container/inlined_vector.h"
#include "absl/strings/str_cat.h"
#include "quic/core/quic_packets.h"
#include "quic/core/quic_transmission_info.h"
@@ -69,12 +70,12 @@ class QUIC_EXPORT_PRIVATE QuicUnackedPacketMap {
// Called to neuter all unencrypted packets to ensure they do not get
// retransmitted. Returns a vector of neutered packet numbers.
- QuicInlinedVector<QuicPacketNumber, 2> NeuterUnencryptedPackets();
+ absl::InlinedVector<QuicPacketNumber, 2> NeuterUnencryptedPackets();
// Called to neuter packets in handshake packet number space to ensure they do
// not get retransmitted. Returns a vector of neutered packet numbers.
// TODO(fayang): Consider to combine this with NeuterUnencryptedPackets.
- QuicInlinedVector<QuicPacketNumber, 2> NeuterHandshakePackets();
+ absl::InlinedVector<QuicPacketNumber, 2> NeuterHandshakePackets();
// Returns true if |packet_number| has retransmittable frames. This will
// return false if all frames of this packet are either non-retransmittable or
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc b/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc
index 1f0137a91bc..98fbf55bdaa 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc
@@ -21,6 +21,7 @@
#include "quic/platform/api/quic_bug_tracker.h"
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
+#include "quic/platform/api/quic_mem_slice.h"
#include "common/platform/api/quiche_logging.h"
#include "common/platform/api/quiche_prefetch.h"
#include "common/quiche_endian.h"
@@ -693,6 +694,32 @@ bool QuicUtils::IsProbingFrame(QuicFrameType type) {
}
}
+// static
+bool QuicUtils::IsAckElicitingFrame(QuicFrameType type) {
+ switch (type) {
+ case PADDING_FRAME:
+ case STOP_WAITING_FRAME:
+ case ACK_FRAME:
+ case CONNECTION_CLOSE_FRAME:
+ return false;
+ default:
+ return true;
+ }
+}
+
+// static
+bool QuicUtils::AreStatelessResetTokensEqual(
+ const StatelessResetToken& token1,
+ const StatelessResetToken& token2) {
+ char byte = 0;
+ for (size_t i = 0; i < kStatelessResetTokenLength; i++) {
+ // This avoids compiler optimizations that could make us stop comparing
+ // after we find a byte that doesn't match.
+ byte |= (token1[i] ^ token2[i]);
+ }
+ return byte == 0;
+}
+
bool IsValidWebTransportSessionId(WebTransportSessionId id,
ParsedQuicVersion version) {
QUICHE_DCHECK(version.UsesHttp3());
@@ -701,5 +728,13 @@ bool IsValidWebTransportSessionId(WebTransportSessionId id,
QuicUtils::IsClientInitiatedStreamId(version.transport_version, id);
}
+QuicByteCount MemSliceSpanTotalSize(absl::Span<QuicMemSlice> span) {
+ QuicByteCount total = 0;
+ for (const QuicMemSlice& slice : span) {
+ total += slice.length();
+ }
+ return total;
+}
+
#undef RETURN_STRING_LITERAL // undef for jumbo builds
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils.h b/chromium/net/third_party/quiche/src/quic/core/quic_utils.h
index 0b089b4bad6..c335bc0e1c1 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_utils.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils.h
@@ -13,6 +13,7 @@
#include "absl/numeric/int128.h"
#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
#include "quic/core/crypto/quic_random.h"
#include "quic/core/frames/quic_frame.h"
#include "quic/core/quic_connection_id.h"
@@ -21,6 +22,7 @@
#include "quic/core/quic_versions.h"
#include "quic/platform/api/quic_export.h"
#include "quic/platform/api/quic_iovec.h"
+#include "quic/platform/api/quic_mem_slice.h"
#include "quic/platform/api/quic_socket_address.h"
namespace quic {
@@ -237,6 +239,14 @@ class QUIC_EXPORT_PRIVATE QuicUtils {
// Return true if this frame is an IETF probing frame.
static bool IsProbingFrame(QuicFrameType type);
+
+ // Return true if the two stateless reset tokens are equal. Performs the
+ // comparison in constant time.
+ static bool AreStatelessResetTokensEqual(const StatelessResetToken& token1,
+ const StatelessResetToken& token2);
+
+ // Return ture if this frame is an ack-eliciting frame.
+ static bool IsAckElicitingFrame(QuicFrameType type);
};
// Returns true if the specific ID is a valid WebTransport session ID that our
@@ -244,6 +254,8 @@ class QUIC_EXPORT_PRIVATE QuicUtils {
bool IsValidWebTransportSessionId(WebTransportSessionId id,
ParsedQuicVersion transport_version);
+QuicByteCount MemSliceSpanTotalSize(absl::Span<QuicMemSlice> span);
+
template <typename Mask>
class QUIC_EXPORT_PRIVATE BitMask {
public:
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc
index dc1165242f5..a02633046e5 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc
@@ -320,6 +320,8 @@ TEST_F(QuicUtilsTest, StatelessResetToken) {
QuicUtils::GenerateStatelessResetToken(connection_id2);
EXPECT_EQ(token1a, token1b);
EXPECT_NE(token1a, token2);
+ EXPECT_TRUE(QuicUtils::AreStatelessResetTokensEqual(token1a, token1b));
+ EXPECT_FALSE(QuicUtils::AreStatelessResetTokensEqual(token1a, token2));
}
enum class TestEnumClassBit : uint8_t {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc
index 1d4dcca70e4..911370f2618 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc
@@ -15,7 +15,7 @@ namespace quic {
QuicVersionManager::QuicVersionManager(
ParsedQuicVersionVector supported_versions)
- : enable_version_rfcv1_(GetQuicReloadableFlag(quic_enable_version_rfcv1)),
+ : disable_version_rfcv1_(GetQuicReloadableFlag(quic_disable_version_rfcv1)),
disable_version_draft_29_(
GetQuicReloadableFlag(quic_disable_version_draft_29)),
disable_version_t051_(GetQuicReloadableFlag(quic_disable_version_t051)),
@@ -49,8 +49,8 @@ const std::vector<std::string>& QuicVersionManager::GetSupportedAlpns() {
void QuicVersionManager::MaybeRefilterSupportedVersions() {
static_assert(SupportedVersions().size() == 6u,
"Supported versions out of sync");
- if (enable_version_rfcv1_ !=
- GetQuicReloadableFlag(quic_enable_version_rfcv1) ||
+ if (disable_version_rfcv1_ !=
+ GetQuicReloadableFlag(quic_disable_version_rfcv1) ||
disable_version_draft_29_ !=
GetQuicReloadableFlag(quic_disable_version_draft_29) ||
disable_version_t051_ !=
@@ -61,7 +61,7 @@ void QuicVersionManager::MaybeRefilterSupportedVersions() {
GetQuicReloadableFlag(quic_disable_version_q046) ||
disable_version_q043_ !=
GetQuicReloadableFlag(quic_disable_version_q043)) {
- enable_version_rfcv1_ = GetQuicReloadableFlag(quic_enable_version_rfcv1);
+ disable_version_rfcv1_ = GetQuicReloadableFlag(quic_disable_version_rfcv1);
disable_version_draft_29_ =
GetQuicReloadableFlag(quic_disable_version_draft_29);
disable_version_t051_ = GetQuicReloadableFlag(quic_disable_version_t051);
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h
index f443395e295..da73f37b910 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h
@@ -50,8 +50,8 @@ class QUIC_EXPORT_PRIVATE QuicVersionManager {
private:
// Cached value of reloadable flags.
- // quic_enable_version_rfcv1 flag
- bool enable_version_rfcv1_;
+ // quic_disable_version_rfcv1 flag
+ bool disable_version_rfcv1_;
// quic_disable_version_draft_29 flag
bool disable_version_draft_29_;
// quic_disable_version_t051 flag
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc b/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc
index ba2ec7ee29e..717932f5a6c 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc
@@ -47,7 +47,7 @@ void SetVersionFlag(const ParsedQuicVersion& version, bool should_enable) {
const bool enable = should_enable;
const bool disable = !should_enable;
if (version == ParsedQuicVersion::RFCv1()) {
- SetQuicReloadableFlag(quic_enable_version_rfcv1, enable);
+ SetQuicReloadableFlag(quic_disable_version_rfcv1, disable);
} else if (version == ParsedQuicVersion::Draft29()) {
SetQuicReloadableFlag(quic_disable_version_draft_29, disable);
} else if (version == ParsedQuicVersion::T051()) {
@@ -297,6 +297,18 @@ ParsedQuicVersionVector CurrentSupportedVersionsWithTls() {
return versions;
}
+ParsedQuicVersionVector CurrentSupportedHttp3Versions() {
+ ParsedQuicVersionVector versions;
+ for (const ParsedQuicVersion& version : CurrentSupportedVersions()) {
+ if (version.UsesHttp3()) {
+ versions.push_back(version);
+ }
+ }
+ QUIC_BUG_IF(no_version_uses_http3, versions.empty())
+ << "No version speaking Http3 found.";
+ return versions;
+}
+
ParsedQuicVersion ParseQuicVersionLabel(QuicVersionLabel version_label) {
for (const ParsedQuicVersion& version : AllSupportedVersions()) {
if (version_label == CreateQuicVersionLabel(version)) {
@@ -395,7 +407,7 @@ ParsedQuicVersionVector FilterSupportedVersions(
filtered_versions.reserve(versions.size());
for (const ParsedQuicVersion& version : versions) {
if (version == ParsedQuicVersion::RFCv1()) {
- if (GetQuicReloadableFlag(quic_enable_version_rfcv1)) {
+ if (!GetQuicReloadableFlag(quic_disable_version_rfcv1)) {
filtered_versions.push_back(version);
}
} else if (version == ParsedQuicVersion::Draft29()) {
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_versions.h b/chromium/net/third_party/quiche/src/quic/core/quic_versions.h
index 75a6826ae81..52e829c2275 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_versions.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions.h
@@ -446,6 +446,10 @@ QUIC_EXPORT_PRIVATE ParsedQuicVersionVector AllSupportedVersionsWithTls();
// PROTOCOL_TLS1_3.
QUIC_EXPORT_PRIVATE ParsedQuicVersionVector CurrentSupportedVersionsWithTls();
+// Returns a subset of CurrentSupportedVersions() using HTTP/3 at the HTTP
+// layer.
+QUIC_EXPORT_PRIVATE ParsedQuicVersionVector CurrentSupportedHttp3Versions();
+
// Returns QUIC version of |index| in result of |versions|. Returns
// UnsupportedQuicVersion() if |index| is out of bounds.
QUIC_EXPORT_PRIVATE ParsedQuicVersionVector
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc
index c9bc8b311ed..31e365e10c1 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc
@@ -445,6 +445,25 @@ TEST_F(QuicVersionsTest, SupportedVersionsAllDistinct) {
}
}
+TEST_F(QuicVersionsTest, CurrentSupportedHttp3Versions) {
+ ParsedQuicVersionVector h3_versions = CurrentSupportedHttp3Versions();
+ ParsedQuicVersionVector all_current_supported_versions =
+ CurrentSupportedVersions();
+ for (auto& version : all_current_supported_versions) {
+ bool version_is_h3 = false;
+ for (auto& h3_version : h3_versions) {
+ if (version == h3_version) {
+ EXPECT_TRUE(version.UsesHttp3());
+ version_is_h3 = true;
+ break;
+ }
+ }
+ if (!version_is_h3) {
+ EXPECT_FALSE(version.UsesHttp3());
+ }
+ }
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h
index dbc2ca9173d..54441e778f1 100644
--- a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h
@@ -9,13 +9,13 @@
#include <cstdint>
#include <utility>
+#include "absl/container/inlined_vector.h"
#include "http2/core/priority_write_scheduler.h"
#include "quic/core/quic_packets.h"
#include "quic/platform/api/quic_bug_tracker.h"
#include "quic/platform/api/quic_containers.h"
#include "quic/platform/api/quic_export.h"
#include "quic/platform/api/quic_flags.h"
-#include "quic/platform/api/quic_map_util.h"
namespace quic {
@@ -102,7 +102,7 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList {
};
// Optimized for the typical case of 2 static streams per session.
- using StreamsVector = QuicInlinedVector<StreamIdBlockedPair, 2>;
+ using StreamsVector = absl::InlinedVector<StreamIdBlockedPair, 2>;
StreamsVector::const_iterator begin() const { return streams_.cbegin(); }
diff --git a/chromium/net/third_party/quiche/src/quic/core/stream_delegate_interface.h b/chromium/net/third_party/quiche/src/quic/core/stream_delegate_interface.h
index 38b8e5326e7..02b195ef0c4 100644
--- a/chromium/net/third_party/quiche/src/quic/core/stream_delegate_interface.h
+++ b/chromium/net/third_party/quiche/src/quic/core/stream_delegate_interface.h
@@ -28,18 +28,13 @@ class QUIC_EXPORT_PRIVATE StreamDelegateInterface {
virtual void OnStreamError(QuicErrorCode error_code,
QuicIetfTransportErrorCodes ietf_error,
std::string error_details) = 0;
- // Called when the stream needs to write data. If |level| is present, the data
- // will be written at the specified |level|. The data will be written
- // at specified transmission |type|.
- // TODO(fayang): Change absl::optional<EncryptionLevel> to EncryptionLevel
- // when deprecating quic_use_write_or_buffer_data_at_level.
- virtual QuicConsumedData WritevData(
- QuicStreamId id,
- size_t write_length,
- QuicStreamOffset offset,
- StreamSendingState state,
- TransmissionType type,
- absl::optional<EncryptionLevel> level) = 0;
+ // Called when the stream needs to write data at specified |level| and
+ // transmission |type|.
+ virtual QuicConsumedData WritevData(QuicStreamId id, size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state,
+ TransmissionType type,
+ EncryptionLevel level) = 0;
// Called to write crypto data.
virtual size_t SendCryptoData(EncryptionLevel level,
size_t write_length,
diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc
index 1b9de030860..737f449c59f 100644
--- a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc
@@ -41,12 +41,16 @@ TlsClientHandshaker::TlsClientHandshaker(
crypto_negotiated_params_(new QuicCryptoNegotiatedParameters),
has_application_state_(has_application_state),
crypto_config_(crypto_config),
- tls_connection_(crypto_config->ssl_ctx(), this) {
+ tls_connection_(crypto_config->ssl_ctx(), this, session->GetSSLConfig()) {
std::string token =
crypto_config->LookupOrCreate(server_id)->source_address_token();
if (!token.empty()) {
session->SetSourceAddressTokenToSend(token);
}
+ if (crypto_config->tls_signature_algorithms().has_value()) {
+ SSL_set1_sigalgs_list(ssl(),
+ crypto_config->tls_signature_algorithms()->c_str());
+ }
}
TlsClientHandshaker::~TlsClientHandshaker() {}
@@ -68,6 +72,16 @@ bool TlsClientHandshaker::CryptoConnect() {
}
SSL_set_quic_use_legacy_codepoint(ssl(), use_legacy_extension);
+ // TODO(b/193650832) Add SetFromConfig to QUIC handshakers and remove reliance
+ // on session pointer.
+ if (session()->permutes_tls_extensions()) {
+ // Ask BoringSSL to randomize the order of TLS extensions.
+#if BORINGSSL_API_VERSION >= 16
+ QUIC_DLOG(INFO) << "Enabling TLS extension permutation";
+ SSL_set_permute_extensions(ssl(), true);
+#endif // BORINGSSL_API_VERSION
+ }
+
// Set the SNI to send, if any.
SSL_set_connect_state(ssl());
if (QUIC_DLOG_INFO_IS_ON() &&
@@ -444,22 +458,8 @@ void TlsClientHandshaker::OnProofVerifyDetailsAvailable(
void TlsClientHandshaker::FinishHandshake() {
FillNegotiatedParams();
- if (retry_handshake_on_early_data_) {
- QUICHE_CHECK(!SSL_in_early_data(ssl()));
- } else {
- if (SSL_in_early_data(ssl())) {
- // SSL_do_handshake returns after sending the ClientHello if the session
- // is 0-RTT-capable, which means that FinishHandshake will get called
- // twice - the first time after sending the ClientHello, and the second
- // time after the handshake is complete. If we're in the first time
- // FinishHandshake is called, we can't do any end-of-handshake processing.
-
- // If we're attempting a 0-RTT handshake, then we need to let the
- // transport and application know what state to apply to early data.
- PrepareZeroRttConfig(cached_state_.get());
- return;
- }
- }
+ QUICHE_CHECK(!SSL_in_early_data(ssl()));
+
QUIC_LOG(INFO) << "Client: handshake finished";
std::string error_details;
@@ -518,7 +518,6 @@ void TlsClientHandshaker::FinishHandshake() {
}
void TlsClientHandshaker::OnEnterEarlyData() {
- QUICHE_DCHECK(retry_handshake_on_early_data_);
QUICHE_DCHECK(SSL_in_early_data(ssl()));
// TODO(wub): It might be unnecessary to FillNegotiatedParams() at this time,
diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc
index 8365f633f0f..4bbb4ed6d0f 100644
--- a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc
@@ -458,13 +458,6 @@ TEST_P(TlsClientHandshakerTest, ZeroRttResumptionWithAyncProofVerifier) {
proof_verifier->InvokePendingCallback(0);
QuicFramer* framer = QuicConnectionPeer::GetFramer(connection_);
- if (!GetQuicReloadableFlag(quic_tls_retry_handshake_on_early_data)) {
- // Client does not have HANDSHAKE key due to b/186438140.
- EXPECT_EQ(nullptr,
- QuicFramerPeer::GetEncrypter(framer, ENCRYPTION_HANDSHAKE));
- return;
- }
-
// Verify client has derived HANDSHAKE key.
EXPECT_NE(nullptr,
QuicFramerPeer::GetEncrypter(framer, ENCRYPTION_HANDSHAKE));
diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc
index 9ba5f3c12f6..4b2d717befc 100644
--- a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc
@@ -96,12 +96,10 @@ void TlsHandshaker::AdvanceHandshake() {
return;
}
- QUICHE_BUG_IF(quic_tls_server_async_done_no_flusher,
- SSL_is_server(ssl()) && add_packet_flusher_on_async_op_done_ &&
- !handshaker_delegate_->PacketFlusherAttached())
- << "is_server:" << SSL_is_server(ssl())
- << ", add_packet_flusher_on_async_op_done_:"
- << add_packet_flusher_on_async_op_done_;
+ QUICHE_BUG_IF(
+ quic_tls_server_async_done_no_flusher,
+ SSL_is_server(ssl()) && !handshaker_delegate_->PacketFlusherAttached())
+ << "is_server:" << SSL_is_server(ssl());
QUIC_VLOG(1) << ENDPOINT << "Continuing handshake";
int rv = SSL_do_handshake(ssl());
@@ -111,8 +109,7 @@ void TlsHandshaker::AdvanceHandshake() {
// processed. Retry SSL_do_handshake once will advance the handshake more in
// that case. If there are no unprocessed ServerHello, the retry will return a
// non-positive number.
- if (retry_handshake_on_early_data_ && rv == 1 && SSL_in_early_data(ssl())) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_tls_retry_handshake_on_early_data, 1, 2);
+ if (rv == 1 && SSL_in_early_data(ssl())) {
OnEnterEarlyData();
rv = SSL_do_handshake(ssl());
QUIC_VLOG(1) << ENDPOINT
diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h
index 0b5ddc36769..72ae0993983 100644
--- a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h
@@ -57,7 +57,7 @@ class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate,
protected:
// Called when a new message is received on the crypto stream and is available
// for the TLS stack to read.
- void AdvanceHandshake();
+ virtual void AdvanceHandshake();
void CloseConnection(QuicErrorCode error, const std::string& reason_phrase);
// Closes the connection, specifying the wire error code |ietf_error|
@@ -71,12 +71,8 @@ class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate,
bool is_connection_closed() const { return is_connection_closed_; }
// Called when |SSL_do_handshake| returns 1, indicating that the handshake has
- // finished. Note that due to 0-RTT, the handshake may "finish" twice;
- // |SSL_in_early_data| can be used to determine whether the handshake is truly
- // done.
- // TODO(wub): When --quic_tls_retry_handshake_on_early_data is true, this
- // function will only be called once when the handshake actually finishes.
- // Update comment when deprecating the flag.
+ // finished. Note that a handshake only finishes once, entering early data
+ // does not count.
virtual void FinishHandshake() = 0;
// Called when |SSL_do_handshake| returns 1 and the connection is in early
@@ -84,7 +80,6 @@ class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate,
// retry |SSL_do_handshake| once.
virtual void OnEnterEarlyData() {
// By default, do nothing but check the preconditions.
- QUICHE_DCHECK(retry_handshake_on_early_data_);
QUICHE_DCHECK(SSL_in_early_data(ssl()));
}
@@ -168,11 +163,10 @@ class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate,
// error code corresponding to the TLS alert description |desc|.
void SendAlert(EncryptionLevel level, uint8_t desc) override;
- const bool add_packet_flusher_on_async_op_done_ =
- GetQuicReloadableFlag(quic_add_packet_flusher_on_async_op_done);
-
- const bool retry_handshake_on_early_data_ =
- GetQuicReloadableFlag(quic_tls_retry_handshake_on_early_data);
+ // Informational callback from BoringSSL. Subclasses can override it to do
+ // logging, tracing, etc.
+ // See |SSL_CTX_set_info_callback| for the meaning of |type| and |value|.
+ void InfoCallback(int /*type*/, int /*value*/) override {}
private:
// ProofVerifierCallbackImpl handles the result of an asynchronous certificate
diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc
index efd4d275013..b2817b15b22 100644
--- a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc
@@ -34,17 +34,24 @@
namespace quic {
+namespace {
+
+// Default port for HTTP/3.
+uint16_t kDefaultPort = 443;
+
+} // namespace
+
TlsServerHandshaker::DefaultProofSourceHandle::DefaultProofSourceHandle(
TlsServerHandshaker* handshaker,
ProofSource* proof_source)
: handshaker_(handshaker), proof_source_(proof_source) {}
TlsServerHandshaker::DefaultProofSourceHandle::~DefaultProofSourceHandle() {
- CancelPendingOperation();
+ CloseHandle();
}
-void TlsServerHandshaker::DefaultProofSourceHandle::CancelPendingOperation() {
- QUIC_DVLOG(1) << "CancelPendingOperation. is_signature_pending="
+void TlsServerHandshaker::DefaultProofSourceHandle::CloseHandle() {
+ QUIC_DVLOG(1) << "CloseHandle. is_signature_pending="
<< (signature_callback_ != nullptr);
if (signature_callback_) {
signature_callback_->Cancel();
@@ -62,7 +69,8 @@ TlsServerHandshaker::DefaultProofSourceHandle::SelectCertificate(
const std::string& /*alpn*/,
absl::optional<std::string> /*alps*/,
const std::vector<uint8_t>& /*quic_transport_params*/,
- const absl::optional<std::vector<uint8_t>>& /*early_data_context*/) {
+ const absl::optional<std::vector<uint8_t>>& /*early_data_context*/,
+ const QuicSSLConfig& /*ssl_config*/) {
if (!handshaker_ || !proof_source_) {
QUIC_BUG(quic_bug_10341_1)
<< "SelectCertificate called on a detached handle";
@@ -74,7 +82,8 @@ TlsServerHandshaker::DefaultProofSourceHandle::SelectCertificate(
handshaker_->OnSelectCertificateDone(
/*ok=*/true, /*is_sync=*/true, chain.get(),
- /*handshake_hints=*/absl::string_view());
+ /*handshake_hints=*/absl::string_view(),
+ /*ticket_encryption_key=*/absl::string_view());
if (!handshaker_->select_cert_status().has_value()) {
QUIC_BUG(quic_bug_12423_1)
<< "select_cert_status() has no value after a synchronous select cert";
@@ -164,7 +173,7 @@ TlsServerHandshaker::TlsServerHandshaker(
proof_source_(crypto_config->proof_source()),
pre_shared_key_(crypto_config->pre_shared_key()),
crypto_negotiated_params_(new QuicCryptoNegotiatedParameters),
- tls_connection_(crypto_config->ssl_ctx(), this),
+ tls_connection_(crypto_config->ssl_ctx(), this, session->GetSSLConfig()),
crypto_config_(crypto_config) {
QUICHE_DCHECK_EQ(PROTOCOL_TLS1_3,
session->connection()->version().handshake_protocol);
@@ -182,15 +191,19 @@ TlsServerHandshaker::TlsServerHandshaker(
if (GetQuicFlag(FLAGS_quic_disable_server_tls_resumption)) {
SSL_set_options(ssl(), SSL_OP_NO_TICKET);
}
-}
-TlsServerHandshaker::~TlsServerHandshaker() {
- CancelOutstandingCallbacks();
+ if (GetQuicReloadableFlag(quic_trace_ssl_events) &&
+ session->connection()->context()->tracer) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_trace_ssl_events);
+ tls_connection_.EnableInfoCallback();
+ }
}
+TlsServerHandshaker::~TlsServerHandshaker() { CancelOutstandingCallbacks(); }
+
void TlsServerHandshaker::CancelOutstandingCallbacks() {
if (proof_source_handle_) {
- proof_source_handle_->CancelPendingOperation();
+ proof_source_handle_->CloseHandle();
}
if (ticket_decryption_callback_) {
ticket_decryption_callback_->Cancel();
@@ -198,6 +211,40 @@ void TlsServerHandshaker::CancelOutstandingCallbacks() {
}
}
+void TlsServerHandshaker::InfoCallback(int type, int value) {
+ QuicConnectionTracer* tracer =
+ session()->connection()->context()->tracer.get();
+
+ if (tracer == nullptr) {
+ return;
+ }
+
+ if (type & SSL_CB_LOOP) {
+ tracer->PrintString(
+ absl::StrCat("SSL:ACCEPT_LOOP:", SSL_state_string_long(ssl())));
+ } else if (type & SSL_CB_ALERT) {
+ const char* prefix =
+ (type & SSL_CB_READ) ? "SSL:READ_ALERT:" : "SSL:WRITE_ALERT:";
+ tracer->PrintString(absl::StrCat(prefix, SSL_alert_type_string_long(value),
+ ":", SSL_alert_desc_string_long(value)));
+ } else if (type & SSL_CB_EXIT) {
+ const char* prefix =
+ (value == 1) ? "SSL:ACCEPT_EXIT_OK:" : "SSL:ACCEPT_EXIT_FAIL:";
+ tracer->PrintString(absl::StrCat(prefix, SSL_state_string_long(ssl())));
+ } else if (type & SSL_CB_HANDSHAKE_START) {
+ tracer->PrintString(
+ absl::StrCat("SSL:HANDSHAKE_START:", SSL_state_string_long(ssl())));
+ } else if (type & SSL_CB_HANDSHAKE_DONE) {
+ tracer->PrintString(
+ absl::StrCat("SSL:HANDSHAKE_DONE:", SSL_state_string_long(ssl())));
+ } else {
+ QUIC_DLOG(INFO) << "Unknown event type " << type << ": "
+ << SSL_state_string_long(ssl());
+ tracer->PrintString(
+ absl::StrCat("SSL:unknown:", value, ":", SSL_state_string_long(ssl())));
+ }
+}
+
std::unique_ptr<ProofSourceHandle>
TlsServerHandshaker::MaybeCreateProofSourceHandle() {
return std::make_unique<DefaultProofSourceHandle>(this, proof_source_);
@@ -351,18 +398,7 @@ TlsServerHandshaker::CreateCurrentOneRttEncrypter() {
void TlsServerHandshaker::OverrideQuicConfigDefaults(QuicConfig* /*config*/) {}
void TlsServerHandshaker::AdvanceHandshakeFromCallback() {
- std::unique_ptr<QuicConnection::ScopedPacketFlusher> flusher;
- if (add_packet_flusher_on_async_op_done_) {
- if (session()->PacketFlusherAttached()) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_add_packet_flusher_on_async_op_done, 1,
- 2);
- } else {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_add_packet_flusher_on_async_op_done, 2,
- 2);
- }
- flusher = std::make_unique<QuicConnection::ScopedPacketFlusher>(
- session()->connection());
- }
+ QuicConnection::ScopedPacketFlusher flusher(session()->connection());
AdvanceHandshake();
if (!is_connection_closed()) {
@@ -514,27 +550,14 @@ void TlsServerHandshaker::SetWriteSecret(
TlsHandshaker::SetWriteSecret(level, cipher, write_secret);
}
-std::string TlsServerHandshaker::GetAcceptChValueForOrigin(
- const std::string& /*origin*/) const {
+std::string TlsServerHandshaker::GetAcceptChValueForHostname(
+ const std::string& /*hostname*/) const {
return {};
}
void TlsServerHandshaker::FinishHandshake() {
- if (retry_handshake_on_early_data_) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_tls_retry_handshake_on_early_data, 2, 2);
- QUICHE_DCHECK(!SSL_in_early_data(ssl()));
- } else {
- if (SSL_in_early_data(ssl())) {
- // If the server accepts early data, SSL_do_handshake returns success
- // twice: once after processing the ClientHello and sending the server's
- // first flight, and then again after the handshake is complete. This
- // results in FinishHandshake getting called twice. On the first call to
- // FinishHandshake, we don't have any confirmation that the client is
- // live, so all end of handshake processing is deferred until the
- // handshake is actually complete.
- return;
- }
- }
+ QUICHE_DCHECK(!SSL_in_early_data(ssl()));
+
if (!valid_alpn_received_) {
QUIC_DLOG(ERROR)
<< "Server: handshake finished without receiving a known ALPN";
@@ -668,7 +691,8 @@ int TlsServerHandshaker::SessionTicketSeal(uint8_t* out,
size_t max_out_len,
absl::string_view in) {
QUICHE_DCHECK(proof_source_->GetTicketCrypter());
- std::vector<uint8_t> ticket = proof_source_->GetTicketCrypter()->Encrypt(in);
+ std::vector<uint8_t> ticket =
+ proof_source_->GetTicketCrypter()->Encrypt(in, ticket_encryption_key_);
if (max_out_len < ticket.size()) {
QUIC_BUG(quic_bug_12423_2)
<< "TicketCrypter returned " << ticket.size()
@@ -689,8 +713,18 @@ ssl_ticket_aead_result_t TlsServerHandshaker::SessionTicketOpen(
absl::string_view in) {
QUICHE_DCHECK(proof_source_->GetTicketCrypter());
+ if (allow_ignore_ticket_open_ && ignore_ticket_open_) {
+ // SetIgnoreTicketOpen has been called. Typically this means the caller is
+ // using handshake hints and expect the hints to contain ticket decryption
+ // results.
+ QUIC_CODE_COUNT(quic_tls_server_handshaker_tickets_ignored_1);
+ return ssl_ticket_aead_ignore_ticket;
+ }
+
if (!ticket_decryption_callback_) {
- ticket_received_ = true;
+ if (!allow_ignore_ticket_open_) {
+ ticket_received_ = true;
+ }
ticket_decryption_callback_ = new DecryptCallback(this);
proof_source_->GetTicketCrypter()->Decrypt(
in, std::unique_ptr<DecryptCallback>(ticket_decryption_callback_));
@@ -742,7 +776,7 @@ ssl_ticket_aead_result_t TlsServerHandshaker::FinalizeSessionTicketOpen(
if (decrypted_session_ticket_.empty()) {
QUIC_DLOG(ERROR) << "Session ticket decryption failed; ignoring ticket";
// Ticket decryption failed. Ignore the ticket.
- QUIC_CODE_COUNT(quic_tls_server_handshaker_tickets_ignored);
+ QUIC_CODE_COUNT(quic_tls_server_handshaker_tickets_ignored_2);
return ssl_ticket_aead_ignore_ticket;
}
if (max_out_len < decrypted_session_ticket_.size()) {
@@ -783,6 +817,15 @@ ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback(
return ssl_select_cert_error;
}
+ if (allow_ignore_ticket_open_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_tls_allow_ignore_ticket_open);
+ const uint8_t* unused_extension_bytes;
+ size_t unused_extension_len;
+ ticket_received_ = SSL_early_callback_ctx_extension_get(
+ client_hello, TLSEXT_TYPE_pre_shared_key, &unused_extension_bytes,
+ &unused_extension_len);
+ }
+
// This callback is called very early by Boring SSL, most of the SSL_get_foo
// function do not work at this point, but SSL_get_servername does.
const char* hostname = SSL_get_servername(ssl(), TLSEXT_NAMETYPE_host_name);
@@ -824,37 +867,36 @@ ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback(
absl::string_view ssl_capabilities_view;
absl::optional<std::string> alps;
- if (use_handshake_hints_) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_tls_server_use_handshake_hints);
- if (CryptoUtils::GetSSLCapabilities(ssl(), &ssl_capabilities,
- &ssl_capabilities_len)) {
- ssl_capabilities_view = absl::string_view(
- reinterpret_cast<const char*>(ssl_capabilities.get()),
- ssl_capabilities_len);
- }
- // Enable ALPS for the session's ALPN.
- SetApplicationSettingsResult alps_result =
- SetApplicationSettings(AlpnForVersion(session()->version()));
- if (!alps_result.success) {
- return ssl_select_cert_error;
- }
- alps = alps_result.alps_length > 0
- ? std::string(alps_result.alps_buffer.get(),
- alps_result.alps_length)
- : std::string();
+ if (CryptoUtils::GetSSLCapabilities(ssl(), &ssl_capabilities,
+ &ssl_capabilities_len)) {
+ ssl_capabilities_view =
+ absl::string_view(reinterpret_cast<const char*>(ssl_capabilities.get()),
+ ssl_capabilities_len);
+ }
+
+ // Enable ALPS for the session's ALPN.
+ SetApplicationSettingsResult alps_result =
+ SetApplicationSettings(AlpnForVersion(session()->version()));
+ if (!alps_result.success) {
+ return ssl_select_cert_error;
}
+ alps =
+ alps_result.alps_length > 0
+ ? std::string(alps_result.alps_buffer.get(), alps_result.alps_length)
+ : std::string();
const QuicAsyncStatus status = proof_source_handle_->SelectCertificate(
- session()->connection()->self_address(),
- session()->connection()->peer_address(), ssl_capabilities_view,
- crypto_negotiated_params_->sni,
+ session()->connection()->self_address().Normalized(),
+ session()->connection()->peer_address().Normalized(),
+ ssl_capabilities_view, crypto_negotiated_params_->sni,
absl::string_view(
reinterpret_cast<const char*>(client_hello->client_hello),
client_hello->client_hello_len),
AlpnForVersion(session()->version()), std::move(alps),
set_transport_params_result.quic_transport_params,
- set_transport_params_result.early_data_context);
+ set_transport_params_result.early_data_context,
+ tls_connection_.ssl_config());
QUICHE_DCHECK_EQ(status, select_cert_status().value());
@@ -876,28 +918,28 @@ ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback(
}
void TlsServerHandshaker::OnSelectCertificateDone(
- bool ok,
- bool is_sync,
- const ProofSource::Chain* chain,
- absl::string_view handshake_hints) {
+ bool ok, bool is_sync, const ProofSource::Chain* chain,
+ absl::string_view handshake_hints,
+ absl::string_view ticket_encryption_key) {
QUIC_DVLOG(1) << "OnSelectCertificateDone. ok:" << ok
<< ", is_sync:" << is_sync
- << ", len(handshake_hints):" << handshake_hints.size();
+ << ", len(handshake_hints):" << handshake_hints.size()
+ << ", len(ticket_encryption_key):"
+ << ticket_encryption_key.size();
+ ticket_encryption_key_ = std::string(ticket_encryption_key);
select_cert_status_ = QUIC_FAILURE;
if (ok) {
if (chain && !chain->certs.empty()) {
tls_connection_.SetCertChain(chain->ToCryptoBuffers().value);
- if (use_handshake_hints_) {
- if (!handshake_hints.empty() &&
- !SSL_set_handshake_hints(
- ssl(), reinterpret_cast<const uint8_t*>(handshake_hints.data()),
- handshake_hints.size())) {
- // If |SSL_set_handshake_hints| fails, the ssl() object will remain
- // intact, it is as if we didn't call it. The handshaker will
- // continue to compute signature/decrypt ticket as normal.
- QUIC_CODE_COUNT(quic_tls_server_set_handshake_hints_failed);
- QUIC_DVLOG(1) << "SSL_set_handshake_hints failed";
- }
+ if (!handshake_hints.empty() &&
+ !SSL_set_handshake_hints(
+ ssl(), reinterpret_cast<const uint8_t*>(handshake_hints.data()),
+ handshake_hints.size())) {
+ // If |SSL_set_handshake_hints| fails, the ssl() object will remain
+ // intact, it is as if we didn't call it. The handshaker will
+ // continue to compute signature/decrypt ticket as normal.
+ QUIC_CODE_COUNT(quic_tls_server_set_handshake_hints_failed);
+ QUIC_DVLOG(1) << "SSL_set_handshake_hints failed";
}
select_cert_status_ = QUIC_SUCCESS;
} else {
@@ -929,6 +971,10 @@ void TlsServerHandshaker::OnSelectCertificateDone(
}
}
+bool TlsServerHandshaker::WillNotCallComputeSignature() const {
+ return SSL_can_release_private_key(ssl());
+}
+
bool TlsServerHandshaker::ValidateHostname(const std::string& hostname) const {
if (!QuicHostnameUtils::IsValidSNI(hostname)) {
// TODO(b/151676147): Include this error string in the CONNECTION_CLOSE
@@ -986,13 +1032,6 @@ int TlsServerHandshaker::SelectAlpn(const uint8_t** out,
return SSL_TLSEXT_ERR_NOACK;
}
- if (!use_handshake_hints_) {
- // Enable ALPS for the selected ALPN protocol.
- if (!SetApplicationSettings(*selected_alpn).success) {
- return SSL_TLSEXT_ERR_NOACK;
- }
- }
-
session()->OnAlpnSelected(*selected_alpn);
valid_alpn_received_ = true;
*out_len = selected_alpn->size();
@@ -1006,8 +1045,17 @@ TlsServerHandshaker::SetApplicationSettings(absl::string_view alpn) {
const uint8_t* alps_data = nullptr;
const std::string& hostname = crypto_negotiated_params_->sni;
- std::string accept_ch_value = GetAcceptChValueForOrigin(hostname);
+ std::string accept_ch_value = GetAcceptChValueForHostname(hostname);
std::string origin = absl::StrCat("https://", hostname);
+ if (GetQuicReloadableFlag(quic_include_port_in_alps_origin)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_include_port_in_alps_origin);
+ uint16_t port = session()->self_address().port();
+ if (port != kDefaultPort) {
+ // This should be rare in production, but useful for test servers.
+ QUIC_CODE_COUNT(quic_server_alps_non_default_port);
+ absl::StrAppend(&origin, ":", port);
+ }
+ }
if (!accept_ch_value.empty()) {
AcceptChFrame frame{{{std::move(origin), std::move(accept_ch_value)}}};
diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h
index 260b094bda7..998cc15ad7c 100644
--- a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h
@@ -19,6 +19,7 @@
#include "quic/core/quic_types.h"
#include "quic/core/tls_handshaker.h"
#include "quic/platform/api/quic_export.h"
+#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
namespace quic {
@@ -82,12 +83,15 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker
const SSL_CIPHER* cipher,
const std::vector<uint8_t>& write_secret) override;
- // Called with normalized SNI hostname as |origin|. Return value will be sent
- // in an ACCEPT_CH frame in the TLS ALPS extension, unless empty.
- virtual std::string GetAcceptChValueForOrigin(
- const std::string& origin) const;
+ // Called with normalized SNI hostname as |hostname|. Return value will be
+ // sent in an ACCEPT_CH frame in the TLS ALPS extension, unless empty.
+ virtual std::string GetAcceptChValueForHostname(
+ const std::string& hostname) const;
protected:
+ // Override for tracing.
+ void InfoCallback(int type, int value) override;
+
// Creates a proof source handle for selecting cert and computing signature.
virtual std::unique_ptr<ProofSourceHandle> MaybeCreateProofSourceHandle();
@@ -165,10 +169,10 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker
bool HasValidSignature(size_t max_signature_size) const;
// ProofSourceHandleCallback implementation:
- void OnSelectCertificateDone(bool ok,
- bool is_sync,
- const ProofSource::Chain* chain,
- absl::string_view handshake_hints) override;
+ void OnSelectCertificateDone(
+ bool ok, bool is_sync, const ProofSource::Chain* chain,
+ absl::string_view handshake_hints,
+ absl::string_view ticket_encryption_key) override;
void OnComputeSignatureDone(
bool ok,
@@ -176,6 +180,17 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker
std::string signature,
std::unique_ptr<ProofSource::Details> details) override;
+ void set_encryption_established(bool encryption_established) {
+ encryption_established_ = encryption_established;
+ }
+
+ bool WillNotCallComputeSignature() const override;
+
+ void SetIgnoreTicketOpen(bool value) { ignore_ticket_open_ = value; }
+
+ const bool allow_ignore_ticket_open_ =
+ GetQuicReloadableFlag(quic_tls_allow_ignore_ticket_open);
+
private:
class QUIC_EXPORT_PRIVATE DecryptCallback
: public ProofSource::DecryptCallback {
@@ -200,8 +215,8 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker
~DefaultProofSourceHandle() override;
- // Cancel the pending signature operation, if any.
- void CancelPendingOperation() override;
+ // Close the handle. Cancel the pending signature operation, if any.
+ void CloseHandle() override;
// Delegates to proof_source_->GetCertChain.
// Returns QUIC_SUCCESS or QUIC_FAILURE. Never returns QUIC_PENDING.
@@ -214,8 +229,8 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker
const std::string& alpn,
absl::optional<std::string> alps,
const std::vector<uint8_t>& quic_transport_params,
- const absl::optional<std::vector<uint8_t>>& early_data_context)
- override;
+ const absl::optional<std::vector<uint8_t>>& early_data_context,
+ const QuicSSLConfig& ssl_config) override;
// Delegates to proof_source_->ComputeTlsSignature.
// Returns QUIC_SUCCESS, QUIC_FAILURE or QUIC_PENDING.
@@ -243,6 +258,20 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker
// Operation has been canceled, or Run has been called.
return;
}
+
+ if (GetQuicReloadableFlag(quic_run_default_signature_callback_once)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_run_default_signature_callback_once);
+ DefaultProofSourceHandle* handle = handle_;
+ handle_ = nullptr;
+
+ handle->signature_callback_ = nullptr;
+ if (handle->handshaker_ != nullptr) {
+ handle->handshaker_->OnComputeSignatureDone(
+ ok, is_sync_, std::move(signature), std::move(details));
+ }
+ return;
+ }
+
handle_->signature_callback_ = nullptr;
if (handle_->handshaker_ != nullptr) {
handle_->handshaker_->OnComputeSignatureDone(
@@ -309,6 +338,9 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker
// indicates that the client attempted a resumption.
bool ticket_received_ = false;
+ // Force SessionTicketOpen to return ssl_ticket_aead_ignore_ticket if called.
+ bool ignore_ticket_open_ = false;
+
// nullopt means select cert hasn't started.
absl::optional<QuicAsyncStatus> select_cert_status_;
@@ -323,6 +355,9 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker
// Pre-shared key used during the handshake.
std::string pre_shared_key_;
+ // (optional) Key to use for encrypting TLS resumption tickets.
+ std::string ticket_encryption_key_;
+
HandshakeState state_ = HANDSHAKE_START;
bool encryption_established_ = false;
bool valid_alpn_received_ = false;
@@ -330,8 +365,6 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker
crypto_negotiated_params_;
TlsServerConnection tls_connection_;
const QuicCryptoServerConfig* crypto_config_; // Unowned.
- const bool use_handshake_hints_ =
- GetQuicReloadableFlag(quic_tls_server_use_handshake_hints);
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc
index d67c8791d2c..6f8db123c99 100644
--- a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc
@@ -561,6 +561,23 @@ TEST_P(TlsServerHandshakerTest, HostnameForCertSelectionAndComputeSignature) {
EXPECT_EQ(last_compute_signature_args().hostname, "test.example.com");
}
+TEST_P(TlsServerHandshakerTest, SSLConfigForCertSelection) {
+ InitializeServerWithFakeProofSourceHandle();
+
+ // Disable early data.
+ server_session_->ssl_config()->early_data_enabled = false;
+
+ server_handshaker_->SetupProofSourceHandle(
+ /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC,
+ /*compute_signature_action=*/FakeProofSourceHandle::Action::
+ DELEGATE_SYNC);
+ InitializeFakeClient();
+ CompleteCryptoHandshake();
+ ExpectHandshakeSuccessful();
+
+ EXPECT_FALSE(last_select_cert_args().ssl_config.early_data_enabled);
+}
+
TEST_P(TlsServerHandshakerTest, ConnectionClosedOnTlsError) {
EXPECT_CALL(*server_connection_,
CloseConnection(QUIC_HANDSHAKE_FAILED, _, _, _));
diff --git a/chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.cc b/chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.cc
index 78227049dbd..2470aacf4cc 100644
--- a/chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.cc
+++ b/chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.cc
@@ -42,11 +42,8 @@ bool WebTransportStreamAdapter::Write(absl::string_view data) {
return false;
}
- QuicUniqueBufferPtr buffer = MakeUniqueBuffer(
- session_->connection()->helper()->GetStreamSendBufferAllocator(),
- data.size());
- memcpy(buffer.get(), data.data(), data.size());
- QuicMemSlice memslice(std::move(buffer), data.size());
+ QuicMemSlice memslice(QuicBuffer::Copy(
+ session_->connection()->helper()->GetStreamSendBufferAllocator(), data));
QuicConsumedData consumed =
stream_->WriteMemSlices(QuicMemSliceSpan(&memslice), /*fin=*/false);
diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_client_bin.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_client_bin.cc
index 920372574a1..bb9964e4207 100644
--- a/chromium/net/third_party/quiche/src/quic/masque/masque_client_bin.cc
+++ b/chromium/net/third_party/quiche/src/quic/masque/masque_client_bin.cc
@@ -50,8 +50,6 @@ int RunMasqueClient(int argc, char* argv[]) {
return 1;
}
- SetQuicReloadableFlag(quic_h3_datagram, true);
-
const bool disable_certificate_verification =
GetQuicFlag(FLAGS_disable_certificate_verification);
QuicEpollServer epoll_server;
diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.cc
index 6eee553e076..0d865692e46 100644
--- a/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.cc
+++ b/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.cc
@@ -92,11 +92,8 @@ MasqueClientSession::GetOrCreateConnectUdpClientState(
return nullptr;
}
- QuicDatagramFlowId flow_id = GetNextDatagramFlowId();
-
QUIC_DLOG(INFO) << "Sending CONNECT-UDP request for " << target_server_address
- << " using flow ID " << flow_id << " on stream "
- << stream->id();
+ << " on stream " << stream->id();
// Send the request.
spdy::Http2HeaderBlock headers;
@@ -104,7 +101,7 @@ MasqueClientSession::GetOrCreateConnectUdpClientState(
headers[":scheme"] = "masque";
headers[":path"] = "/";
headers[":authority"] = target_server_address.ToString();
- SpdyUtils::AddDatagramFlowIdHeader(&headers, flow_id);
+ SpdyUtils::AddDatagramFlowIdHeader(&headers, stream->id());
size_t bytes_sent =
stream->SendRequest(std::move(headers), /*body=*/"", /*fin=*/false);
if (bytes_sent == 0) {
@@ -112,9 +109,10 @@ MasqueClientSession::GetOrCreateConnectUdpClientState(
return nullptr;
}
+ absl::optional<QuicDatagramContextId> context_id;
connect_udp_client_states_.push_back(
- ConnectUdpClientState(stream, encapsulated_client_session, this, flow_id,
- target_server_address));
+ ConnectUdpClientState(stream, encapsulated_client_session, this,
+ context_id, target_server_address));
return &connect_udp_client_states_.back();
}
@@ -137,12 +135,15 @@ void MasqueClientSession::SendPacket(
return;
}
- QuicDatagramFlowId flow_id = connect_udp->flow_id();
- MessageStatus message_status =
- SendHttp3Datagram(connect_udp->flow_id(), packet);
+ MessageStatus message_status = SendHttp3Datagram(
+ connect_udp->stream()->id(), connect_udp->context_id(), packet);
QUIC_DVLOG(1) << "Sent packet to " << target_server_address
- << " compressed with flow ID " << flow_id
+ << " compressed with stream ID " << connect_udp->stream()->id()
+ << " context ID "
+ << (connect_udp->context_id().has_value()
+ ? connect_udp->context_id().value()
+ : 0)
<< " and got message status "
<< MessageStatusToString(message_status);
}
@@ -178,7 +179,11 @@ void MasqueClientSession::UnregisterConnectionId(
for (auto it = connect_udp_client_states_.begin();
it != connect_udp_client_states_.end();) {
if (it->encapsulated_client_session() == encapsulated_client_session) {
- QUIC_DLOG(INFO) << "Removing state for flow_id " << it->flow_id();
+ QUIC_DLOG(INFO) << "Removing state for stream ID " << it->stream()->id()
+ << " context ID "
+ << (it->context_id().has_value()
+ ? it->context_id().value()
+ : 0);
auto* stream = it->stream();
it = connect_udp_client_states_.erase(it);
if (!stream->write_side_closed()) {
@@ -217,8 +222,10 @@ void MasqueClientSession::OnStreamClosed(QuicStreamId stream_id) {
it != connect_udp_client_states_.end();) {
if (it->stream()->id() == stream_id) {
QUIC_DLOG(INFO) << "Stream " << stream_id
- << " was closed, removing state for flow_id "
- << it->flow_id();
+ << " was closed, removing state for context ID "
+ << (it->context_id().has_value()
+ ? it->context_id().value()
+ : 0);
auto* encapsulated_client_session = it->encapsulated_client_session();
it = connect_udp_client_states_.erase(it);
encapsulated_client_session->CloseConnection(
@@ -233,24 +240,41 @@ void MasqueClientSession::OnStreamClosed(QuicStreamId stream_id) {
QuicSpdyClientSession::OnStreamClosed(stream_id);
}
+bool MasqueClientSession::OnSettingsFrame(const SettingsFrame& frame) {
+ if (!QuicSpdyClientSession::OnSettingsFrame(frame)) {
+ QUIC_DLOG(ERROR) << "Failed to parse received settings";
+ return false;
+ }
+ if (!SupportsH3Datagram()) {
+ QUIC_DLOG(ERROR) << "Refusing to use MASQUE without HTTP/3 Datagrams";
+ return false;
+ }
+ owner_->OnSettingsReceived();
+ return true;
+}
+
MasqueClientSession::ConnectUdpClientState::ConnectUdpClientState(
QuicSpdyClientStream* stream,
EncapsulatedClientSession* encapsulated_client_session,
MasqueClientSession* masque_session,
- QuicDatagramFlowId flow_id,
+ absl::optional<QuicDatagramContextId> context_id,
const QuicSocketAddress& target_server_address)
: stream_(stream),
encapsulated_client_session_(encapsulated_client_session),
masque_session_(masque_session),
- flow_id_(flow_id),
+ context_id_(context_id),
target_server_address_(target_server_address) {
QUICHE_DCHECK_NE(masque_session_, nullptr);
- masque_session_->RegisterHttp3FlowId(this->flow_id(), this);
+ this->stream()->RegisterHttp3DatagramRegistrationVisitor(this);
+ Http3DatagramContextExtensions extensions;
+ this->stream()->RegisterHttp3DatagramContextId(this->context_id(), extensions,
+ this);
}
MasqueClientSession::ConnectUdpClientState::~ConnectUdpClientState() {
- if (flow_id_.has_value()) {
- masque_session_->UnregisterHttp3FlowId(flow_id());
+ if (stream() != nullptr) {
+ stream()->UnregisterHttp3DatagramContextId(context_id());
+ stream()->UnregisterHttp3DatagramRegistrationVisitor();
}
}
@@ -265,23 +289,69 @@ MasqueClientSession::ConnectUdpClientState::operator=(
stream_ = other.stream_;
encapsulated_client_session_ = other.encapsulated_client_session_;
masque_session_ = other.masque_session_;
- flow_id_ = other.flow_id_;
+ context_id_ = other.context_id_;
target_server_address_ = other.target_server_address_;
- other.flow_id_.reset();
- if (flow_id_.has_value()) {
- masque_session_->UnregisterHttp3FlowId(flow_id());
- masque_session_->RegisterHttp3FlowId(flow_id(), this);
+ other.stream_ = nullptr;
+ if (stream() != nullptr) {
+ stream()->MoveHttp3DatagramRegistration(this);
+ stream()->MoveHttp3DatagramContextIdRegistration(context_id(), this);
}
return *this;
}
void MasqueClientSession::ConnectUdpClientState::OnHttp3Datagram(
- QuicDatagramFlowId flow_id,
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
absl::string_view payload) {
- QUICHE_DCHECK_EQ(flow_id, this->flow_id());
+ QUICHE_DCHECK_EQ(stream_id, stream()->id());
+ QUICHE_DCHECK(context_id == context_id_);
encapsulated_client_session_->ProcessPacket(payload, target_server_address_);
QUIC_DVLOG(1) << "Sent " << payload.size()
- << " bytes to connection for flow_id " << flow_id;
+ << " bytes to connection for stream ID " << stream_id
+ << " context ID "
+ << (context_id.has_value() ? context_id.value() : 0);
+}
+
+void MasqueClientSession::ConnectUdpClientState::OnContextReceived(
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& /*extensions*/) {
+ if (stream_id != stream_->id()) {
+ QUIC_BUG(MASQUE client bad datagram context registration)
+ << "Registered stream ID " << stream_id << ", expected "
+ << stream_->id();
+ return;
+ }
+ if (context_id != context_id_) {
+ QUIC_DLOG(INFO) << "Ignoring unexpected context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " instead of "
+ << (context_id_.has_value() ? context_id_.value() : 0)
+ << " on stream ID " << stream_->id();
+ return;
+ }
+ // Do nothing since the client registers first and we currently ignore
+ // extensions.
+}
+
+void MasqueClientSession::ConnectUdpClientState::OnContextClosed(
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& /*extensions*/) {
+ if (stream_id != stream_->id()) {
+ QUIC_BUG(MASQUE client bad datagram context registration)
+ << "Closed context on stream ID " << stream_id << ", expected "
+ << stream_->id();
+ return;
+ }
+ if (context_id != context_id_) {
+ QUIC_DLOG(INFO) << "Ignoring unexpected close of context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " instead of "
+ << (context_id_.has_value() ? context_id_.value() : 0)
+ << " on stream ID " << stream_->id();
+ return;
+ }
+ QUIC_DLOG(INFO) << "Received datagram context close on stream ID "
+ << stream_->id() << ", closing stream";
+ masque_session_->ResetStream(stream_->id(), QUIC_STREAM_CANCELLED);
}
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.h b/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.h
index e055a7699be..1c130873340 100644
--- a/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.h
+++ b/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.h
@@ -31,6 +31,9 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession {
// Notifies the owner that the client connection ID is no longer in use.
virtual void UnregisterClientConnectionId(
QuicConnectionId client_connection_id) = 0;
+
+ // Notifies the owner that a settings frame has been received.
+ virtual void OnSettingsReceived() = 0;
};
// Interface meant to be implemented by encapsulated client sessions, i.e.
// the end-to-end QUIC client sessions that run inside MASQUE encapsulation.
@@ -75,6 +78,9 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession {
ConnectionCloseSource source) override;
void OnStreamClosed(QuicStreamId stream_id) override;
+ // From QuicSpdySession.
+ bool OnSettingsFrame(const SettingsFrame& frame) override;
+
// Send encapsulated packet.
void SendPacket(QuicConnectionId client_connection_id,
QuicConnectionId server_connection_id,
@@ -101,7 +107,8 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession {
private:
// State that the MasqueClientSession keeps for each CONNECT-UDP request.
class QUIC_NO_EXPORT ConnectUdpClientState
- : public QuicSpdySession::Http3DatagramVisitor {
+ : public QuicSpdyStream::Http3DatagramRegistrationVisitor,
+ public QuicSpdyStream::Http3DatagramVisitor {
public:
// |stream| and |encapsulated_client_session| must be valid for the lifetime
// of the ConnectUdpClientState.
@@ -109,7 +116,7 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession {
QuicSpdyClientStream* stream,
EncapsulatedClientSession* encapsulated_client_session,
MasqueClientSession* masque_session,
- QuicDatagramFlowId flow_id,
+ absl::optional<QuicDatagramContextId> context_id,
const QuicSocketAddress& target_server_address);
~ConnectUdpClientState();
@@ -124,26 +131,38 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession {
EncapsulatedClientSession* encapsulated_client_session() const {
return encapsulated_client_session_;
}
- QuicDatagramFlowId flow_id() const {
- QUICHE_DCHECK(flow_id_.has_value());
- return *flow_id_;
+ absl::optional<QuicDatagramContextId> context_id() const {
+ return context_id_;
}
const QuicSocketAddress& target_server_address() const {
return target_server_address_;
}
- // From QuicSpdySession::Http3DatagramVisitor.
- void OnHttp3Datagram(QuicDatagramFlowId flow_id,
+ // From QuicSpdyStream::Http3DatagramVisitor.
+ void OnHttp3Datagram(QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
absl::string_view payload) override;
+ // From QuicSpdyStream::Http3DatagramRegistrationVisitor.
+ void OnContextReceived(
+ QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions) override;
+ void OnContextClosed(
+ QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions) override;
+
private:
QuicSpdyClientStream* stream_; // Unowned.
EncapsulatedClientSession* encapsulated_client_session_; // Unowned.
MasqueClientSession* masque_session_; // Unowned.
- absl::optional<QuicDatagramFlowId> flow_id_;
+ absl::optional<QuicDatagramContextId> context_id_;
QuicSocketAddress target_server_address_;
};
+ bool ShouldNegotiateHttp3Datagram() override { return true; }
+
const ConnectUdpClientState* GetOrCreateConnectUdpClientState(
const QuicSocketAddress& target_server_address,
EncapsulatedClientSession* encapsulated_client_session);
diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.cc
index b021b41a0a6..035b9f9d030 100644
--- a/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.cc
+++ b/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.cc
@@ -21,7 +21,7 @@ namespace quic {
namespace {
// |kFlowId0| is used to indicate creation of a new compression context.
-const QuicDatagramFlowId kFlowId0 = 0;
+const QuicDatagramStreamId kFlowId0 = 0;
enum MasqueAddressFamily : uint8_t {
MasqueAddressFamilyIPv4 = 4,
@@ -32,16 +32,16 @@ enum MasqueAddressFamily : uint8_t {
MasqueCompressionEngine::MasqueCompressionEngine(
QuicSpdySession* masque_session)
- : masque_session_(masque_session) {}
+ : masque_session_(masque_session),
+ next_available_flow_id_(
+ masque_session_->perspective() == Perspective::IS_CLIENT ? 0 : 1) {}
-QuicDatagramFlowId MasqueCompressionEngine::FindOrCreateCompressionContext(
+QuicDatagramStreamId MasqueCompressionEngine::FindOrCreateCompressionContext(
QuicConnectionId client_connection_id,
QuicConnectionId server_connection_id,
- const QuicSocketAddress& server_address,
- bool client_connection_id_present,
- bool server_connection_id_present,
- bool* validated) {
- QuicDatagramFlowId flow_id = kFlowId0;
+ const QuicSocketAddress& server_address, bool client_connection_id_present,
+ bool server_connection_id_present, bool* validated) {
+ QuicDatagramStreamId flow_id = kFlowId0;
*validated = false;
for (const auto& kv : contexts_) {
const MasqueCompressionContext& context = kv.second;
@@ -74,11 +74,8 @@ QuicDatagramFlowId MasqueCompressionEngine::FindOrCreateCompressionContext(
}
// Create new compression context.
- flow_id = masque_session_->GetNextDatagramFlowId();
- if (flow_id == kFlowId0) {
- // Do not use value zero which is reserved in this mode.
- flow_id = masque_session_->GetNextDatagramFlowId();
- }
+ next_available_flow_id_ += 2;
+ flow_id = next_available_flow_id_;
QUIC_DVLOG(1) << "Compression assigning new flow_id " << flow_id << " to "
<< server_address << " client " << client_connection_id
<< " server " << server_connection_id;
@@ -96,13 +93,9 @@ bool MasqueCompressionEngine::WriteCompressedPacketToSlice(
QuicConnectionId server_connection_id,
const QuicSocketAddress& server_address,
QuicConnectionId destination_connection_id,
- QuicConnectionId source_connection_id,
- QuicDatagramFlowId flow_id,
- bool validated,
- uint8_t first_byte,
- bool long_header,
- QuicDataReader* reader,
- QuicDataWriter* writer) {
+ QuicConnectionId source_connection_id, QuicDatagramStreamId flow_id,
+ bool validated, uint8_t first_byte, bool long_header,
+ QuicDataReader* reader, QuicDataWriter* writer) {
if (validated) {
QUIC_DVLOG(1) << "Compressing using validated flow_id " << flow_id;
if (!writer->WriteVarInt62(flow_id)) {
@@ -222,8 +215,7 @@ bool MasqueCompressionEngine::WriteCompressedPacketToSlice(
}
void MasqueCompressionEngine::CompressAndSendPacket(
- absl::string_view packet,
- QuicConnectionId client_connection_id,
+ absl::string_view packet, QuicConnectionId client_connection_id,
QuicConnectionId server_connection_id,
const QuicSocketAddress& server_address) {
QUIC_DVLOG(2) << "Compressing client " << client_connection_id << " server "
@@ -258,7 +250,7 @@ void MasqueCompressionEngine::CompressAndSendPacket(
}
bool validated = false;
- QuicDatagramFlowId flow_id = FindOrCreateCompressionContext(
+ QuicDatagramStreamId flow_id = FindOrCreateCompressionContext(
client_connection_id, server_connection_id, server_address,
client_connection_id_present, server_connection_id_present, &validated);
@@ -276,10 +268,10 @@ void MasqueCompressionEngine::CompressAndSendPacket(
sizeof(server_address.port()) + sizeof(uint8_t) +
server_address.host().ToPackedString().length();
}
- QuicUniqueBufferPtr buffer = MakeUniqueBuffer(
+ QuicBuffer buffer(
masque_session_->connection()->helper()->GetStreamSendBufferAllocator(),
slice_length);
- QuicDataWriter writer(slice_length, buffer.get());
+ QuicDataWriter writer(buffer.size(), buffer.data());
if (!WriteCompressedPacketToSlice(
client_connection_id, server_connection_id, server_address,
@@ -288,18 +280,16 @@ void MasqueCompressionEngine::CompressAndSendPacket(
return;
}
- QuicMemSlice slice(std::move(buffer), slice_length);
MessageResult message_result =
- masque_session_->SendMessage(QuicMemSliceSpan(&slice));
+ masque_session_->SendMessage(QuicMemSlice(std::move(buffer)));
QUIC_DVLOG(1) << "Sent packet compressed with flow ID " << flow_id
<< " and got message result " << message_result;
}
bool MasqueCompressionEngine::ParseCompressionContext(
- QuicDataReader* reader,
- MasqueCompressionContext* context) {
- QuicDatagramFlowId new_flow_id;
+ QuicDataReader* reader, MasqueCompressionContext* context) {
+ QuicDatagramStreamId new_flow_id;
if (!reader->ReadVarInt62(&new_flow_id)) {
QUIC_DLOG(ERROR) << "Could not read new_flow_id";
return false;
@@ -398,10 +388,8 @@ bool MasqueCompressionEngine::ParseCompressionContext(
}
bool MasqueCompressionEngine::WriteDecompressedPacket(
- QuicDataReader* reader,
- const MasqueCompressionContext& context,
- std::vector<char>* packet,
- bool* version_present) {
+ QuicDataReader* reader, const MasqueCompressionContext& context,
+ std::vector<char>* packet, bool* version_present) {
QuicConnectionId destination_connection_id, source_connection_id;
if (masque_session_->perspective() == Perspective::IS_SERVER) {
destination_connection_id = context.server_connection_id;
@@ -464,16 +452,13 @@ bool MasqueCompressionEngine::WriteDecompressedPacket(
}
bool MasqueCompressionEngine::DecompressDatagram(
- absl::string_view datagram,
- QuicConnectionId* client_connection_id,
- QuicConnectionId* server_connection_id,
- QuicSocketAddress* server_address,
- std::vector<char>* packet,
- bool* version_present) {
+ absl::string_view datagram, QuicConnectionId* client_connection_id,
+ QuicConnectionId* server_connection_id, QuicSocketAddress* server_address,
+ std::vector<char>* packet, bool* version_present) {
QUIC_DVLOG(1) << "Decompressing DATAGRAM frame of length "
<< datagram.length();
QuicDataReader reader(datagram);
- QuicDatagramFlowId flow_id;
+ QuicDatagramStreamId flow_id;
if (!reader.ReadVarInt62(&flow_id)) {
QUIC_DLOG(ERROR) << "Could not read flow_id";
return false;
@@ -525,14 +510,14 @@ bool MasqueCompressionEngine::DecompressDatagram(
void MasqueCompressionEngine::UnregisterClientConnectionId(
QuicConnectionId client_connection_id) {
- std::vector<QuicDatagramFlowId> flow_ids_to_remove;
+ std::vector<QuicDatagramStreamId> flow_ids_to_remove;
for (const auto& kv : contexts_) {
const MasqueCompressionContext& context = kv.second;
if (context.client_connection_id == client_connection_id) {
flow_ids_to_remove.push_back(kv.first);
}
}
- for (QuicDatagramFlowId flow_id : flow_ids_to_remove) {
+ for (QuicDatagramStreamId flow_id : flow_ids_to_remove) {
contexts_.erase(flow_id);
}
}
diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.h b/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.h
index 9bea618ae76..581b9501791 100644
--- a/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.h
+++ b/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.h
@@ -63,8 +63,7 @@ class QUIC_NO_EXPORT MasqueCompressionEngine {
QuicConnectionId* client_connection_id,
QuicConnectionId* server_connection_id,
QuicSocketAddress* server_address,
- std::vector<char>* packet,
- bool* version_present);
+ std::vector<char>* packet, bool* version_present);
// Clears all entries referencing |client_connection_id| from the
// compression table.
@@ -83,12 +82,11 @@ class QUIC_NO_EXPORT MasqueCompressionEngine {
// whether the corresponding connection ID is present in the current packet.
// |validated| will contain whether the compression context that matches
// these arguments is currently validated or not.
- QuicDatagramFlowId FindOrCreateCompressionContext(
+ QuicDatagramStreamId FindOrCreateCompressionContext(
QuicConnectionId client_connection_id,
QuicConnectionId server_connection_id,
const QuicSocketAddress& server_address,
- bool client_connection_id_present,
- bool server_connection_id_present,
+ bool client_connection_id_present, bool server_connection_id_present,
bool* validated);
// Writes compressed packet to |slice| during compression.
@@ -97,11 +95,9 @@ class QUIC_NO_EXPORT MasqueCompressionEngine {
const QuicSocketAddress& server_address,
QuicConnectionId destination_connection_id,
QuicConnectionId source_connection_id,
- QuicDatagramFlowId flow_id,
- bool validated,
- uint8_t first_byte,
- bool long_header,
- QuicDataReader* reader,
+ QuicDatagramStreamId flow_id,
+ bool validated, uint8_t first_byte,
+ bool long_header, QuicDataReader* reader,
QuicDataWriter* writer);
// Parses compression context from flow ID 0 during decompression.
@@ -115,7 +111,8 @@ class QUIC_NO_EXPORT MasqueCompressionEngine {
bool* version_present);
QuicSpdySession* masque_session_; // Unowned.
- absl::flat_hash_map<QuicDatagramFlowId, MasqueCompressionContext> contexts_;
+ absl::flat_hash_map<QuicDatagramStreamId, MasqueCompressionContext> contexts_;
+ QuicDatagramStreamId next_available_flow_id_;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.cc
index 5ad91cfd9ad..ec475205ac8 100644
--- a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.cc
+++ b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.cc
@@ -81,6 +81,11 @@ std::unique_ptr<MasqueEpollClient> MasqueEpollClient::Create(
return nullptr;
}
+ if (!masque_client->WaitUntilSettingsReceived()) {
+ QUIC_LOG(ERROR) << "Failed to receive settings";
+ return nullptr;
+ }
+
if (masque_client->masque_mode() == MasqueMode::kLegacy) {
// Construct the legacy mode init request.
spdy::Http2HeaderBlock header_block;
@@ -114,6 +119,17 @@ std::unique_ptr<MasqueEpollClient> MasqueEpollClient::Create(
return masque_client;
}
+void MasqueEpollClient::OnSettingsReceived() {
+ settings_received_ = true;
+}
+
+bool MasqueEpollClient::WaitUntilSettingsReceived() {
+ while (connected() && !settings_received_) {
+ network_helper()->RunEventLoop();
+ }
+ return connected() && settings_received_;
+}
+
void MasqueEpollClient::UnregisterClientConnectionId(
QuicConnectionId client_connection_id) {
std::string body(client_connection_id.data(), client_connection_id.length());
diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.h b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.h
index 1db161f5aaa..2f5faca0aff 100644
--- a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.h
+++ b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.h
@@ -35,6 +35,8 @@ class QUIC_NO_EXPORT MasqueEpollClient : public QuicClient,
// Convenience accessor for the underlying connection ID.
QuicConnectionId connection_id();
+ // From MasqueClientSession::Owner.
+ void OnSettingsReceived() override;
// Send a MASQUE client connection ID unregister command to the server.
void UnregisterClientConnectionId(
QuicConnectionId client_connection_id) override;
@@ -50,12 +52,17 @@ class QUIC_NO_EXPORT MasqueEpollClient : public QuicClient,
std::unique_ptr<ProofVerifier> proof_verifier,
const std::string& authority);
+ // Wait synchronously until we receive the peer's settings. Returns whether
+ // they were received.
+ bool WaitUntilSettingsReceived();
+
// Disallow copy and assign.
MasqueEpollClient(const MasqueEpollClient&) = delete;
MasqueEpollClient& operator=(const MasqueEpollClient&) = delete;
MasqueMode masque_mode_;
std::string authority_;
+ bool settings_received_ = false;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_server_bin.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_server_bin.cc
index d5a5d9f7d36..aaccda3722e 100644
--- a/chromium/net/third_party/quiche/src/quic/masque/masque_server_bin.cc
+++ b/chromium/net/third_party/quiche/src/quic/masque/masque_server_bin.cc
@@ -52,8 +52,6 @@ int main(int argc, char* argv[]) {
return 0;
}
- SetQuicReloadableFlag(quic_h3_datagram, true);
-
quic::MasqueMode masque_mode = quic::MasqueMode::kOpen;
std::string mode_string = GetQuicFlag(FLAGS_masque_mode);
if (mode_string == "legacy") {
diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.cc
index 8a531cab3a0..effc17ff316 100644
--- a/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.cc
+++ b/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.cc
@@ -58,8 +58,7 @@ class FdWrapper {
};
std::unique_ptr<QuicBackendResponse> CreateBackendErrorResponse(
- absl::string_view status,
- absl::string_view error_details) {
+ absl::string_view status, absl::string_view error_details) {
spdy::Http2HeaderBlock response_headers;
response_headers[":status"] = status;
response_headers["masque-debug-info"] = error_details;
@@ -72,24 +71,15 @@ std::unique_ptr<QuicBackendResponse> CreateBackendErrorResponse(
} // namespace
MasqueServerSession::MasqueServerSession(
- MasqueMode masque_mode,
- const QuicConfig& config,
+ MasqueMode masque_mode, const QuicConfig& config,
const ParsedQuicVersionVector& supported_versions,
- QuicConnection* connection,
- QuicSession::Visitor* visitor,
- Visitor* owner,
- QuicEpollServer* epoll_server,
- QuicCryptoServerStreamBase::Helper* helper,
+ QuicConnection* connection, QuicSession::Visitor* visitor, Visitor* owner,
+ QuicEpollServer* epoll_server, QuicCryptoServerStreamBase::Helper* helper,
const QuicCryptoServerConfig* crypto_config,
QuicCompressedCertsCache* compressed_certs_cache,
MasqueServerBackend* masque_server_backend)
- : QuicSimpleServerSession(config,
- supported_versions,
- connection,
- visitor,
- helper,
- crypto_config,
- compressed_certs_cache,
+ : QuicSimpleServerSession(config, supported_versions, connection, visitor,
+ helper, crypto_config, compressed_certs_cache,
masque_server_backend),
masque_server_backend_(masque_server_backend),
owner_(owner),
@@ -153,8 +143,7 @@ void MasqueServerSession::OnMessageLost(QuicMessageId message_id) {
}
void MasqueServerSession::OnConnectionClosed(
- const QuicConnectionCloseFrame& frame,
- ConnectionCloseSource source) {
+ const QuicConnectionCloseFrame& frame, ConnectionCloseSource source) {
QuicSimpleServerSession::OnConnectionClosed(frame, source);
QUIC_DLOG(INFO) << "Closing connection for " << connection_id();
masque_server_backend_->RemoveBackendClient(connection_id());
@@ -165,7 +154,7 @@ void MasqueServerSession::OnConnectionClosed(
void MasqueServerSession::OnStreamClosed(QuicStreamId stream_id) {
connect_udp_server_states_.remove_if(
[stream_id](const ConnectUdpServerState& connect_udp) {
- return connect_udp.stream_id() == stream_id;
+ return connect_udp.stream()->id() == stream_id;
});
QuicSimpleServerSession::OnStreamClosed(stream_id);
@@ -213,7 +202,7 @@ std::unique_ptr<QuicBackendResponse> MasqueServerSession::HandleMasqueRequest(
QUIC_DLOG(ERROR) << "MASQUE request with bad method \"" << method << "\"";
return CreateBackendErrorResponse("400", "Bad method");
}
- absl::optional<QuicDatagramFlowId> flow_id =
+ absl::optional<QuicDatagramStreamId> flow_id =
SpdyUtils::ParseDatagramFlowIdHeader(request_headers);
if (!flow_id.has_value()) {
QUIC_DLOG(ERROR)
@@ -255,17 +244,36 @@ std::unique_ptr<QuicBackendResponse> MasqueServerSession::HandleMasqueRequest(
QUIC_DLOG(ERROR) << "Socket creation failed";
return CreateBackendErrorResponse("500", "Socket creation failed");
}
- QuicSocketAddress any_v6_address(QuicIpAddress::Any6(), 0);
+ QuicSocketAddress empty_address(QuicIpAddress::Any6(), 0);
+ if (target_server_address.host().IsIPv4()) {
+ empty_address = QuicSocketAddress(QuicIpAddress::Any4(), 0);
+ }
QuicUdpSocketApi socket_api;
- if (!socket_api.Bind(fd_wrapper.fd(), any_v6_address)) {
+ if (!socket_api.Bind(fd_wrapper.fd(), empty_address)) {
QUIC_DLOG(ERROR) << "Socket bind failed";
return CreateBackendErrorResponse("500", "Socket bind failed");
}
epoll_server_->RegisterFDForRead(fd_wrapper.fd(), this);
- connect_udp_server_states_.emplace_back(ConnectUdpServerState(
- *flow_id, request_handler->stream_id(), target_server_address,
- fd_wrapper.extract_fd(), this));
+ absl::optional<QuicDatagramContextId> context_id;
+ QuicSpdyStream* stream = static_cast<QuicSpdyStream*>(
+ GetActiveStream(request_handler->stream_id()));
+ if (stream == nullptr) {
+ QUIC_BUG(bad masque server stream type)
+ << "Unexpected stream type for stream ID "
+ << request_handler->stream_id();
+ return CreateBackendErrorResponse("500", "Bad stream type");
+ }
+ stream->RegisterHttp3DatagramFlowId(*flow_id);
+ connect_udp_server_states_.push_back(
+ ConnectUdpServerState(stream, context_id, target_server_address,
+ fd_wrapper.extract_fd(), this));
+
+ // TODO(b/181256914) remove this when we drop support for
+ // draft-ietf-masque-h3-datagram-00 in favor of later drafts.
+ Http3DatagramContextExtensions extensions;
+ stream->RegisterHttp3DatagramContextId(context_id, extensions,
+ &connect_udp_server_states_.back());
spdy::Http2HeaderBlock response_headers;
response_headers[":status"] = "200";
@@ -326,8 +334,7 @@ void MasqueServerSession::HandlePacketFromServer(
}
void MasqueServerSession::OnRegistration(QuicEpollServer* /*eps*/,
- QuicUdpSocketFd fd,
- int event_mask) {
+ QuicUdpSocketFd fd, int event_mask) {
QUIC_DVLOG(1) << "OnRegistration " << fd << " event_mask " << event_mask;
}
@@ -350,13 +357,12 @@ void MasqueServerSession::OnEvent(QuicUdpSocketFd fd, QuicEpollEvent* event) {
<< event->in_events << " on unknown fd " << fd;
return;
}
- QuicDatagramFlowId flow_id = it->flow_id();
QuicSocketAddress expected_target_server_address =
it->target_server_address();
QUICHE_DCHECK(expected_target_server_address.IsInitialized());
QUIC_DVLOG(1) << "Received readable event on fd " << fd << " (mask "
- << event->in_events << ") flow_id " << flow_id << " server "
- << expected_target_server_address;
+ << event->in_events << ") stream ID " << it->stream()->id()
+ << " server " << expected_target_server_address;
QuicUdpSocketApi socket_api;
BitMask64 packet_info_interested(QuicUdpPacketInfoBit::PEER_ADDRESS);
char packet_buffer[kMaxIncomingPacketSize];
@@ -392,12 +398,14 @@ void MasqueServerSession::OnEvent(QuicUdpSocketFd fd, QuicEpollEvent* event) {
return;
}
// The packet is valid, send it to the client in a DATAGRAM frame.
- MessageStatus message_status = SendHttp3Datagram(
- flow_id, absl::string_view(read_result.packet_buffer.buffer,
- read_result.packet_buffer.buffer_len));
+ MessageStatus message_status = it->stream()->SendHttp3Datagram(
+ it->context_id(),
+ absl::string_view(read_result.packet_buffer.buffer,
+ read_result.packet_buffer.buffer_len));
QUIC_DVLOG(1) << "Sent UDP packet from " << expected_target_server_address
<< " of length " << read_result.packet_buffer.buffer_len
- << " with flow ID " << flow_id << " and got message status "
+ << " with stream ID " << it->stream()->id()
+ << " and got message status "
<< MessageStatusToString(message_status);
}
}
@@ -417,24 +425,25 @@ std::string MasqueServerSession::Name() const {
}
MasqueServerSession::ConnectUdpServerState::ConnectUdpServerState(
- QuicDatagramFlowId flow_id,
- QuicStreamId stream_id,
- const QuicSocketAddress& target_server_address,
- QuicUdpSocketFd fd,
+ QuicSpdyStream* stream, absl::optional<QuicDatagramContextId> context_id,
+ const QuicSocketAddress& target_server_address, QuicUdpSocketFd fd,
MasqueServerSession* masque_session)
- : flow_id_(flow_id),
- stream_id_(stream_id),
+ : stream_(stream),
+ context_id_(context_id),
target_server_address_(target_server_address),
fd_(fd),
masque_session_(masque_session) {
QUICHE_DCHECK_NE(fd_, kQuicInvalidSocketFd);
QUICHE_DCHECK_NE(masque_session_, nullptr);
- masque_session_->RegisterHttp3FlowId(this->flow_id(), this);
+ this->stream()->RegisterHttp3DatagramRegistrationVisitor(this);
}
MasqueServerSession::ConnectUdpServerState::~ConnectUdpServerState() {
- if (flow_id_.has_value()) {
- masque_session_->UnregisterHttp3FlowId(flow_id());
+ if (stream() != nullptr) {
+ stream()->UnregisterHttp3DatagramRegistrationVisitor();
+ if (context_registered_) {
+ stream()->UnregisterHttp3DatagramContextId(context_id());
+ }
}
if (fd_ == kQuicInvalidSocketFd) {
return;
@@ -460,24 +469,29 @@ MasqueServerSession::ConnectUdpServerState::operator=(
masque_session_->epoll_server()->UnregisterFD(fd_);
socket_api.Destroy(fd_);
}
- flow_id_ = other.flow_id_;
- stream_id_ = other.stream_id_;
+ stream_ = other.stream_;
+ other.stream_ = nullptr;
+ context_id_ = other.context_id_;
target_server_address_ = other.target_server_address_;
fd_ = other.fd_;
masque_session_ = other.masque_session_;
other.fd_ = kQuicInvalidSocketFd;
- other.flow_id_.reset();
- if (flow_id_.has_value()) {
- masque_session_->UnregisterHttp3FlowId(flow_id());
- masque_session_->RegisterHttp3FlowId(flow_id(), this);
+ context_registered_ = other.context_registered_;
+ other.context_registered_ = false;
+ if (stream() != nullptr) {
+ stream()->MoveHttp3DatagramRegistration(this);
+ if (context_registered_) {
+ stream()->MoveHttp3DatagramContextIdRegistration(context_id(), this);
+ }
}
return *this;
}
void MasqueServerSession::ConnectUdpServerState::OnHttp3Datagram(
- QuicDatagramFlowId flow_id,
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
absl::string_view payload) {
- QUICHE_DCHECK_EQ(flow_id, this->flow_id());
+ QUICHE_DCHECK_EQ(stream_id, stream()->id());
+ QUICHE_DCHECK(context_id == context_id_);
QuicUdpSocketApi socket_api;
QuicUdpPacketInfo packet_info;
packet_info.SetPeerAddress(target_server_address_);
@@ -487,4 +501,58 @@ void MasqueServerSession::ConnectUdpServerState::OnHttp3Datagram(
<< target_server_address_ << " with result " << write_result;
}
+void MasqueServerSession::ConnectUdpServerState::OnContextReceived(
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& /*extensions*/) {
+ if (stream_id != stream()->id()) {
+ QUIC_BUG(MASQUE server bad datagram context registration)
+ << "Registered stream ID " << stream_id << ", expected "
+ << stream()->id();
+ return;
+ }
+ if (!context_received_) {
+ context_received_ = true;
+ context_id_ = context_id;
+ }
+ if (context_id != context_id_) {
+ QUIC_DLOG(INFO) << "Ignoring unexpected context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " instead of "
+ << (context_id_.has_value() ? context_id_.value() : 0)
+ << " on stream ID " << stream()->id();
+ return;
+ }
+ if (context_registered_) {
+ QUIC_BUG(MASQUE server double datagram context registration)
+ << "Try to re-register stream ID " << stream_id << " context ID "
+ << (context_id_.has_value() ? context_id_.value() : 0);
+ return;
+ }
+ context_registered_ = true;
+ Http3DatagramContextExtensions reply_extensions;
+ stream()->RegisterHttp3DatagramContextId(context_id_, reply_extensions, this);
+}
+
+void MasqueServerSession::ConnectUdpServerState::OnContextClosed(
+ QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& /*extensions*/) {
+ if (stream_id != stream()->id()) {
+ QUIC_BUG(MASQUE server bad datagram context registration)
+ << "Closed context on stream ID " << stream_id << ", expected "
+ << stream()->id();
+ return;
+ }
+ if (context_id != context_id_) {
+ QUIC_DLOG(INFO) << "Ignoring unexpected close of context ID "
+ << (context_id.has_value() ? context_id.value() : 0)
+ << " instead of "
+ << (context_id_.has_value() ? context_id_.value() : 0)
+ << " on stream ID " << stream()->id();
+ return;
+ }
+ QUIC_DLOG(INFO) << "Received datagram context close on stream ID "
+ << stream()->id() << ", closing stream";
+ masque_session_->ResetStream(stream()->id(), QUIC_STREAM_CANCELLED);
+}
+
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.h b/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.h
index 14b8293e9b1..1bec14c5464 100644
--- a/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.h
+++ b/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.h
@@ -38,14 +38,10 @@ class QUIC_NO_EXPORT MasqueServerSession
};
explicit MasqueServerSession(
- MasqueMode masque_mode,
- const QuicConfig& config,
+ MasqueMode masque_mode, const QuicConfig& config,
const ParsedQuicVersionVector& supported_versions,
- QuicConnection* connection,
- QuicSession::Visitor* visitor,
- Visitor* owner,
- QuicEpollServer* epoll_server,
- QuicCryptoServerStreamBase::Helper* helper,
+ QuicConnection* connection, QuicSession::Visitor* visitor, Visitor* owner,
+ QuicEpollServer* epoll_server, QuicCryptoServerStreamBase::Helper* helper,
const QuicCryptoServerConfig* crypto_config,
QuicCompressedCertsCache* compressed_certs_cache,
MasqueServerBackend* masque_server_backend);
@@ -71,8 +67,7 @@ class QUIC_NO_EXPORT MasqueServerSession
QuicSimpleServerBackend::RequestHandler* request_handler) override;
// From QuicEpollCallbackInterface.
- void OnRegistration(QuicEpollServer* eps,
- QuicUdpSocketFd fd,
+ void OnRegistration(QuicEpollServer* eps, QuicUdpSocketFd fd,
int event_mask) override;
void OnModification(QuicUdpSocketFd fd, int event_mask) override;
void OnEvent(QuicUdpSocketFd fd, QuicEpollEvent* event) override;
@@ -88,15 +83,15 @@ class QUIC_NO_EXPORT MasqueServerSession
private:
// State that the MasqueServerSession keeps for each CONNECT-UDP request.
class QUIC_NO_EXPORT ConnectUdpServerState
- : public QuicSpdySession::Http3DatagramVisitor {
+ : public QuicSpdyStream::Http3DatagramRegistrationVisitor,
+ public QuicSpdyStream::Http3DatagramVisitor {
public:
// ConnectUdpServerState takes ownership of |fd|. It will unregister it
// from |epoll_server| and close the file descriptor when destructed.
explicit ConnectUdpServerState(
- QuicDatagramFlowId flow_id,
- QuicStreamId stream_id,
- const QuicSocketAddress& target_server_address,
- QuicUdpSocketFd fd,
+ QuicSpdyStream* stream,
+ absl::optional<QuicDatagramContextId> context_id,
+ const QuicSocketAddress& target_server_address, QuicUdpSocketFd fd,
MasqueServerSession* masque_session);
~ConnectUdpServerState();
@@ -107,28 +102,42 @@ class QUIC_NO_EXPORT MasqueServerSession
ConnectUdpServerState& operator=(const ConnectUdpServerState&) = delete;
ConnectUdpServerState& operator=(ConnectUdpServerState&&);
- QuicDatagramFlowId flow_id() const {
- QUICHE_DCHECK(flow_id_.has_value());
- return *flow_id_;
+ QuicSpdyStream* stream() const { return stream_; }
+ absl::optional<QuicDatagramContextId> context_id() const {
+ return context_id_;
}
- QuicStreamId stream_id() const { return stream_id_; }
const QuicSocketAddress& target_server_address() const {
return target_server_address_;
}
QuicUdpSocketFd fd() const { return fd_; }
- // From QuicSpdySession::Http3DatagramVisitor.
- void OnHttp3Datagram(QuicDatagramFlowId flow_id,
+ // From QuicSpdyStream::Http3DatagramVisitor.
+ void OnHttp3Datagram(QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
absl::string_view payload) override;
+ // From QuicSpdyStream::Http3DatagramRegistrationVisitor.
+ void OnContextReceived(
+ QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions) override;
+ void OnContextClosed(
+ QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions) override;
+
private:
- absl::optional<QuicDatagramFlowId> flow_id_;
- QuicStreamId stream_id_;
+ QuicSpdyStream* stream_;
+ absl::optional<QuicDatagramContextId> context_id_;
QuicSocketAddress target_server_address_;
- QuicUdpSocketFd fd_; // Owned.
+ QuicUdpSocketFd fd_; // Owned.
MasqueServerSession* masque_session_; // Unowned.
+ bool context_received_ = false;
+ bool context_registered_ = false;
};
+ bool ShouldNegotiateHttp3Datagram() override { return true; }
+
MasqueServerBackend* masque_server_backend_; // Unowned.
Visitor* owner_; // Unowned.
QuicEpollServer* epoll_server_; // Unowned.
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h
index f7fd922f594..eb16bfa18c3 100644
--- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h
@@ -7,25 +7,15 @@
#include "net/quic/platform/impl/quic_containers_impl.h"
-#include "absl/hash/hash.h"
-
namespace quic {
-// A map which offers insertion-ordered iteration.
-template <typename Key, typename Value, typename Hash = absl::Hash<Key>>
-using QuicLinkedHashMap = QuicLinkedHashMapImpl<Key, Value, Hash>;
-
-// A vector optimized for small sizes. Provides the same APIs as a std::vector.
-template <typename T, size_t N, typename A = std::allocator<T>>
-using QuicInlinedVector = QuicInlinedVectorImpl<T, N, A>;
-
-// An ordered set of values.
+// An ordered container optimized for small sets.
+// An implementation with O(n) mutations might be chosen
+// in case it has better memory usage and/or faster access.
//
// DOES NOT GUARANTEE POINTER OR ITERATOR STABILITY!
-template <typename Key,
- typename Compare = std::less<Key>,
- typename Rep = std::vector<Key>>
-using QuicOrderedSet = QuicOrderedSetImpl<Key, Compare, Rep>;
+template <typename Key, typename Compare = std::less<Key>>
+using QuicSmallOrderedSet = QuicSmallOrderedSetImpl<Key, Compare>;
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers_test.cc
deleted file mode 100644
index bc255d36dcf..00000000000
--- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers_test.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "quic/platform/api/quic_containers.h"
-
-#include "quic/platform/api/quic_test.h"
-
-using ::testing::ElementsAre;
-
-namespace quic {
-namespace test {
-namespace {
-
-TEST(QuicInlinedVectorTest, Swap) {
- {
- // Inline to inline.
- QuicInlinedVector<int, 2> self({1, 2});
- QuicInlinedVector<int, 2> other({3});
-
- self.swap(other);
-
- EXPECT_THAT(self, ElementsAre(3));
- EXPECT_THAT(other, ElementsAre(1, 2));
- }
-
- {
- // Inline to out-of-line.
- QuicInlinedVector<int, 2> self({1, 2});
- QuicInlinedVector<int, 2> other({3, 4, 5, 6});
-
- self.swap(other);
-
- EXPECT_THAT(self, ElementsAre(3, 4, 5, 6));
- EXPECT_THAT(other, ElementsAre(1, 2));
- }
-
- {
- // Out-of-line to inline.
- QuicInlinedVector<int, 2> self({1, 2, 3});
- QuicInlinedVector<int, 2> other({4, 5});
-
- self.swap(other);
-
- EXPECT_THAT(self, ElementsAre(4, 5));
- EXPECT_THAT(other, ElementsAre(1, 2, 3));
- }
-
- {
- // Out-of-line to Out-of-line.
- QuicInlinedVector<int, 2> self({1, 2, 3});
- QuicInlinedVector<int, 2> other({4, 5, 6, 7});
-
- self.swap(other);
-
- EXPECT_THAT(self, ElementsAre(4, 5, 6, 7));
- EXPECT_THAT(other, ElementsAre(1, 2, 3));
- }
-}
-
-} // namespace
-} // namespace test
-} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_estimate_memory_usage.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_estimate_memory_usage.h
deleted file mode 100644
index a5a093b9f1c..00000000000
--- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_estimate_memory_usage.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The Chromium 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 QUICHE_QUIC_PLATFORM_API_QUIC_ESTIMATE_MEMORY_USAGE_H_
-#define QUICHE_QUIC_PLATFORM_API_QUIC_ESTIMATE_MEMORY_USAGE_H_
-
-#include <cstddef>
-
-#include "quiche_platform_impl/quiche_estimate_memory_usage_impl.h"
-
-namespace quic {
-
-template <class T>
-size_t QuicEstimateMemoryUsage(const T& object) {
- return quiche::QuicheEstimateMemoryUsageImpl(object);
-}
-
-} // namespace quic
-
-#endif // QUICHE_QUIC_PLATFORM_API_QUIC_ESTIMATE_MEMORY_USAGE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.cc
deleted file mode 100644
index 5cb310baf9f..00000000000
--- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "quic/platform/api/quic_file_utils.h"
-
-#include "absl/strings/string_view.h"
-#include "net/quic/platform/impl/quic_file_utils_impl.h"
-
-namespace quic {
-
-// Traverses the directory |dirname| and retuns all of the files
-// it contains.
-std::vector<std::string> ReadFileContents(const std::string& dirname) {
- return ReadFileContentsImpl(dirname);
-}
-
-// Reads the contents of |filename| as a string into |contents|.
-void ReadFileContents(absl::string_view filename, std::string* contents) {
- ReadFileContentsImpl(filename, contents);
-}
-
-} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.h
deleted file mode 100644
index a1f881f63a6..00000000000
--- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2018 The Chromium 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 QUICHE_QUIC_PLATFORM_API_QUIC_FILE_UTILS_H_
-#define QUICHE_QUIC_PLATFORM_API_QUIC_FILE_UTILS_H_
-
-#include <string>
-#include <vector>
-
-#include "absl/strings/string_view.h"
-#include "quic/platform/api/quic_export.h"
-
-namespace quic {
-
-// Traverses the directory |dirname| and returns all of the files it contains.
-QUIC_EXPORT_PRIVATE std::vector<std::string> ReadFileContents(
- const std::string& dirname);
-
-// Reads the contents of |filename| as a string into |contents|.
-QUIC_EXPORT_PRIVATE void ReadFileContents(absl::string_view filename,
- std::string* contents);
-
-} // namespace quic
-
-#endif // QUICHE_QUIC_PLATFORM_API_QUIC_FILE_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc
index 788cb2d5425..398f44a4a75 100644
--- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc
@@ -19,10 +19,7 @@ TEST_F(QuicHostnameUtilsTest, IsValidSNI) {
// IP as SNI.
EXPECT_FALSE(QuicHostnameUtils::IsValidSNI("192.168.0.1"));
// SNI without any dot.
- SetQuicReloadableFlag(quic_and_tls_allow_sni_without_dots, true);
EXPECT_TRUE(QuicHostnameUtils::IsValidSNI("somedomain"));
- SetQuicReloadableFlag(quic_and_tls_allow_sni_without_dots, false);
- EXPECT_FALSE(QuicHostnameUtils::IsValidSNI("somedomain"));
// Invalid by RFC2396 but unfortunately domains of this form exist.
EXPECT_TRUE(QuicHostnameUtils::IsValidSNI("some_domain.com"));
// An empty string must be invalid otherwise the QUIC client will try sending
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_map_util.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_map_util.h
deleted file mode 100644
index 95daec82c30..00000000000
--- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_map_util.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2017 The Chromium 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 QUICHE_QUIC_PLATFORM_API_QUIC_MAP_UTIL_H_
-#define QUICHE_QUIC_PLATFORM_API_QUIC_MAP_UTIL_H_
-
-#include "net/quic/platform/impl/quic_map_util_impl.h"
-
-namespace quic {
-
-template <class Collection, class Key>
-bool QuicContainsKey(const Collection& collection, const Key& key) {
- return QuicContainsKeyImpl(collection, key);
-}
-
-template <typename Collection, typename Value>
-bool QuicContainsValue(const Collection& collection, const Value& value) {
- return QuicContainsValueImpl(collection, value);
-}
-
-} // namespace quic
-
-#endif // QUICHE_QUIC_PLATFORM_API_QUIC_MAP_UTIL_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h
index 32e64575b25..adc03d45d8c 100644
--- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h
@@ -6,6 +6,8 @@
#define QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_H_
#include <memory>
+
+#include "absl/strings/string_view.h"
#include "quic/platform/api/quic_export.h"
#include "net/quic/platform/impl/quic_mem_slice_impl.h"
@@ -31,9 +33,20 @@ class QUIC_EXPORT_PRIVATE QuicMemSlice {
// Constructs a QuicMemSlice that takes ownership of |buffer|. |length| must
// not be zero. To construct an empty QuicMemSlice, use the zero-argument
// constructor instead.
+ // TODO(vasilvv): switch all users to QuicBuffer version, and make this
+ // private.
QuicMemSlice(QuicUniqueBufferPtr buffer, size_t length)
: impl_(std::move(buffer), length) {}
+ // Constructs a QuicMemSlice that takes ownership of |buffer|. The length of
+ // the |buffer| must not be zero. To construct an empty QuicMemSlice, use the
+ // zero-argument constructor instead.
+ explicit QuicMemSlice(QuicBuffer buffer) : QuicMemSlice() {
+ // Store the size of the buffer *before* calling buffer.Release().
+ const size_t size = buffer.size();
+ *this = QuicMemSlice(buffer.Release(), size);
+ }
+
// Constructs a QuicMemSlice that takes ownership of |buffer| allocated on
// heap. |length| must not be zero.
QuicMemSlice(std::unique_ptr<char[]> buffer, size_t length)
@@ -61,6 +74,10 @@ class QUIC_EXPORT_PRIVATE QuicMemSlice {
const char* data() const { return impl_.data(); }
// Returns the length of underlying data buffer.
size_t length() const { return impl_.length(); }
+ // Returns the representation of the underlying data as a string view.
+ absl::string_view AsStringView() const {
+ return absl::string_view(data(), length());
+ }
bool empty() const { return impl_.empty(); }
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_test.cc
index 380c462f880..44095dffa01 100644
--- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_test.cc
@@ -5,6 +5,9 @@
#include "quic/platform/api/quic_mem_slice.h"
#include <memory>
+
+#include "absl/strings/string_view.h"
+#include "quic/core/quic_buffer_allocator.h"
#include "quic/core/quic_simple_buffer_allocator.h"
#include "quic/platform/api/quic_test.h"
@@ -56,6 +59,18 @@ TEST_F(QuicMemSliceTest, SliceAllocatedOnHeap) {
EXPECT_EQ(moved.length(), used_length);
}
+TEST_F(QuicMemSliceTest, SliceFromBuffer) {
+ const absl::string_view kTestString =
+ "RFC 9000 Release Celebration Memorial Test String";
+ auto buffer = QuicBuffer::Copy(&allocator_, kTestString);
+ QuicMemSlice slice(std::move(buffer));
+
+ EXPECT_EQ(buffer.data(), nullptr); // NOLINT(bugprone-use-after-move)
+ EXPECT_EQ(buffer.size(), 0u);
+ EXPECT_EQ(slice.AsStringView(), kTestString);
+ EXPECT_EQ(slice.length(), kTestString.length());
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller.cc
index c8a6517a4c3..dd996c5c55b 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller.cc
+++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller.cc
@@ -49,6 +49,10 @@ bool TunDeviceController::UpdateAddress(const IpRange& desired_range) {
if (address_updated) {
current_address_ = desired_address;
+
+ for (const auto& cb : address_update_cbs_) {
+ cb(current_address_);
+ }
}
return address_updated;
@@ -110,6 +114,19 @@ bool TunDeviceController::UpdateRoutes(
return true;
}
+bool TunDeviceController::UpdateRoutesWithRetries(
+ const IpRange& desired_range,
+ const std::vector<IpRange>& desired_routes,
+ int retries) {
+ while (retries-- > 0) {
+ if (UpdateRoutes(desired_range, desired_routes)) {
+ return true;
+ }
+ absl::SleepFor(absl::Milliseconds(100));
+ }
+ return false;
+}
+
bool TunDeviceController::UpdateRules(IpRange desired_range) {
if (!absl::GetFlag(FLAGS_qbone_tun_device_replace_default_routing_rules)) {
return true;
@@ -148,4 +165,9 @@ QuicIpAddress TunDeviceController::current_address() {
return current_address_;
}
+void TunDeviceController::RegisterAddressUpdateCallback(
+ const std::function<void(QuicIpAddress)>& cb) {
+ address_update_cbs_.push_back(cb);
+}
+
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller.h
index 6854521014f..612e98f5222 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller.h
+++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller.h
@@ -39,6 +39,20 @@ class TunDeviceController {
virtual bool UpdateRoutes(const IpRange& desired_range,
const std::vector<IpRange>& desired_routes);
+ // Same as UpdateRoutes, but will wait and retry up to the number of times
+ // given by |retries| before giving up. This is an unpleasant workaround to
+ // deal with older kernels that aren't always able to set a route with a
+ // source address immediately after adding the address to the interface.
+ //
+ // TODO(b/179430548): Remove this once we've root-caused the underlying issue.
+ virtual bool UpdateRoutesWithRetries(
+ const IpRange& desired_range,
+ const std::vector<IpRange>& desired_routes,
+ int retries);
+
+ virtual void RegisterAddressUpdateCallback(
+ const std::function<void(QuicIpAddress)>& cb);
+
virtual QuicIpAddress current_address();
private:
@@ -51,6 +65,8 @@ class TunDeviceController {
NetlinkInterface* netlink_;
QuicIpAddress current_address_;
+
+ std::vector<std::function<void(QuicIpAddress)>> address_update_cbs_;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller_test.cc
index 73a7abc2864..53e5b3c14c2 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_controller_test.cc
@@ -44,8 +44,10 @@ class TunDeviceControllerTest : public QuicTest {
public:
TunDeviceControllerTest()
: controller_(kIfname, true, &netlink_),
- link_local_range_(
- *QboneConstants::TerminatorLocalAddressRange()) {}
+ link_local_range_(*QboneConstants::TerminatorLocalAddressRange()) {
+ controller_.RegisterAddressUpdateCallback(
+ [this](QuicIpAddress address) { notified_address_ = address; });
+ }
protected:
void ExpectLinkInfo(const std::string& interface_name, int ifindex) {
@@ -60,6 +62,7 @@ class TunDeviceControllerTest : public QuicTest {
MockNetlink netlink_;
TunDeviceController controller_;
+ QuicIpAddress notified_address_;
IpRange link_local_range_;
};
@@ -77,6 +80,7 @@ TEST_F(TunDeviceControllerTest, AddressAppliedWhenNoneExisted) {
.WillOnce(Return(true));
EXPECT_TRUE(controller_.UpdateAddress(kIpRange));
+ EXPECT_THAT(notified_address_, Eq(kIpRange.FirstAddressInRange()));
}
TEST_F(TunDeviceControllerTest, OldAddressesAreRemoved) {
@@ -110,6 +114,7 @@ TEST_F(TunDeviceControllerTest, OldAddressesAreRemoved) {
.WillOnce(Return(true));
EXPECT_TRUE(controller_.UpdateAddress(kIpRange));
+ EXPECT_THAT(notified_address_, Eq(kIpRange.FirstAddressInRange()));
}
TEST_F(TunDeviceControllerTest, UpdateRoutesRemovedOldRoutes) {
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc
index 52196239d13..985632ab72c 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc
+++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc
@@ -12,7 +12,6 @@
#include "quic/core/crypto/quic_random.h"
#include "quic/platform/api/quic_ip_address.h"
#include "quic/platform/api/quic_logging.h"
-#include "net/quic/platform/impl/quic_ip_address_impl.h"
#include "quic/qbone/platform/rtnetlink_message.h"
namespace quic {
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc
index 944257f7af2..7303b06091a 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc
+++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc
@@ -4,6 +4,8 @@
#include "quic/qbone/qbone_control_stream.h"
+#include <cstdint>
+#include <limits>
#include "absl/strings/string_view.h"
#include "quic/core/quic_session.h"
#include "quic/platform/api/quic_bug_tracker.h"
@@ -51,10 +53,10 @@ bool QboneControlStreamBase::SendMessage(const proto2::Message& proto) {
QUIC_BUG(quic_bug_11023_1) << "Failed to serialize QboneControlRequest";
return false;
}
- if (tmp.size() > kuint16max) {
+ if (tmp.size() > std::numeric_limits<uint16_t>::max()) {
QUIC_BUG(quic_bug_11023_2)
<< "QboneControlRequest too large: " << tmp.size() << " > "
- << kuint16max;
+ << std::numeric_limits<uint16_t>::max();
return false;
}
uint16_t size = tmp.size();
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.cc
index 5ee52e1e065..f8acb4f6a86 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.cc
+++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger.cc
@@ -14,7 +14,7 @@ bool QbonePacketExchanger::ReadAndDeliverPacket(
std::string error;
std::unique_ptr<QuicData> packet = ReadPacket(&blocked, &error);
if (packet == nullptr) {
- if (!blocked) {
+ if (!blocked && visitor_) {
visitor_->OnReadError(error);
}
return false;
@@ -31,11 +31,14 @@ void QbonePacketExchanger::WritePacketToNetwork(const char* packet,
if (WritePacket(packet, size, &blocked, &error)) {
return;
}
- if (!blocked) {
- visitor_->OnWriteError(error);
- return;
+ if (blocked) {
+ write_blocked_ = true;
+ } else {
+ QUIC_LOG_EVERY_N_SEC(ERROR, 60) << "Packet write failed: " << error;
+ if (visitor_) {
+ visitor_->OnWriteError(error);
+ }
}
- write_blocked_ = true;
}
// Drop the packet on the floor if the queue if full.
@@ -58,7 +61,7 @@ void QbonePacketExchanger::SetWritable() {
packet_queue_.front()->length(), &blocked, &error)) {
packet_queue_.pop_front();
} else {
- if (!blocked) {
+ if (!blocked && visitor_) {
visitor_->OnWriteError(error);
}
write_blocked_ = blocked;
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger_test.cc
index a2b502fa26f..5db87536aba 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_exchanger_test.cc
@@ -251,5 +251,21 @@ TEST(QbonePacketExchangerTest, WriteErrorsGetNotified) {
ASSERT_TRUE(exchanger.packets_written().empty());
}
+TEST(QbonePacketExchangerTest, NullVisitorDoesntCrash) {
+ FakeQbonePacketExchanger exchanger(nullptr, kMaxPendingPackets);
+ MockQboneClient client;
+ std::string packet = "data";
+
+ // Force read error.
+ std::string io_error = "I/O error";
+ exchanger.SetReadError(io_error);
+ EXPECT_FALSE(exchanger.ReadAndDeliverPacket(&client));
+
+ // Force write error
+ exchanger.ForceWriteFailure(false, io_error);
+ exchanger.WritePacketToNetwork(packet.data(), packet.length());
+ EXPECT_TRUE(exchanger.packets_written().empty());
+}
+
} // namespace
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc
index 7b45ed1050b..6e83e760f3b 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc
+++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc
@@ -103,6 +103,10 @@ void QbonePacketProcessor::ProcessPacket(std::string* packet,
SendTcpReset(*packet, direction);
stats_->OnPacketDroppedWithTcpReset(direction);
break;
+ case ProcessingResult::TCP_RESET:
+ SendTcpReset(*packet, direction);
+ stats_->OnPacketDroppedWithTcpReset(direction);
+ break;
}
}
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h
index c85280fe18f..8c2375a288c 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h
+++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h
@@ -46,6 +46,8 @@ class QbonePacketProcessor {
// RST requires information from the current connection state to be
// well-formed.
ICMP_AND_TCP_RESET = 4,
+ // Send a TCP RST.
+ TCP_RESET = 5,
};
class OutputInterface {
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc
index e984196faf0..2f2f31e3239 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc
+++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc
@@ -140,11 +140,9 @@ void QboneSessionBase::SendPacketToPeer(absl::string_view packet) {
}
if (send_packets_as_messages_) {
- QuicUniqueBufferPtr buffer = MakeUniqueBuffer(
- connection()->helper()->GetStreamSendBufferAllocator(), packet.size());
- memcpy(buffer.get(), packet.data(), packet.size());
- QuicMemSlice slice(std::move(buffer), packet.size());
- switch (SendMessage(QuicMemSliceSpan(&slice), /*flush=*/true).status) {
+ QuicMemSlice slice(QuicBuffer::Copy(
+ connection()->helper()->GetStreamSendBufferAllocator(), packet));
+ switch (SendMessage(absl::MakeSpan(&slice, 1), /*flush=*/true).status) {
case MESSAGE_STATUS_SUCCESS:
break;
case MESSAGE_STATUS_TOO_LARGE: {
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc
index fb9a288cbeb..9887f79a46e 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc
@@ -117,6 +117,14 @@ class IndirectionProofSource : public ProofSource {
std::move(callback));
}
+ absl::InlinedVector<uint16_t, 8> SupportedTlsSignatureAlgorithms()
+ const override {
+ if (!proof_source_) {
+ return {};
+ }
+ return proof_source_->SupportedTlsSignatureAlgorithms();
+ }
+
TicketCrypter* GetTicketCrypter() override { return nullptr; }
private:
diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc
index bcbc5a514e8..fc2ce819d90 100644
--- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc
@@ -40,12 +40,10 @@ class MockQuicSession : public QboneSessionBase {
~MockQuicSession() override {}
// Writes outgoing data from QuicStream to a string.
- QuicConsumedData WritevData(QuicStreamId id,
- size_t write_length,
- QuicStreamOffset offset,
- StreamSendingState state,
+ QuicConsumedData WritevData(QuicStreamId id, size_t write_length,
+ QuicStreamOffset offset, StreamSendingState state,
TransmissionType type,
- absl::optional<EncryptionLevel> level) override {
+ EncryptionLevel level) override {
if (!writable_) {
return QuicConsumedData(0, false);
}
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc
index 00264e59a09..d28b4faff85 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc
@@ -743,6 +743,12 @@ void MovePackets(PacketSavingConnection* source_conn,
size_t index = *inout_packet_index;
for (; index < source_conn->encrypted_packets_.size(); index++) {
+ if (!dest_conn->connected()) {
+ QUIC_LOG(INFO)
+ << "Destination connection disconnected. Skipping packet at index "
+ << index;
+ continue;
+ }
// In order to properly test the code we need to perform encryption and
// decryption so that the crypters latch when expected. The crypters are in
// |dest_conn|, but we don't want to try and use them there. Instead we swap
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h b/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h
index 4f771a33ebc..447b77066bd 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h
@@ -34,6 +34,11 @@ class FailingProofSource : public ProofSource {
absl::string_view in,
std::unique_ptr<SignatureCallback> callback) override;
+ absl::InlinedVector<uint16_t, 8> SupportedTlsSignatureAlgorithms()
+ const override {
+ return {};
+ }
+
TicketCrypter* GetTicketCrypter() override { return nullptr; }
};
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc
index bc75678872f..1109d659fac 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc
@@ -123,6 +123,11 @@ void FakeProofSource::ComputeTlsSignature(
std::move(callback), delegate_.get()));
}
+absl::InlinedVector<uint16_t, 8>
+FakeProofSource::SupportedTlsSignatureAlgorithms() const {
+ return delegate_->SupportedTlsSignatureAlgorithms();
+}
+
ProofSource::TicketCrypter* FakeProofSource::GetTicketCrypter() {
if (ticket_crypter_) {
return ticket_crypter_.get();
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h
index 077f34d074f..c088d43a9bb 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h
@@ -52,6 +52,8 @@ class FakeProofSource : public ProofSource {
uint16_t signature_algorithm,
absl::string_view in,
std::unique_ptr<ProofSource::SignatureCallback> callback) override;
+ absl::InlinedVector<uint16_t, 8> SupportedTlsSignatureAlgorithms()
+ const override;
TicketCrypter* GetTicketCrypter() override;
// Sets the TicketCrypter to use. If nullptr, the TicketCrypter from
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.cc b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.cc
index b1fedf8c161..f34247e9bae 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.cc
@@ -63,9 +63,10 @@ FakeProofSourceHandle::FakeProofSourceHandle(
select_cert_action_(select_cert_action),
compute_signature_action_(compute_signature_action) {}
-void FakeProofSourceHandle::CancelPendingOperation() {
+void FakeProofSourceHandle::CloseHandle() {
select_cert_op_.reset();
compute_signature_op_.reset();
+ closed_ = true;
}
QuicAsyncStatus FakeProofSourceHandle::SelectCertificate(
@@ -77,10 +78,12 @@ QuicAsyncStatus FakeProofSourceHandle::SelectCertificate(
const std::string& alpn,
absl::optional<std::string> alps,
const std::vector<uint8_t>& quic_transport_params,
- const absl::optional<std::vector<uint8_t>>& early_data_context) {
+ const absl::optional<std::vector<uint8_t>>& early_data_context,
+ const QuicSSLConfig& ssl_config) {
+ QUICHE_CHECK(!closed_);
all_select_cert_args_.push_back(SelectCertArgs(
server_address, client_address, ssl_capabilities, hostname, client_hello,
- alpn, alps, quic_transport_params, early_data_context));
+ alpn, alps, quic_transport_params, early_data_context, ssl_config));
if (select_cert_action_ == Action::DELEGATE_ASYNC ||
select_cert_action_ == Action::FAIL_ASYNC) {
@@ -90,7 +93,8 @@ QuicAsyncStatus FakeProofSourceHandle::SelectCertificate(
} else if (select_cert_action_ == Action::FAIL_SYNC) {
callback()->OnSelectCertificateDone(
/*ok=*/false,
- /*is_sync=*/true, nullptr, /*handshake_hints=*/absl::string_view());
+ /*is_sync=*/true, nullptr, /*handshake_hints=*/absl::string_view(),
+ /*ticket_encryption_key=*/absl::string_view());
return QUIC_FAILURE;
}
@@ -99,8 +103,10 @@ QuicAsyncStatus FakeProofSourceHandle::SelectCertificate(
delegate_->GetCertChain(server_address, client_address, hostname);
bool ok = chain && !chain->certs.empty();
- callback_->OnSelectCertificateDone(ok, /*is_sync=*/true, chain.get(),
- /*handshake_hints=*/absl::string_view());
+ callback_->OnSelectCertificateDone(
+ ok, /*is_sync=*/true, chain.get(),
+ /*handshake_hints=*/absl::string_view(),
+ /*ticket_encryption_key=*/absl::string_view());
return ok ? QUIC_SUCCESS : QUIC_FAILURE;
}
@@ -111,6 +117,7 @@ QuicAsyncStatus FakeProofSourceHandle::ComputeSignature(
uint16_t signature_algorithm,
absl::string_view in,
size_t max_signature_size) {
+ QUICHE_CHECK(!closed_);
all_compute_signature_args_.push_back(
ComputeSignatureArgs(server_address, client_address, hostname,
signature_algorithm, in, max_signature_size));
@@ -171,16 +178,20 @@ FakeProofSourceHandle::SelectCertOperation::SelectCertOperation(
void FakeProofSourceHandle::SelectCertOperation::Run() {
if (action_ == Action::FAIL_ASYNC) {
- callback_->OnSelectCertificateDone(/*ok=*/false,
- /*is_sync=*/false, nullptr,
- /*handshake_hints=*/absl::string_view());
+ callback_->OnSelectCertificateDone(
+ /*ok=*/false,
+ /*is_sync=*/false, nullptr,
+ /*handshake_hints=*/absl::string_view(),
+ /*ticket_encryption_key=*/absl::string_view());
} else if (action_ == Action::DELEGATE_ASYNC) {
QuicReferenceCountedPointer<ProofSource::Chain> chain =
delegate_->GetCertChain(args_.server_address, args_.client_address,
args_.hostname);
bool ok = chain && !chain->certs.empty();
- callback_->OnSelectCertificateDone(ok, /*is_sync=*/false, chain.get(),
- /*handshake_hints=*/absl::string_view());
+ callback_->OnSelectCertificateDone(
+ ok, /*is_sync=*/false, chain.get(),
+ /*handshake_hints=*/absl::string_view(),
+ /*ticket_encryption_key=*/absl::string_view());
} else {
QUIC_BUG(quic_bug_10139_1)
<< "Unexpected action: " << static_cast<int>(action_);
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.h b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.h
index d2b991896f8..3d038a4d58b 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.h
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.h
@@ -35,7 +35,7 @@ class FakeProofSourceHandle : public ProofSourceHandle {
~FakeProofSourceHandle() override = default;
- void CancelPendingOperation() override;
+ void CloseHandle() override;
QuicAsyncStatus SelectCertificate(
const QuicSocketAddress& server_address,
@@ -46,7 +46,8 @@ class FakeProofSourceHandle : public ProofSourceHandle {
const std::string& alpn,
absl::optional<std::string> alps,
const std::vector<uint8_t>& quic_transport_params,
- const absl::optional<std::vector<uint8_t>>& early_data_context) override;
+ const absl::optional<std::vector<uint8_t>>& early_data_context,
+ const QuicSSLConfig& ssl_config) override;
QuicAsyncStatus ComputeSignature(const QuicSocketAddress& server_address,
const QuicSocketAddress& client_address,
@@ -70,7 +71,8 @@ class FakeProofSourceHandle : public ProofSourceHandle {
std::string alpn,
absl::optional<std::string> alps,
std::vector<uint8_t> quic_transport_params,
- absl::optional<std::vector<uint8_t>> early_data_context)
+ absl::optional<std::vector<uint8_t>> early_data_context,
+ QuicSSLConfig ssl_config)
: server_address(server_address),
client_address(client_address),
ssl_capabilities(ssl_capabilities),
@@ -79,7 +81,8 @@ class FakeProofSourceHandle : public ProofSourceHandle {
alpn(alpn),
alps(alps),
quic_transport_params(quic_transport_params),
- early_data_context(early_data_context) {}
+ early_data_context(early_data_context),
+ ssl_config(ssl_config) {}
QuicSocketAddress server_address;
QuicSocketAddress client_address;
@@ -90,6 +93,7 @@ class FakeProofSourceHandle : public ProofSourceHandle {
absl::optional<std::string> alps;
std::vector<uint8_t> quic_transport_params;
absl::optional<std::vector<uint8_t>> early_data_context;
+ QuicSSLConfig ssl_config;
};
struct ComputeSignatureArgs {
@@ -171,6 +175,7 @@ class FakeProofSourceHandle : public ProofSourceHandle {
private:
int NumPendingOperations() const;
+ bool closed_ = false;
ProofSource* delegate_;
ProofSourceHandleCallback* callback_;
// Action for the next select cert operation.
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc
index 35cc9785209..76dd778734c 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc
@@ -68,7 +68,14 @@ PacketDroppingTestWriter::PacketDroppingTestWriter()
simple_random_.set_seed(seed);
}
-PacketDroppingTestWriter::~PacketDroppingTestWriter() = default;
+PacketDroppingTestWriter::~PacketDroppingTestWriter() {
+ if (write_unblocked_alarm_ != nullptr) {
+ write_unblocked_alarm_->PermanentCancel();
+ }
+ if (delay_alarm_ != nullptr) {
+ delay_alarm_->PermanentCancel();
+ }
+}
void PacketDroppingTestWriter::Initialize(
QuicConnectionHelperInterface* helper,
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_offline_decoder.cc b/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_offline_decoder.cc
index 0f681fef83a..a7ea1be114b 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_offline_decoder.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_offline_decoder.cc
@@ -35,9 +35,9 @@
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "quic/core/quic_types.h"
-#include "quic/platform/api/quic_file_utils.h"
#include "quic/platform/api/quic_logging.h"
#include "quic/test_tools/qpack/qpack_test_utils.h"
+#include "common/platform/api/quiche_file_utils.h"
#include "common/quiche_endian.h"
namespace quic {
@@ -69,8 +69,7 @@ bool QpackOfflineDecoder::DecodeAndVerifyOfflineData(
}
void QpackOfflineDecoder::OnEncoderStreamError(
- QuicErrorCode error_code,
- absl::string_view error_message) {
+ QuicErrorCode error_code, absl::string_view error_message) {
QUIC_LOG(ERROR) << "Encoder stream error: "
<< QuicErrorCodeToString(error_code) << " " << error_message;
encoder_stream_error_detected_ = true;
@@ -87,12 +86,7 @@ bool QpackOfflineDecoder::ParseInputFilename(absl::string_view input_filename) {
auto piece_it = pieces.rbegin();
// Acknowledgement mode: 1 for immediate, 0 for none.
- bool immediate_acknowledgement = false;
- if (*piece_it == "0") {
- immediate_acknowledgement = false;
- } else if (*piece_it == "1") {
- immediate_acknowledgement = true;
- } else {
+ if (*piece_it != "0" && *piece_it != "1") {
QUIC_LOG(ERROR)
<< "Header acknowledgement field must be 0 or 1 in input filename "
<< input_filename;
@@ -136,9 +130,10 @@ bool QpackOfflineDecoder::DecodeHeaderBlocksFromFile(
absl::string_view input_filename) {
// Store data in |input_data_storage|; use a absl::string_view to
// efficiently keep track of remaining portion yet to be decoded.
- std::string input_data_storage;
- ReadFileContents(input_filename, &input_data_storage);
- absl::string_view input_data(input_data_storage);
+ absl::optional<std::string> input_data_storage =
+ quiche::ReadFileContents(input_filename);
+ QUICHE_DCHECK(input_data_storage.has_value());
+ absl::string_view input_data(*input_data_storage);
while (!input_data.empty()) {
// Parse stream_id and length.
@@ -233,9 +228,10 @@ bool QpackOfflineDecoder::VerifyDecodedHeaderLists(
// Store data in |expected_headers_data_storage|; use a
// absl::string_view to efficiently keep track of remaining portion
// yet to be decoded.
- std::string expected_headers_data_storage;
- ReadFileContents(expected_headers_filename, &expected_headers_data_storage);
- absl::string_view expected_headers_data(expected_headers_data_storage);
+ absl::optional<std::string> expected_headers_data_storage =
+ quiche::ReadFileContents(expected_headers_filename);
+ QUICHE_DCHECK(expected_headers_data_storage.has_value());
+ absl::string_view expected_headers_data(*expected_headers_data_storage);
while (!decoded_header_lists_.empty()) {
spdy::Http2HeaderBlock decoded_header_list =
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc
index 71036cb2512..43c345094ef 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc
@@ -398,11 +398,7 @@ QuicIdleNetworkDetector& QuicConnectionPeer::GetIdleNetworkDetector(
void QuicConnectionPeer::SetServerConnectionId(
QuicConnection* connection,
const QuicConnectionId& server_connection_id) {
- if (connection->use_connection_id_on_default_path_) {
- connection->default_path_.server_connection_id = server_connection_id;
- } else {
- connection->server_connection_id_ = server_connection_id;
- }
+ connection->default_path_.server_connection_id = server_connection_id;
connection->InstallInitialCrypters(server_connection_id);
}
@@ -430,7 +426,7 @@ void QuicConnectionPeer::SendPing(QuicConnection* connection) {
void QuicConnectionPeer::SetLastPacketDestinationAddress(
QuicConnection* connection,
const QuicSocketAddress& address) {
- connection->last_packet_destination_address_ = address;
+ connection->last_received_packet_info_.destination_address = address;
}
// static
@@ -525,5 +521,12 @@ bool QuicConnectionPeer::HasSelfIssuedConnectionIdToConsume(
return connection->self_issued_cid_manager_->HasConnectionIdToConsume();
}
+// static
+QuicSelfIssuedConnectionIdManager*
+QuicConnectionPeer::GetSelfIssuedConnectionIdManager(
+ QuicConnection* connection) {
+ return connection->self_issued_cid_manager_.get();
+}
+
} // namespace test
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h
index cbc829a7a65..99b8b94fd74 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h
@@ -213,6 +213,9 @@ class QuicConnectionPeer {
static bool HasSelfIssuedConnectionIdToConsume(
const QuicConnection* connection);
+
+ static QuicSelfIssuedConnectionIdManager* GetSelfIssuedConnectionIdManager(
+ QuicConnection* connection);
};
} // namespace test
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.cc
index 14cc59418f5..8692a25c685 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.cc
@@ -6,7 +6,6 @@
#include "quic/core/quic_framer.h"
#include "quic/core/quic_packets.h"
-#include "quic/platform/api/quic_map_util.h"
namespace quic {
namespace test {
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc
index a3dbf843e58..e734a794aea 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc
@@ -8,7 +8,6 @@
#include "quic/core/quic_session.h"
#include "quic/core/quic_stream.h"
#include "quic/core/quic_utils.h"
-#include "quic/platform/api/quic_map_util.h"
namespace quic {
namespace test {
@@ -152,24 +151,20 @@ bool QuicSessionPeer::IsStreamClosed(QuicSession* session, QuicStreamId id) {
// static
bool QuicSessionPeer::IsStreamCreated(QuicSession* session, QuicStreamId id) {
- return QuicContainsKey(session->stream_map_, id);
+ return session->stream_map_.contains(id);
}
// static
bool QuicSessionPeer::IsStreamAvailable(QuicSession* session, QuicStreamId id) {
if (VersionHasIetfQuicFrames(session->transport_version())) {
if (id % QuicUtils::StreamIdDelta(session->transport_version()) < 2) {
- return QuicContainsKey(
- session->ietf_streamid_manager_.bidirectional_stream_id_manager_
- .available_streams_,
- id);
+ return session->ietf_streamid_manager_.bidirectional_stream_id_manager_
+ .available_streams_.contains(id);
}
- return QuicContainsKey(
- session->ietf_streamid_manager_.unidirectional_stream_id_manager_
- .available_streams_,
- id);
+ return session->ietf_streamid_manager_.unidirectional_stream_id_manager_
+ .available_streams_.contains(id);
}
- return QuicContainsKey(session->stream_id_manager_.available_streams_, id);
+ return session->stream_id_manager_.available_streams_.contains(id);
}
// static
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc
index c547ac22610..a27f073e993 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc
@@ -106,8 +106,13 @@ void QuicSpdySessionPeer::SetH3DatagramSupported(QuicSpdySession* session,
}
// static
+bool QuicSpdySessionPeer::ShouldNegotiateHttp3Datagram(
+ QuicSpdySession* session) {
+ return session->ShouldNegotiateHttp3Datagram();
+}
+
+// static
void QuicSpdySessionPeer::EnableWebTransport(QuicSpdySession& session) {
- SetQuicReloadableFlag(quic_h3_datagram, true);
QUICHE_DCHECK(session.WillNegotiateWebTransport());
session.h3_datagram_supported_ = true;
session.peer_supports_webtransport_ = true;
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h
index 9ba54979e9a..0d06c4ddfad 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h
@@ -52,6 +52,7 @@ class QuicSpdySessionPeer {
QuicSpdySession* session);
static void SetH3DatagramSupported(QuicSpdySession* session,
bool h3_datagram_supported);
+ static bool ShouldNegotiateHttp3Datagram(QuicSpdySession* session);
static void EnableWebTransport(QuicSpdySession& session);
};
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc
index 7cc3ff9be3b..598e14cb1b0 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc
@@ -618,16 +618,12 @@ const std::string& QuicTestClient::cert_sct() const {
->cert_sct();
}
-QuicTagValueMap QuicTestClient::GetServerConfig() const {
+const QuicTagValueMap& QuicTestClient::GetServerConfig() const {
QuicCryptoClientConfig* config = client_->crypto_config();
- QuicCryptoClientConfig::CachedState* state =
+ const QuicCryptoClientConfig::CachedState* state =
config->LookupOrCreate(client_->server_id());
const CryptoHandshakeMessage* handshake_msg = state->GetServerConfig();
- if (handshake_msg != nullptr) {
- return handshake_msg->tag_value_map();
- } else {
- return QuicTagValueMap();
- }
+ return handshake_msg->tag_value_map();
}
bool QuicTestClient::connected() const {
@@ -786,7 +782,7 @@ void QuicTestClient::OnClose(QuicSpdyStream* stream) {
// written.
client()->OnClose(stream);
++num_responses_;
- if (!QuicContainsKey(open_streams_, stream->id())) {
+ if (open_streams_.find(stream->id()) == open_streams_.end()) {
return;
}
if (latest_created_stream_ == stream) {
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h
index d764ca9d2a5..66ebb5bc736 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h
@@ -14,11 +14,10 @@
#include "quic/core/quic_framer.h"
#include "quic/core/quic_packet_creator.h"
#include "quic/core/quic_packets.h"
-#include "quic/platform/api/quic_containers.h"
#include "quic/platform/api/quic_epoll.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/platform/api/quic_test.h"
#include "quic/tools/quic_client.h"
+#include "common/quiche_linked_hash_map.h"
namespace quic {
@@ -282,8 +281,8 @@ class QuicTestClient : public QuicSpdyStream::Visitor,
// or the empty std::string if no signed timestamp was presented.
const std::string& cert_sct() const;
- // Get the server config map.
- QuicTagValueMap GetServerConfig() const;
+ // Get the server config map. Server config must exist.
+ const QuicTagValueMap& GetServerConfig() const;
void set_auto_reconnect(bool reconnect) { auto_reconnect_ = reconnect; }
@@ -401,7 +400,8 @@ class QuicTestClient : public QuicSpdyStream::Visitor,
QuicSpdyClientStream* latest_created_stream_;
std::map<QuicStreamId, QuicSpdyClientStream*> open_streams_;
// Received responses of closed streams.
- QuicLinkedHashMap<QuicStreamId, PerStreamState> closed_stream_states_;
+ quiche::QuicheLinkedHashMap<QuicStreamId, PerStreamState>
+ closed_stream_states_;
QuicRstStreamErrorCode stream_error_;
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc
index 5feb6462321..126b87e039c 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc
@@ -191,7 +191,11 @@ std::unique_ptr<QuicPacket> BuildUnsizedDataPacket(
EncryptionLevel level = HeaderToEncryptionLevel(header);
size_t length =
framer->BuildDataPacket(header, frames, buffer, packet_size, level);
- QUICHE_DCHECK_NE(0u, length);
+
+ if (length == 0) {
+ delete[] buffer;
+ return nullptr;
+ }
// Re-construct the data packet with data ownership.
return std::make_unique<QuicPacket>(
buffer, length, /* owns_buffer */ true,
@@ -1323,10 +1327,12 @@ QuicMemSliceSpan MakeSpan(QuicBufferAllocator* allocator,
}
QuicMemSlice MemSliceFromString(absl::string_view data) {
+ if (data.empty()) {
+ return QuicMemSlice();
+ }
+
static SimpleBufferAllocator* allocator = new SimpleBufferAllocator();
- QuicUniqueBufferPtr buffer = MakeUniqueBuffer(allocator, data.size());
- memcpy(buffer.get(), data.data(), data.size());
- return QuicMemSlice(std::move(buffer), data.size());
+ return QuicMemSlice(QuicBuffer::Copy(allocator, data));
}
bool TaggingEncrypter::EncryptPacket(uint64_t /*packet_number*/,
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h
index 9fbffe0b76c..06f3eeca7c2 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h
@@ -282,7 +282,7 @@ class SimpleRandom : public QuicRandom {
private:
uint8_t buffer_[4096];
- size_t buffer_offset_;
+ size_t buffer_offset_ = 0;
uint8_t key_[32];
void FillBuffer();
@@ -763,10 +763,8 @@ class MockQuicConnection : public QuicConnection {
(QuicStreamId, QuicRstStreamErrorCode),
(override));
MOCK_METHOD(bool, SendControlFrame, (const QuicFrame& frame), (override));
- MOCK_METHOD(MessageStatus,
- SendMessage,
- (QuicMessageId, QuicMemSliceSpan, bool),
- (override));
+ MOCK_METHOD(MessageStatus, SendMessage,
+ (QuicMessageId, absl::Span<QuicMemSlice>, bool), (override));
MOCK_METHOD(bool,
SendPathChallenge,
(const QuicPathFrameBuffer&,
@@ -904,14 +902,10 @@ class MockQuicSession : public QuicSession {
CreateIncomingStream,
(PendingStream*),
(override));
- MOCK_METHOD(QuicConsumedData,
- WritevData,
- (QuicStreamId id,
- size_t write_length,
- QuicStreamOffset offset,
- StreamSendingState state,
- TransmissionType type,
- absl::optional<EncryptionLevel> level),
+ MOCK_METHOD(QuicConsumedData, WritevData,
+ (QuicStreamId id, size_t write_length, QuicStreamOffset offset,
+ StreamSendingState state, TransmissionType type,
+ EncryptionLevel level),
(override));
MOCK_METHOD(bool,
WriteControlFrame,
@@ -1042,14 +1036,10 @@ class MockQuicSpdySession : public QuicSpdySession {
MOCK_METHOD(bool, ShouldCreateIncomingStream, (QuicStreamId id), (override));
MOCK_METHOD(bool, ShouldCreateOutgoingBidirectionalStream, (), (override));
MOCK_METHOD(bool, ShouldCreateOutgoingUnidirectionalStream, (), (override));
- MOCK_METHOD(QuicConsumedData,
- WritevData,
- (QuicStreamId id,
- size_t write_length,
- QuicStreamOffset offset,
- StreamSendingState state,
- TransmissionType type,
- absl::optional<EncryptionLevel> level),
+ MOCK_METHOD(QuicConsumedData, WritevData,
+ (QuicStreamId id, size_t write_length, QuicStreamOffset offset,
+ StreamSendingState state, TransmissionType type,
+ EncryptionLevel level),
(override));
MOCK_METHOD(void,
MaybeSendRstStreamFrame,
@@ -1132,10 +1122,6 @@ class MockHttp3DebugVisitor : public Http3DebugVisitor {
(override));
MOCK_METHOD(void,
- OnCancelPushFrameReceived,
- (const CancelPushFrame&),
- (override));
- MOCK_METHOD(void,
OnSettingsFrameReceived,
(const SettingsFrame&),
(override));
@@ -1166,14 +1152,6 @@ class MockHttp3DebugVisitor : public Http3DebugVisitor {
(QuicStreamId, QuicHeaderList),
(override));
MOCK_METHOD(void,
- OnPushPromiseFrameReceived,
- (QuicStreamId, QuicStreamId, QuicByteCount),
- (override));
- MOCK_METHOD(void,
- OnPushPromiseDecoded,
- (QuicStreamId, QuicStreamId, QuicHeaderList),
- (override));
- MOCK_METHOD(void,
OnUnknownFrameReceived,
(QuicStreamId, uint64_t, QuicByteCount),
(override));
@@ -1191,10 +1169,6 @@ class MockHttp3DebugVisitor : public Http3DebugVisitor {
OnHeadersFrameSent,
(QuicStreamId, const spdy::SpdyHeaderBlock&),
(override));
- MOCK_METHOD(void,
- OnPushPromiseFrameSent,
- (QuicStreamId, QuicStreamId, const spdy::SpdyHeaderBlock&),
- (override));
};
class TestQuicSpdyServerSession : public QuicServerSessionBase {
@@ -1241,9 +1215,14 @@ class TestQuicSpdyServerSession : public QuicServerSessionBase {
MockQuicCryptoServerStreamHelper* helper() { return &helper_; }
+ QuicSSLConfig GetSSLConfig() const override { return ssl_config_; }
+
+ QuicSSLConfig* ssl_config() { return &ssl_config_; }
+
private:
MockQuicSessionVisitor visitor_;
MockQuicCryptoServerStreamHelper helper_;
+ QuicSSLConfig ssl_config_;
};
// A test implementation of QuicClientPushPromiseIndex::Delegate.
@@ -2318,31 +2297,49 @@ bool WriteServerVersionNegotiationProbeResponse(
uint8_t source_connection_id_length);
// Implementation of Http3DatagramVisitor which saves all received datagrams.
-class SavingHttp3DatagramVisitor
- : public QuicSpdySession::Http3DatagramVisitor {
+class SavingHttp3DatagramVisitor : public QuicSpdyStream::Http3DatagramVisitor {
public:
struct SavedHttp3Datagram {
- QuicDatagramFlowId flow_id;
+ QuicStreamId stream_id;
+ absl::optional<QuicDatagramContextId> context_id;
std::string payload;
bool operator==(const SavedHttp3Datagram& o) const {
- return flow_id == o.flow_id && payload == o.payload;
+ return stream_id == o.stream_id && context_id == o.context_id &&
+ payload == o.payload;
}
};
const std::vector<SavedHttp3Datagram>& received_h3_datagrams() const {
return received_h3_datagrams_;
}
- // Override from QuicSpdySession::Http3DatagramVisitor.
- void OnHttp3Datagram(QuicDatagramFlowId flow_id,
+ // Override from QuicSpdyStream::Http3DatagramVisitor.
+ void OnHttp3Datagram(QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
absl::string_view payload) override {
received_h3_datagrams_.push_back(
- SavedHttp3Datagram{flow_id, std::string(payload)});
+ SavedHttp3Datagram{stream_id, context_id, std::string(payload)});
}
private:
std::vector<SavedHttp3Datagram> received_h3_datagrams_;
};
+class MockHttp3DatagramRegistrationVisitor
+ : public QuicSpdyStream::Http3DatagramRegistrationVisitor {
+ public:
+ MOCK_METHOD(void, OnContextReceived,
+ (QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions),
+ (override));
+
+ MOCK_METHOD(void, OnContextClosed,
+ (QuicStreamId stream_id,
+ absl::optional<QuicDatagramContextId> context_id,
+ const Http3DatagramContextExtensions& extensions),
+ (override));
+};
+
} // namespace test
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.cc
index 5ef53f4bf23..413a840e32c 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.cc
@@ -10,7 +10,6 @@
#include "quic/core/quic_data_writer.h"
#include "quic/platform/api/quic_bug_tracker.h"
#include "quic/platform/api/quic_flags.h"
-#include "quic/platform/api/quic_map_util.h"
namespace quic {
@@ -28,7 +27,7 @@ void SimpleDataProducer::SaveStreamData(QuicStreamId id,
if (data_length == 0) {
return;
}
- if (!QuicContainsKey(send_buffer_map_, id)) {
+ if (!send_buffer_map_.contains(id)) {
send_buffer_map_[id] = std::make_unique<QuicStreamSendBuffer>(&allocator_);
}
send_buffer_map_[id]->SaveStreamData(iov, iov_count, iov_offset, data_length);
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc
index 51d4017948f..a1383b0e41d 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc
@@ -6,7 +6,6 @@
#include "quic/core/quic_utils.h"
#include "quic/platform/api/quic_logging.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/test_tools/quic_test_utils.h"
namespace quic {
@@ -40,7 +39,7 @@ QuicConsumedData SimpleSessionNotifier::WriteOrBufferData(
QuicStreamId id,
QuicByteCount data_length,
StreamSendingState state) {
- if (!QuicContainsKey(stream_map_, id)) {
+ if (!stream_map_.contains(id)) {
stream_map_[id] = StreamState();
}
StreamState& stream_state = stream_map_.find(id)->second;
@@ -163,16 +162,14 @@ void SimpleSessionNotifier::NeuterUnencryptedData() {
}
void SimpleSessionNotifier::OnCanWrite() {
- if (connection_->donot_write_mid_packet_processing()) {
- if (connection_->framer().is_processing_packet()) {
- // Do not write data in the middle of packet processing because rest
- // frames in the packet may change the data to write. For example, lost
- // data could be acknowledged. Also, connection is going to emit
- // OnCanWrite signal post packet processing.
- QUIC_BUG(simple_notifier_write_mid_packet_processing)
- << "Try to write mid packet processing.";
- return;
- }
+ if (connection_->framer().is_processing_packet()) {
+ // Do not write data in the middle of packet processing because rest
+ // frames in the packet may change the data to write. For example, lost
+ // data could be acknowledged. Also, connection is going to emit
+ // OnCanWrite signal post packet processing.
+ QUIC_BUG(simple_notifier_write_mid_packet_processing)
+ << "Try to write mid packet processing.";
+ return;
}
if (!RetransmitLostCryptoData() || !RetransmitLostControlFrames() ||
!RetransmitLostStreamData()) {
@@ -263,7 +260,7 @@ bool SimpleSessionNotifier::OnFrameAcked(const QuicFrame& frame,
if (frame.type != STREAM_FRAME) {
return OnControlFrameAcked(frame);
}
- if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ if (!stream_map_.contains(frame.stream_frame.stream_id)) {
return false;
}
auto* state = &stream_map_.find(frame.stream_frame.stream_id)->second;
@@ -304,7 +301,7 @@ void SimpleSessionNotifier::OnFrameLost(const QuicFrame& frame) {
OnControlFrameLost(frame);
return;
}
- if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ if (!stream_map_.contains(frame.stream_frame.stream_id)) {
return;
}
auto* state = &stream_map_.find(frame.stream_frame.stream_id)->second;
@@ -359,7 +356,7 @@ void SimpleSessionNotifier::RetransmitFrames(const QuicFrames& frames,
}
continue;
}
- if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ if (!stream_map_.contains(frame.stream_frame.stream_id)) {
continue;
}
const auto& state = stream_map_.find(frame.stream_frame.stream_id)->second;
@@ -437,7 +434,7 @@ bool SimpleSessionNotifier::IsFrameOutstanding(const QuicFrame& frame) const {
if (frame.type != STREAM_FRAME) {
return IsControlFrameOutstanding(frame);
}
- if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ if (!stream_map_.contains(frame.stream_frame.stream_id)) {
return false;
}
const auto& state = stream_map_.find(frame.stream_frame.stream_id)->second;
@@ -463,8 +460,8 @@ bool SimpleSessionNotifier::HasUnackedCryptoData() const {
}
return false;
}
- if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId(
- connection_->transport_version()))) {
+ if (!stream_map_.contains(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()))) {
return false;
}
const auto& state =
@@ -521,7 +518,7 @@ void SimpleSessionNotifier::OnControlFrameLost(const QuicFrame& frame) {
kInvalidControlFrameId) {
return;
}
- if (!QuicContainsKey(lost_control_frames_, id)) {
+ if (!lost_control_frames_.contains(id)) {
lost_control_frames_[id] = true;
}
}
@@ -584,8 +581,8 @@ bool SimpleSessionNotifier::RetransmitLostCryptoData() {
}
return true;
}
- if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId(
- connection_->transport_version()))) {
+ if (!stream_map_.contains(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()))) {
return true;
}
auto& state =
@@ -701,7 +698,7 @@ bool SimpleSessionNotifier::HasBufferedStreamData() const {
}
bool SimpleSessionNotifier::StreamIsWaitingForAcks(QuicStreamId id) const {
- if (!QuicContainsKey(stream_map_, id)) {
+ if (!stream_map_.contains(id)) {
return false;
}
const StreamState& state = stream_map_.find(id)->second;
@@ -710,7 +707,7 @@ bool SimpleSessionNotifier::StreamIsWaitingForAcks(QuicStreamId id) const {
}
bool SimpleSessionNotifier::StreamHasBufferedData(QuicStreamId id) const {
- if (!QuicContainsKey(stream_map_, id)) {
+ if (!stream_map_.contains(id)) {
return false;
}
const StreamState& state = stream_map_.find(id)->second;
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h
index bc76ddc877a..8bdadb2f45d 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h
@@ -10,6 +10,7 @@
#include "quic/core/session_notifier_interface.h"
#include "quic/platform/api/quic_test.h"
#include "common/quiche_circular_deque.h"
+#include "common/quiche_linked_hash_map.h"
namespace quic {
@@ -134,7 +135,7 @@ class SimpleSessionNotifier : public SessionNotifierInterface {
quiche::QuicheCircularDeque<QuicFrame> control_frames_;
- QuicLinkedHashMap<QuicControlFrameId, bool> lost_control_frames_;
+ quiche::QuicheLinkedHashMap<QuicControlFrameId, bool> lost_control_frames_;
// Id of latest saved control frame. 0 if no control frame has been saved.
QuicControlFrameId last_control_frame_id_;
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc
index c286bf6e853..c7e23cd0711 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc
@@ -26,7 +26,7 @@ Queue::Queue(Simulator* simulator, std::string name, QuicByteCount capacity)
new AggregationAlarmDelegate(this)));
}
-Queue::~Queue() {}
+Queue::~Queue() { aggregation_timeout_alarm_->PermanentCancel(); }
void Queue::set_tx_port(ConstrainedPortInterface* port) {
tx_port_ = port;
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/test_ticket_crypter.cc b/chromium/net/third_party/quiche/src/quic/test_tools/test_ticket_crypter.cc
index 4c4cfbbac2f..be9021a7baa 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/test_ticket_crypter.cc
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/test_ticket_crypter.cc
@@ -37,7 +37,8 @@ size_t TestTicketCrypter::MaxOverhead() {
return ticket_prefix_.size();
}
-std::vector<uint8_t> TestTicketCrypter::Encrypt(absl::string_view in) {
+std::vector<uint8_t> TestTicketCrypter::Encrypt(
+ absl::string_view in, absl::string_view /* encryption_key */) {
size_t prefix_len = ticket_prefix_.size();
std::vector<uint8_t> out(prefix_len + in.size());
memcpy(out.data(), ticket_prefix_.data(), prefix_len);
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/test_ticket_crypter.h b/chromium/net/third_party/quiche/src/quic/test_tools/test_ticket_crypter.h
index 63919c61bba..0300998fa37 100644
--- a/chromium/net/third_party/quiche/src/quic/test_tools/test_ticket_crypter.h
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/test_ticket_crypter.h
@@ -19,7 +19,8 @@ class TestTicketCrypter : public ProofSource::TicketCrypter {
// TicketCrypter interface
size_t MaxOverhead() override;
- std::vector<uint8_t> Encrypt(absl::string_view in) override;
+ std::vector<uint8_t> Encrypt(absl::string_view in,
+ absl::string_view encryption_key) override;
void Decrypt(absl::string_view in,
std::unique_ptr<ProofSource::DecryptCallback> callback) override;
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc
index 53c6800a2e5..6d10de282cd 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc
@@ -3,6 +3,8 @@
// found in the LICENSE file.
#include "quic/tools/quic_client_base.h"
+
+#include <algorithm>
#include <memory>
#include "quic/core/crypto/quic_random.h"
@@ -442,13 +444,20 @@ QuicConnectionId QuicClientBase::GetClientConnectionId() {
bool QuicClientBase::CanReconnectWithDifferentVersion(
ParsedQuicVersion* version) const {
if (session_ == nullptr || session_->connection() == nullptr ||
- session_->error() != QUIC_INVALID_VERSION ||
- session_->connection()->server_supported_versions().empty()) {
+ session_->error() != QUIC_INVALID_VERSION) {
return false;
}
+
+ const auto& server_supported_versions =
+ session_->connection()->server_supported_versions();
+ if (server_supported_versions.empty()) {
+ return false;
+ }
+
for (const auto& client_version : supported_versions_) {
- if (QuicContainsValue(session_->connection()->server_supported_versions(),
- client_version)) {
+ if (std::find(server_supported_versions.begin(),
+ server_supported_versions.end(),
+ client_version) != server_supported_versions.end()) {
*version = client_version;
return true;
}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h
index 82ee39256cf..51e3433d90a 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h
@@ -145,6 +145,11 @@ class QuicClientBase {
crypto_config_.set_user_agent_id(user_agent_id);
}
+ void SetTlsSignatureAlgorithms(std::string signature_algorithms) {
+ crypto_config_.set_tls_signature_algorithms(
+ std::move(signature_algorithms));
+ }
+
const ParsedQuicVersionVector& supported_versions() const {
return supported_versions_;
}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h b/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h
index df0a3b4a4f5..bb8a898384c 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h
@@ -15,9 +15,9 @@
#include "quic/core/http/quic_client_push_promise_index.h"
#include "quic/core/quic_config.h"
#include "quic/core/quic_packet_reader.h"
-#include "quic/platform/api/quic_containers.h"
#include "quic/platform/api/quic_epoll.h"
#include "quic/tools/quic_client_base.h"
+#include "common/quiche_linked_hash_map.h"
namespace quic {
@@ -73,7 +73,8 @@ class QuicClientEpollNetworkHelper : public QuicClientBase::NetworkHelper,
QuicEpollServer* epoll_server() { return epoll_server_; }
- const QuicLinkedHashMap<int, QuicSocketAddress>& fd_address_map() const {
+ const quiche::QuicheLinkedHashMap<int, QuicSocketAddress>& fd_address_map()
+ const {
return fd_address_map_;
}
@@ -110,7 +111,7 @@ class QuicClientEpollNetworkHelper : public QuicClientBase::NetworkHelper,
// Map mapping created UDP sockets to their addresses. By using linked hash
// map, the order of socket creation can be recorded.
- QuicLinkedHashMap<int, QuicSocketAddress> fd_address_map_;
+ quiche::QuicheLinkedHashMap<int, QuicSocketAddress> fd_address_map_;
// If overflow_supported_ is true, this will be the number of packets dropped
// during the lifetime of the server.
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc
index de2064831f4..58aa7a94ce8 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc
@@ -12,10 +12,9 @@
#include "absl/strings/string_view.h"
#include "quic/core/http/spdy_utils.h"
#include "quic/platform/api/quic_bug_tracker.h"
-#include "quic/platform/api/quic_file_utils.h"
#include "quic/platform/api/quic_logging.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/tools/web_transport_test_visitors.h"
+#include "common/platform/api/quiche_file_utils.h"
#include "common/quiche_text_utils.h"
using spdy::Http2HeaderBlock;
@@ -29,7 +28,14 @@ QuicMemoryCacheBackend::ResourceFile::ResourceFile(const std::string& file_name)
QuicMemoryCacheBackend::ResourceFile::~ResourceFile() = default;
void QuicMemoryCacheBackend::ResourceFile::Read() {
- ReadFileContents(file_name_, &file_contents_);
+ absl::optional<std::string> maybe_file_contents =
+ quiche::ReadFileContents(file_name_);
+ if (!maybe_file_contents) {
+ QUIC_LOG(DFATAL) << "Failed to read file for the memory cache backend: "
+ << file_name_;
+ return;
+ }
+ file_contents_ = *maybe_file_contents;
// First read the headers.
size_t start = 0;
@@ -140,8 +146,7 @@ void QuicMemoryCacheBackend::ResourceFile::HandleXOriginalUrl() {
}
const QuicBackendResponse* QuicMemoryCacheBackend::GetResponse(
- absl::string_view host,
- absl::string_view path) const {
+ absl::string_view host, absl::string_view path) const {
QuicWriterMutexLock lock(&response_mutex_);
auto it = responses_.find(GetKey(host, path));
@@ -179,11 +184,8 @@ void QuicMemoryCacheBackend::AddSimpleResponse(absl::string_view host,
}
void QuicMemoryCacheBackend::AddSimpleResponseWithServerPushResources(
- absl::string_view host,
- absl::string_view path,
- int response_code,
- absl::string_view body,
- std::list<ServerPushInfo> push_resources) {
+ absl::string_view host, absl::string_view path, int response_code,
+ absl::string_view body, std::list<ServerPushInfo> push_resources) {
AddSimpleResponse(host, path, response_code, body);
MaybeAddServerPushResources(host, path, push_resources);
}
@@ -214,10 +216,8 @@ void QuicMemoryCacheBackend::AddResponse(absl::string_view host,
}
void QuicMemoryCacheBackend::AddResponseWithEarlyHints(
- absl::string_view host,
- absl::string_view path,
- spdy::Http2HeaderBlock response_headers,
- absl::string_view response_body,
+ absl::string_view host, absl::string_view path,
+ spdy::Http2HeaderBlock response_headers, absl::string_view response_body,
const std::vector<spdy::Http2HeaderBlock>& early_hints) {
AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE,
std::move(response_headers), response_body,
@@ -225,18 +225,15 @@ void QuicMemoryCacheBackend::AddResponseWithEarlyHints(
}
void QuicMemoryCacheBackend::AddSpecialResponse(
- absl::string_view host,
- absl::string_view path,
+ absl::string_view host, absl::string_view path,
SpecialResponseType response_type) {
AddResponseImpl(host, path, response_type, Http2HeaderBlock(), "",
Http2HeaderBlock(), std::vector<spdy::Http2HeaderBlock>());
}
void QuicMemoryCacheBackend::AddSpecialResponse(
- absl::string_view host,
- absl::string_view path,
- spdy::Http2HeaderBlock response_headers,
- absl::string_view response_body,
+ absl::string_view host, absl::string_view path,
+ spdy::Http2HeaderBlock response_headers, absl::string_view response_body,
SpecialResponseType response_type) {
AddResponseImpl(host, path, response_type, std::move(response_headers),
response_body, Http2HeaderBlock(),
@@ -254,7 +251,12 @@ bool QuicMemoryCacheBackend::InitializeBackend(
QUIC_LOG(INFO)
<< "Attempting to initialize QuicMemoryCacheBackend from directory: "
<< cache_directory;
- std::vector<std::string> files = ReadFileContents(cache_directory);
+ std::vector<std::string> files;
+ if (!quiche::EnumerateDirectoryRecursively(cache_directory, files)) {
+ QUIC_BUG(QuicMemoryCacheBackend unreadable directory)
+ << "Can't read QuicMemoryCacheBackend directory: " << cache_directory;
+ return false;
+ }
std::list<std::unique_ptr<ResourceFile>> resource_files;
for (const auto& filename : files) {
std::unique_ptr<ResourceFile> resource_file(new ResourceFile(filename));
@@ -402,19 +404,16 @@ QuicMemoryCacheBackend::~QuicMemoryCacheBackend() {
}
void QuicMemoryCacheBackend::AddResponseImpl(
- absl::string_view host,
- absl::string_view path,
- SpecialResponseType response_type,
- Http2HeaderBlock response_headers,
- absl::string_view response_body,
- Http2HeaderBlock response_trailers,
+ absl::string_view host, absl::string_view path,
+ SpecialResponseType response_type, Http2HeaderBlock response_headers,
+ absl::string_view response_body, Http2HeaderBlock response_trailers,
const std::vector<spdy::Http2HeaderBlock>& early_hints) {
QuicWriterMutexLock lock(&response_mutex_);
QUICHE_DCHECK(!host.empty())
<< "Host must be populated, e.g. \"www.google.com\"";
std::string key = GetKey(host, path);
- if (QuicContainsKey(responses_, key)) {
+ if (responses_.contains(key)) {
QUIC_BUG(quic_bug_10932_3)
<< "Response for '" << key << "' already exists!";
return;
@@ -441,8 +440,7 @@ std::string QuicMemoryCacheBackend::GetKey(absl::string_view host,
}
void QuicMemoryCacheBackend::MaybeAddServerPushResources(
- absl::string_view request_host,
- absl::string_view request_path,
+ absl::string_view request_host, absl::string_view request_path,
std::list<ServerPushInfo> push_resources) {
std::string request_url = GetKey(request_host, request_path);
@@ -468,7 +466,7 @@ void QuicMemoryCacheBackend::MaybeAddServerPushResources(
bool found_existing_response = false;
{
QuicWriterMutexLock lock(&response_mutex_);
- found_existing_response = QuicContainsKey(responses_, GetKey(host, path));
+ found_existing_response = responses_.contains(GetKey(host, path));
}
if (!found_existing_response) {
// Add a server push response to responses map, if it is not in the map.
@@ -481,8 +479,7 @@ void QuicMemoryCacheBackend::MaybeAddServerPushResources(
}
bool QuicMemoryCacheBackend::PushResourceExistsInCache(
- std::string original_request_url,
- ServerPushInfo resource) {
+ std::string original_request_url, ServerPushInfo resource) {
QuicWriterMutexLock lock(&response_mutex_);
auto resource_range =
server_push_resources_.equal_range(original_request_url);
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc
index d0383e19b6e..71f5dc5bbb4 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc
@@ -4,12 +4,13 @@
#include "quic/tools/quic_memory_cache_backend.h"
+#include <vector>
+
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
-#include "quic/platform/api/quic_file_utils.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/platform/api/quic_test.h"
#include "quic/tools/quic_backend_response.h"
+#include "common/platform/api/quiche_file_utils.h"
namespace quic {
namespace test {
@@ -21,8 +22,7 @@ using ServerPushInfo = QuicBackendResponse::ServerPushInfo;
class QuicMemoryCacheBackendTest : public QuicTest {
protected:
- void CreateRequest(std::string host,
- std::string path,
+ void CreateRequest(std::string host, std::string path,
spdy::Http2HeaderBlock* headers) {
(*headers)[":method"] = "GET";
(*headers)[":path"] = path;
@@ -49,7 +49,7 @@ TEST_F(QuicMemoryCacheBackendTest, AddSimpleResponseGetResponse) {
CreateRequest("www.google.com", "/", &request_headers);
const Response* response = cache_.GetResponse("www.google.com", "/");
ASSERT_TRUE(response);
- ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ ASSERT_TRUE(response->headers().contains(":status"));
EXPECT_EQ("200", response->headers().find(":status")->second);
EXPECT_EQ(response_body.size(), response->body().length());
}
@@ -82,10 +82,10 @@ TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDir) {
const Response* response =
cache_.GetResponse("test.example.com", "/index.html");
ASSERT_TRUE(response);
- ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ ASSERT_TRUE(response->headers().contains(":status"));
EXPECT_EQ("200", response->headers().find(":status")->second);
// Connection headers are not valid in HTTP/2.
- EXPECT_FALSE(QuicContainsKey(response->headers(), "connection"));
+ EXPECT_FALSE(response->headers().contains("connection"));
EXPECT_LT(0U, response->body().length());
}
@@ -108,10 +108,10 @@ TEST_F(QuicMemoryCacheBackendTest, UsesOriginalUrl) {
const Response* response =
cache_.GetResponse("test.example.com", "/site_map.html");
ASSERT_TRUE(response);
- ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ ASSERT_TRUE(response->headers().contains(":status"));
EXPECT_EQ("200", response->headers().find(":status")->second);
// Connection headers are not valid in HTTP/2.
- EXPECT_FALSE(QuicContainsKey(response->headers(), "connection"));
+ EXPECT_FALSE(response->headers().contains("connection"));
EXPECT_LT(0U, response->body().length());
}
@@ -121,7 +121,9 @@ TEST_F(QuicMemoryCacheBackendTest, UsesOriginalUrlOnly) {
// X-Original-Url header's value will be used.
std::string dir;
std::string path = "map.html";
- for (const std::string& file : ReadFileContents(CacheDirectory())) {
+ std::vector<std::string> files;
+ ASSERT_TRUE(quiche::EnumerateDirectoryRecursively(CacheDirectory(), files));
+ for (const std::string& file : files) {
if (absl::EndsWithIgnoreCase(file, "map.html")) {
dir = file;
dir.erase(dir.length() - path.length() - 1);
@@ -134,10 +136,10 @@ TEST_F(QuicMemoryCacheBackendTest, UsesOriginalUrlOnly) {
const Response* response =
cache_.GetResponse("test.example.com", "/site_map.html");
ASSERT_TRUE(response);
- ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ ASSERT_TRUE(response->headers().contains(":status"));
EXPECT_EQ("200", response->headers().find(":status")->second);
// Connection headers are not valid in HTTP/2.
- EXPECT_FALSE(QuicContainsKey(response->headers(), "connection"));
+ EXPECT_FALSE(response->headers().contains("connection"));
EXPECT_LT(0U, response->body().length());
}
@@ -157,20 +159,20 @@ TEST_F(QuicMemoryCacheBackendTest, DefaultResponse) {
// Now we should get the default response for the original request.
response = cache_.GetResponse("www.google.com", "/");
ASSERT_TRUE(response);
- ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ ASSERT_TRUE(response->headers().contains(":status"));
EXPECT_EQ("200", response->headers().find(":status")->second);
// Now add a set response for / and make sure it is returned
cache_.AddSimpleResponse("www.google.com", "/", 302, "");
response = cache_.GetResponse("www.google.com", "/");
ASSERT_TRUE(response);
- ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ ASSERT_TRUE(response->headers().contains(":status"));
EXPECT_EQ("302", response->headers().find(":status")->second);
// We should get the default response for other requests.
response = cache_.GetResponse("www.google.com", "/asd");
ASSERT_TRUE(response);
- ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ ASSERT_TRUE(response->headers().contains(":status"));
EXPECT_EQ("200", response->headers().find(":status")->second);
}
@@ -243,7 +245,7 @@ TEST_F(QuicMemoryCacheBackendTest, GetServerPushResourcesAndPushResponses) {
std::string path = url.path();
const Response* response = cache_.GetResponse(host, path);
ASSERT_TRUE(response);
- ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ ASSERT_TRUE(response->headers().contains(":status"));
EXPECT_EQ(push_response_status[i++],
response->headers().find(":status")->second);
EXPECT_EQ(push_resource.body, response->body());
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc
index d1efcba4fe0..07c46289da8 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc
@@ -54,4 +54,8 @@ bool QuicSimpleClientSession::ShouldNegotiateWebTransport() {
return enable_web_transport_;
}
+bool QuicSimpleClientSession::ShouldNegotiateHttp3Datagram() {
+ return enable_web_transport_;
+}
+
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h
index aa86d9a83c9..124e56f8bf1 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h
@@ -30,6 +30,7 @@ class QuicSimpleClientSession : public QuicSpdyClientSession {
std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override;
bool ShouldNegotiateWebTransport() override;
+ bool ShouldNegotiateHttp3Datagram() override;
private:
const bool drop_response_body_;
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc
index ac95e46fb97..5cd365298f3 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc
@@ -18,7 +18,6 @@
#include "quic/platform/api/quic_bug_tracker.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
-#include "quic/platform/api/quic_map_util.h"
#include "quic/tools/quic_simple_server_session.h"
#include "spdy/core/spdy_protocol.h"
@@ -155,13 +154,13 @@ void QuicSimpleServerStream::SendResponse() {
return;
}
- if (!QuicContainsKey(request_headers_, ":authority")) {
+ if (!request_headers_.contains(":authority")) {
QUIC_DVLOG(1) << "Request headers do not contain :authority.";
SendErrorResponse();
return;
}
- if (!QuicContainsKey(request_headers_, ":path")) {
+ if (!request_headers_.contains(":path")) {
// CONNECT and other CONNECT-like methods (such as CONNECT-UDP) do not all
// require :path to be present.
auto it = request_headers_.find(":method");
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc
index 25423114fab..5c9cd2274d9 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc
@@ -16,6 +16,7 @@
#include "quic/core/http/http_encoder.h"
#include "quic/core/http/spdy_utils.h"
#include "quic/core/quic_error_codes.h"
+#include "quic/core/quic_simple_buffer_allocator.h"
#include "quic/core/quic_types.h"
#include "quic/core/quic_utils.h"
#include "quic/platform/api/quic_expect_bug.h"
@@ -156,14 +157,10 @@ class MockQuicSimpleServerSession : public QuicSimpleServerSession {
CreateIncomingStream,
(QuicStreamId id),
(override));
- MOCK_METHOD(QuicConsumedData,
- WritevData,
- (QuicStreamId id,
- size_t write_length,
- QuicStreamOffset offset,
- StreamSendingState state,
- TransmissionType type,
- absl::optional<EncryptionLevel> level),
+ MOCK_METHOD(QuicConsumedData, WritevData,
+ (QuicStreamId id, size_t write_length, QuicStreamOffset offset,
+ StreamSendingState state, TransmissionType type,
+ EncryptionLevel level),
(override));
MOCK_METHOD(void,
OnStreamHeaderList,
@@ -311,11 +308,10 @@ TEST_P(QuicSimpleServerStreamTest, TestFraming) {
.WillRepeatedly(
Invoke(&session_, &MockQuicSimpleServerSession::ConsumeData));
stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
- std::string data = UsesHttp3() ? header + body_ : body_;
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
+ std::string data =
+ UsesHttp3() ? absl::StrCat(header.AsStringView(), body_) : body_;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
EXPECT_EQ("11", StreamHeadersValue("content-length"));
@@ -330,11 +326,10 @@ TEST_P(QuicSimpleServerStreamTest, TestFramingOnePacket) {
Invoke(&session_, &MockQuicSimpleServerSession::ConsumeData));
stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
- std::string data = UsesHttp3() ? header + body_ : body_;
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
+ std::string data =
+ UsesHttp3() ? absl::StrCat(header.AsStringView(), body_) : body_;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
EXPECT_EQ("11", StreamHeadersValue("content-length"));
@@ -377,20 +372,20 @@ TEST_P(QuicSimpleServerStreamTest, TestFramingExtraData) {
EXPECT_CALL(session_, WritevData(_, kErrorLength, _, FIN, _, _));
stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
- std::string data = UsesHttp3() ? header + body_ : body_;
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
+ std::string data =
+ UsesHttp3() ? absl::StrCat(header.AsStringView(), body_) : body_;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
// Content length is still 11. This will register as an error and we won't
// accept the bytes.
- header_length =
- HttpEncoder::SerializeDataFrameHeader(large_body.length(), &buffer);
- header = std::string(buffer.get(), header_length);
- std::string data2 = UsesHttp3() ? header + large_body : large_body;
+ header = HttpEncoder::SerializeDataFrameHeader(large_body.length(),
+ SimpleBufferAllocator::Get());
+ std::string data2 = UsesHttp3()
+ ? absl::StrCat(header.AsStringView(), large_body)
+ : large_body;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/true, data.size(), data2));
EXPECT_EQ("11", StreamHeadersValue("content-length"));
@@ -409,9 +404,8 @@ TEST_P(QuicSimpleServerStreamTest, SendResponseWithIllegalResponseStatus) {
response_headers_[":status"] = "200 OK";
response_headers_["content-length"] = "5";
std::string body = "Yummm";
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body.length(), &buffer);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), SimpleBufferAllocator::Get());
memory_cache_backend_.AddResponse("www.google.com", "/bar",
std::move(response_headers_), body);
@@ -421,7 +415,7 @@ TEST_P(QuicSimpleServerStreamTest, SendResponseWithIllegalResponseStatus) {
InSequence s;
EXPECT_CALL(*stream_, WriteHeadersMock(false));
if (UsesHttp3()) {
- EXPECT_CALL(session_, WritevData(_, header_length, _, NO_FIN, _, _));
+ EXPECT_CALL(session_, WritevData(_, header.size(), _, NO_FIN, _, _));
}
EXPECT_CALL(session_, WritevData(_, kErrorLength, _, FIN, _, _));
@@ -442,9 +436,8 @@ TEST_P(QuicSimpleServerStreamTest, SendResponseWithIllegalResponseStatus2) {
response_headers_["content-length"] = "5";
std::string body = "Yummm";
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body.length(), &buffer);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), SimpleBufferAllocator::Get());
memory_cache_backend_.AddResponse("www.google.com", "/bar",
std::move(response_headers_), body);
@@ -454,7 +447,7 @@ TEST_P(QuicSimpleServerStreamTest, SendResponseWithIllegalResponseStatus2) {
InSequence s;
EXPECT_CALL(*stream_, WriteHeadersMock(false));
if (UsesHttp3()) {
- EXPECT_CALL(session_, WritevData(_, header_length, _, NO_FIN, _, _));
+ EXPECT_CALL(session_, WritevData(_, header.size(), _, NO_FIN, _, _));
}
EXPECT_CALL(session_, WritevData(_, kErrorLength, _, FIN, _, _));
@@ -507,9 +500,8 @@ TEST_P(QuicSimpleServerStreamTest, SendResponseWithValidHeaders) {
response_headers_["content-length"] = "5";
std::string body = "Yummm";
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body.length(), &buffer);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), SimpleBufferAllocator::Get());
memory_cache_backend_.AddResponse("www.google.com", "/bar",
std::move(response_headers_), body);
@@ -518,7 +510,7 @@ TEST_P(QuicSimpleServerStreamTest, SendResponseWithValidHeaders) {
InSequence s;
EXPECT_CALL(*stream_, WriteHeadersMock(false));
if (UsesHttp3()) {
- EXPECT_CALL(session_, WritevData(_, header_length, _, NO_FIN, _, _));
+ EXPECT_CALL(session_, WritevData(_, header.size(), _, NO_FIN, _, _));
}
EXPECT_CALL(session_, WritevData(_, body.length(), _, FIN, _, _));
@@ -538,9 +530,8 @@ TEST_P(QuicSimpleServerStreamTest, SendResponseWithEarlyHints) {
(*request_headers)[":authority"] = host;
(*request_headers)[":method"] = "GET";
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body.length(), &buffer);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), SimpleBufferAllocator::Get());
std::vector<spdy::Http2HeaderBlock> early_hints;
// Add two Early Hints.
const size_t kNumEarlyHintsResponses = 2;
@@ -562,7 +553,7 @@ TEST_P(QuicSimpleServerStreamTest, SendResponseWithEarlyHints) {
}
EXPECT_CALL(*stream_, WriteHeadersMock(false));
if (UsesHttp3()) {
- EXPECT_CALL(session_, WritevData(_, header_length, _, NO_FIN, _, _));
+ EXPECT_CALL(session_, WritevData(_, header.size(), _, NO_FIN, _, _));
}
EXPECT_CALL(session_, WritevData(_, body.length(), _, FIN, _, _));
@@ -608,9 +599,8 @@ TEST_P(QuicSimpleServerStreamTest, PushResponseOnServerInitiatedStream) {
response_headers_[":status"] = "200";
response_headers_["content-length"] = "5";
const std::string kBody = "Hello";
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(kBody.length(), &buffer);
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
memory_cache_backend_.AddResponse(kHost, kPath, std::move(response_headers_),
kBody);
@@ -620,7 +610,7 @@ TEST_P(QuicSimpleServerStreamTest, PushResponseOnServerInitiatedStream) {
EXPECT_CALL(*server_initiated_stream, WriteHeadersMock(false));
if (UsesHttp3()) {
- EXPECT_CALL(session_, WritevData(kServerInitiatedStreamId, header_length, _,
+ EXPECT_CALL(session_, WritevData(kServerInitiatedStreamId, header.size(), _,
NO_FIN, _, _));
}
EXPECT_CALL(session_,
@@ -728,27 +718,27 @@ TEST_P(QuicSimpleServerStreamTest,
TEST_P(QuicSimpleServerStreamTest, InvalidHeadersWithFin) {
char arr[] = {
- 0x3a, 0x68, 0x6f, 0x73, // :hos
- 0x74, 0x00, 0x00, 0x00, // t...
- 0x00, 0x00, 0x00, 0x00, // ....
- 0x07, 0x3a, 0x6d, 0x65, // .:me
- 0x74, 0x68, 0x6f, 0x64, // thod
- 0x00, 0x00, 0x00, 0x03, // ....
- 0x47, 0x45, 0x54, 0x00, // GET.
- 0x00, 0x00, 0x05, 0x3a, // ...:
- 0x70, 0x61, 0x74, 0x68, // path
- 0x00, 0x00, 0x00, 0x04, // ....
- 0x2f, 0x66, 0x6f, 0x6f, // /foo
- 0x00, 0x00, 0x00, 0x07, // ....
- 0x3a, 0x73, 0x63, 0x68, // :sch
- 0x65, 0x6d, 0x65, 0x00, // eme.
- 0x00, 0x00, 0x00, 0x00, // ....
- 0x00, 0x00, 0x08, 0x3a, // ...:
- 0x76, 0x65, 0x72, 0x73, // vers
- 0x96, 0x6f, 0x6e, 0x00, // <i(69)>on.
- 0x00, 0x00, 0x08, 0x48, // ...H
- 0x54, 0x54, 0x50, 0x2f, // TTP/
- 0x31, 0x2e, 0x31, // 1.1
+ 0x3a, 0x68, 0x6f, 0x73, // :hos
+ 0x74, 0x00, 0x00, 0x00, // t...
+ 0x00, 0x00, 0x00, 0x00, // ....
+ 0x07, 0x3a, 0x6d, 0x65, // .:me
+ 0x74, 0x68, 0x6f, 0x64, // thod
+ 0x00, 0x00, 0x00, 0x03, // ....
+ 0x47, 0x45, 0x54, 0x00, // GET.
+ 0x00, 0x00, 0x05, 0x3a, // ...:
+ 0x70, 0x61, 0x74, 0x68, // path
+ 0x00, 0x00, 0x00, 0x04, // ....
+ 0x2f, 0x66, 0x6f, 0x6f, // /foo
+ 0x00, 0x00, 0x00, 0x07, // ....
+ 0x3a, 0x73, 0x63, 0x68, // :sch
+ 0x65, 0x6d, 0x65, 0x00, // eme.
+ 0x00, 0x00, 0x00, 0x00, // ....
+ 0x00, 0x00, 0x08, 0x3a, // ...:
+ 0x76, 0x65, 0x72, 0x73, // vers
+ '\x96', 0x6f, 0x6e, 0x00, // <i(69)>on.
+ 0x00, 0x00, 0x08, 0x48, // ...H
+ 0x54, 0x54, 0x50, 0x2f, // TTP/
+ 0x31, 0x2e, 0x31, // 1.1
};
absl::string_view data(arr, ABSL_ARRAYSIZE(arr));
QuicStreamFrame frame(stream_->id(), true, 0, data);
@@ -767,11 +757,10 @@ TEST_P(QuicSimpleServerStreamTest, ConnectSendsResponseBeforeFinReceived) {
header_list.OnHeaderBlockEnd(128, 128);
EXPECT_CALL(*stream_, WriteHeadersMock(/*fin=*/false));
stream_->OnStreamHeaderList(/*fin=*/false, kFakeFrameLen, header_list);
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
- std::string data = UsesHttp3() ? header + body_ : body_;
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
+ std::string data =
+ UsesHttp3() ? absl::StrCat(header.AsStringView(), body_) : body_;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
EXPECT_EQ("CONNECT-SILLY", StreamHeadersValue(":method"));
@@ -793,11 +782,10 @@ TEST_P(QuicSimpleServerStreamTest, ConnectWithInvalidHeader) {
header_list.OnHeaderBlockEnd(128, 128);
EXPECT_CALL(*stream_, WriteHeadersMock(/*fin=*/false));
stream_->OnStreamHeaderList(/*fin=*/false, kFakeFrameLen, header_list);
- std::unique_ptr<char[]> buffer;
- QuicByteCount header_length =
- HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
- std::string header = std::string(buffer.get(), header_length);
- std::string data = UsesHttp3() ? header + body_ : body_;
+ QuicBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body_.length(), SimpleBufferAllocator::Get());
+ std::string data =
+ UsesHttp3() ? absl::StrCat(header.AsStringView(), body_) : body_;
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
EXPECT_EQ("CONNECT-SILLY", StreamHeadersValue(":method"));
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc
index 4f51eb47eba..464fa70fda7 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc
@@ -67,6 +67,10 @@ const QuicSpdyClientSession* QuicSpdyClientBase::client_session() const {
}
void QuicSpdyClientBase::InitializeSession() {
+ if (max_inbound_header_list_size_ > 0) {
+ client_session()->set_max_inbound_header_list_size(
+ max_inbound_header_list_size_);
+ }
client_session()->Initialize();
client_session()->CryptoConnect();
}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h
index 2ba55f11693..c49d0ed84a5 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h
@@ -149,6 +149,10 @@ class QuicSpdyClientBase : public QuicClientBase,
bool EarlyDataAccepted() override;
bool ReceivedInchoateReject() override;
+ void set_max_inbound_header_list_size(size_t size) {
+ max_inbound_header_list_size_ = size;
+ }
+
protected:
int GetNumSentClientHellosFromSession() override;
int GetNumReceivedServerConfigUpdatesFromSession() override;
@@ -223,6 +227,9 @@ class QuicSpdyClientBase : public QuicClientBase,
bool drop_response_body_ = false;
bool enable_web_transport_ = false;
+ // If not zero, used to set client's max inbound header size before session
+ // initialize.
+ size_t max_inbound_header_list_size_ = 0;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc
index 8d227913ce5..0db266b9e11 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc
@@ -211,6 +211,13 @@ DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t,
-1,
"Length of the client connection ID used.");
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, max_time_before_crypto_handshake_ms,
+ 10000,
+ "Max time to wait before handshake completes.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, max_inbound_header_list_size, 128 * 1024,
+ "Max inbound header list size. 0 means default.");
+
namespace quic {
QuicToyClient::QuicToyClient(ClientFactory* client_factory)
@@ -293,6 +300,8 @@ int QuicToyClient::SendRequestsAndPrintResponses(
config.custom_transport_parameters_to_send()[kCustomParameter] =
custom_value;
}
+ config.set_max_time_before_crypto_handshake(QuicTime::Delta::FromMilliseconds(
+ GetQuicFlag(FLAGS_max_time_before_crypto_handshake_ms)));
int address_family_for_lookup = AF_UNSPEC;
if (GetQuicFlag(FLAGS_ip_version_for_host_lookup) == "4") {
@@ -325,6 +334,11 @@ int QuicToyClient::SendRequestsAndPrintResponses(
if (client_connection_id_length >= 0) {
client->set_client_connection_id_length(client_connection_id_length);
}
+ const size_t max_inbound_header_list_size =
+ GetQuicFlag(FLAGS_max_inbound_header_list_size);
+ if (max_inbound_header_list_size > 0) {
+ client->set_max_inbound_header_list_size(max_inbound_header_list_size);
+ }
if (!client->Initialize()) {
std::cerr << "Failed to initialize client." << std::endl;
return 1;
diff --git a/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter.cc b/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter.cc
index 0130352ecfd..149adda20ff 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter.cc
+++ b/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter.cc
@@ -38,7 +38,12 @@ size_t SimpleTicketCrypter::MaxOverhead() {
return kEpochSize + kIVSize + kAuthTagSize;
}
-std::vector<uint8_t> SimpleTicketCrypter::Encrypt(absl::string_view in) {
+std::vector<uint8_t> SimpleTicketCrypter::Encrypt(
+ absl::string_view in, absl::string_view encryption_key) {
+ // This class is only used in Chromium, in which the |encryption_key| argument
+ // will never be populated and an internally-cached key should be used for
+ // encrypting tickets.
+ QUICHE_DCHECK(encryption_key.empty());
MaybeRotateKeys();
std::vector<uint8_t> out(in.size() + MaxOverhead());
out[0] = key_epoch_;
diff --git a/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter.h b/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter.h
index d547a25bf9e..052bc5872a3 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter.h
+++ b/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter.h
@@ -24,7 +24,8 @@ class QUIC_NO_EXPORT SimpleTicketCrypter
~SimpleTicketCrypter() override;
size_t MaxOverhead() override;
- std::vector<uint8_t> Encrypt(absl::string_view in) override;
+ std::vector<uint8_t> Encrypt(absl::string_view in,
+ absl::string_view encryption_key) override;
void Decrypt(
absl::string_view in,
std::unique_ptr<quic::ProofSource::DecryptCallback> callback) override;
diff --git a/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter_test.cc b/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter_test.cc
index ad041c10de8..be71018de23 100644
--- a/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/tools/simple_ticket_crypter_test.cc
@@ -42,7 +42,7 @@ class SimpleTicketCrypterTest : public QuicTest {
TEST_F(SimpleTicketCrypterTest, EncryptDecrypt) {
std::vector<uint8_t> plaintext = {1, 2, 3, 4, 5};
std::vector<uint8_t> ciphertext =
- ticket_crypter_.Encrypt(StringPiece(plaintext));
+ ticket_crypter_.Encrypt(StringPiece(plaintext), {});
EXPECT_NE(plaintext, ciphertext);
std::vector<uint8_t> out_plaintext;
@@ -54,16 +54,16 @@ TEST_F(SimpleTicketCrypterTest, EncryptDecrypt) {
TEST_F(SimpleTicketCrypterTest, CiphertextsDiffer) {
std::vector<uint8_t> plaintext = {1, 2, 3, 4, 5};
std::vector<uint8_t> ciphertext1 =
- ticket_crypter_.Encrypt(StringPiece(plaintext));
+ ticket_crypter_.Encrypt(StringPiece(plaintext), {});
std::vector<uint8_t> ciphertext2 =
- ticket_crypter_.Encrypt(StringPiece(plaintext));
+ ticket_crypter_.Encrypt(StringPiece(plaintext), {});
EXPECT_NE(ciphertext1, ciphertext2);
}
TEST_F(SimpleTicketCrypterTest, DecryptionFailureWithModifiedCiphertext) {
std::vector<uint8_t> plaintext = {1, 2, 3, 4, 5};
std::vector<uint8_t> ciphertext =
- ticket_crypter_.Encrypt(StringPiece(plaintext));
+ ticket_crypter_.Encrypt(StringPiece(plaintext), {});
EXPECT_NE(plaintext, ciphertext);
// Check that a bit flip in any byte will cause a decryption failure.
@@ -88,7 +88,7 @@ TEST_F(SimpleTicketCrypterTest, DecryptionFailureWithEmptyCiphertext) {
TEST_F(SimpleTicketCrypterTest, KeyRotation) {
std::vector<uint8_t> plaintext = {1, 2, 3};
std::vector<uint8_t> ciphertext =
- ticket_crypter_.Encrypt(StringPiece(plaintext));
+ ticket_crypter_.Encrypt(StringPiece(plaintext), {});
EXPECT_FALSE(ciphertext.empty());
// Advance the clock 8 days, so the key used for |ciphertext| is now the
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc
index 3fd6ebcf87d..03db87d2e51 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc
@@ -7,7 +7,6 @@
#include "http2/decoder/decode_buffer.h"
#include "http2/decoder/decode_status.h"
#include "common/platform/api/quiche_logging.h"
-#include "spdy/platform/api/spdy_estimate_memory_usage.h"
using ::http2::DecodeBuffer;
@@ -118,10 +117,6 @@ void HpackDecoderAdapter::set_max_header_block_bytes(
max_header_block_bytes_ = max_header_block_bytes;
}
-size_t HpackDecoderAdapter::EstimateMemoryUsage() const {
- return SpdyEstimateMemoryUsage(hpack_decoder_);
-}
-
HpackDecoderAdapter::ListenerAdapter::ListenerAdapter() : handler_(nullptr) {}
HpackDecoderAdapter::ListenerAdapter::~ListenerAdapter() = default;
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h
index 98d10fe2c9a..43a762bb757 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h
@@ -67,6 +67,12 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderAdapter {
// a SpdyHeadersHandlerInterface.
const SpdyHeaderBlock& decoded_block() const;
+ // Returns the current dynamic table size, including the 32 bytes per entry
+ // overhead mentioned in RFC 7541 section 4.1.
+ size_t GetDynamicTableSize() const {
+ return hpack_decoder_.GetDynamicTableSize();
+ }
+
// Set how much encoded data this decoder is willing to buffer.
// TODO(jamessynge): Resolve definition of this value, as it is currently
// too tied to a single implementation. We probably want to limit one or more
@@ -79,8 +85,6 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderAdapter {
// accepted.
void set_max_header_block_bytes(size_t max_header_block_bytes);
- size_t EstimateMemoryUsage() const;
-
// Error code if an error has occurred, Error::kOk otherwise.
http2::HpackDecodingError error() const { return error_; }
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc
index d223ccf9f5a..bab6c481e78 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc
@@ -21,12 +21,12 @@
#include "http2/test_tools/http2_random.h"
#include "common/platform/api/quiche_logging.h"
#include "common/platform/api/quiche_test.h"
+#include "common/quiche_text_utils.h"
#include "spdy/core/hpack/hpack_constants.h"
#include "spdy/core/hpack/hpack_encoder.h"
#include "spdy/core/hpack/hpack_output_stream.h"
#include "spdy/core/recording_headers_handler.h"
#include "spdy/core/spdy_test_utils.h"
-#include "spdy/platform/api/spdy_string_utils.h"
using ::http2::HpackEntryType;
using ::http2::HpackStringPair;
@@ -135,7 +135,8 @@ class HpackDecoderAdapterTest
}
bool HandleControlFrameHeadersData(absl::string_view str) {
- QUICHE_VLOG(3) << "HandleControlFrameHeadersData:\n" << SpdyHexDump(str);
+ QUICHE_VLOG(3) << "HandleControlFrameHeadersData:\n"
+ << quiche::QuicheTextUtils::HexDump(str);
bytes_passed_in_ += str.size();
return decoder_.HandleControlFrameHeadersData(str.data(), str.size());
}
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc
index 27f0444f274..ccf17da20b2 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc
@@ -14,7 +14,6 @@
#include "spdy/core/hpack/hpack_constants.h"
#include "spdy/core/hpack/hpack_header_table.h"
#include "spdy/core/hpack/hpack_output_stream.h"
-#include "spdy/platform/api/spdy_estimate_memory_usage.h"
namespace spdy {
@@ -124,11 +123,6 @@ void HpackEncoder::ApplyHeaderTableSizeSetting(size_t size_setting) {
should_emit_table_size_ = true;
}
-size_t HpackEncoder::EstimateMemoryUsage() const {
- return SpdyEstimateMemoryUsage(header_table_) +
- SpdyEstimateMemoryUsage(output_stream_);
-}
-
void HpackEncoder::EncodeRepresentations(RepresentationIterator* iter,
std::string* output) {
MaybeEmitTableSize();
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h
index d85b98ede0b..5e2ea03980c 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h
@@ -95,8 +95,9 @@ class QUICHE_EXPORT_PRIVATE HpackEncoder {
void DisableCompression() { enable_compression_ = false; }
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
+ // Returns the current dynamic table size, including the 32 bytes per entry
+ // overhead mentioned in RFC 7541 section 4.1.
+ size_t GetDynamicTableSize() const { return header_table_.size(); }
private:
friend class test::HpackEncoderPeer;
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc
index 774a06e8129..dfd8d8fea2c 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc
@@ -155,6 +155,7 @@ class HpackEncoderTest : public QuicheTestWithParam<EncodeStrategy> {
// No further insertions may occur without evictions.
peer_.table()->SetMaxSize(peer_.table()->size());
+ QUICHE_CHECK_EQ(kInitialDynamicTableSize, peer_.table()->size());
}
void SaveHeaders(absl::string_view name, absl::string_view value) {
@@ -254,6 +255,9 @@ class HpackEncoderTest : public QuicheTestWithParam<EncodeStrategy> {
HpackEncoder encoder_;
test::HpackEncoderPeer peer_;
+ // Calculated based on the names and values inserted in SetUp(), above.
+ const size_t kInitialDynamicTableSize = 4 * (10 + 32);
+
const HpackEntry* static_;
const HpackEntry* key_1_;
const HpackEntry* key_2_;
@@ -280,6 +284,7 @@ INSTANTIATE_TEST_SUITE_P(HpackEncoderTests,
::testing::Values(kDefault));
TEST_P(HpackEncoderTestWithDefaultStrategy, EncodeRepresentations) {
+ EXPECT_EQ(kInitialDynamicTableSize, encoder_.GetDynamicTableSize());
encoder_.SetHeaderListener(
[this](absl::string_view name, absl::string_view value) {
this->SaveHeaders(name, value);
@@ -308,6 +313,30 @@ TEST_P(HpackEncoderTestWithDefaultStrategy, EncodeRepresentations) {
Pair("accept", "text/html, text/plain,application/xml"),
Pair("cookie", "val4"),
Pair("withnul", absl::string_view("one\0two", 7))));
+ // Insertions and evictions have happened over the course of the test.
+ EXPECT_GE(kInitialDynamicTableSize, encoder_.GetDynamicTableSize());
+}
+
+TEST_P(HpackEncoderTestWithDefaultStrategy, DynamicTableGrows) {
+ EXPECT_EQ(kInitialDynamicTableSize, encoder_.GetDynamicTableSize());
+ peer_.table()->SetMaxSize(4096);
+ encoder_.SetHeaderListener(
+ [this](absl::string_view name, absl::string_view value) {
+ this->SaveHeaders(name, value);
+ });
+ const std::vector<std::pair<absl::string_view, absl::string_view>>
+ header_list = {{"cookie", "val1; val2;val3"},
+ {":path", "/home"},
+ {"accept", "text/html, text/plain,application/xml"},
+ {"cookie", "val4"},
+ {"withnul", absl::string_view("one\0two", 7)}};
+ std::string out;
+ EXPECT_TRUE(test::HpackEncoderPeer::EncodeRepresentations(&encoder_,
+ header_list, &out));
+
+ EXPECT_FALSE(out.empty());
+ // Insertions have happened over the course of the test.
+ EXPECT_GT(encoder_.GetDynamicTableSize(), kInitialDynamicTableSize);
}
INSTANTIATE_TEST_SUITE_P(HpackEncoderTests,
@@ -479,6 +508,7 @@ TEST_P(HpackEncoderTest, EncodingWithoutCompression) {
Pair("hello", "aloha"),
Pair("multivalue", "value1, value2")));
}
+ EXPECT_EQ(kInitialDynamicTableSize, encoder_.GetDynamicTableSize());
}
TEST_P(HpackEncoderTest, MultipleEncodingPasses) {
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc
index 4deb0dfcc81..d755214b995 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc
@@ -5,7 +5,6 @@
#include "spdy/core/hpack/hpack_entry.h"
#include "absl/strings/str_cat.h"
-#include "spdy/platform/api/spdy_estimate_memory_usage.h"
namespace spdy {
@@ -24,8 +23,4 @@ std::string HpackEntry::GetDebugString() const {
return absl::StrCat("{ name: \"", name_, "\", value: \"", value_, "\" }");
}
-size_t HpackEntry::EstimateMemoryUsage() const {
- return SpdyEstimateMemoryUsage(name_) + SpdyEstimateMemoryUsage(value_);
-}
-
} // namespace spdy
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h
index 0c263584f66..86e39779b7d 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h
@@ -71,9 +71,6 @@ class QUICHE_EXPORT_PRIVATE HpackEntry {
std::string GetDebugString() const;
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
-
private:
std::string name_;
std::string value_;
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.cc
index 8536a8523fc..6358003c2ba 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.cc
@@ -9,7 +9,6 @@
#include "common/platform/api/quiche_logging.h"
#include "spdy/core/hpack/hpack_constants.h"
#include "spdy/core/hpack/hpack_static_table.h"
-#include "spdy/platform/api/spdy_estimate_memory_usage.h"
namespace spdy {
@@ -186,10 +185,4 @@ const HpackEntry* HpackHeaderTable::TryAddEntry(absl::string_view name,
return &dynamic_entries_.front();
}
-size_t HpackHeaderTable::EstimateMemoryUsage() const {
- return SpdyEstimateMemoryUsage(dynamic_entries_) +
- SpdyEstimateMemoryUsage(dynamic_index_) +
- SpdyEstimateMemoryUsage(dynamic_name_index_);
-}
-
} // namespace spdy
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h
index 49478cd35b2..427fc857ef5 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h
@@ -98,9 +98,6 @@ class QUICHE_EXPORT_PRIVATE HpackHeaderTable {
const HpackEntry* TryAddEntry(absl::string_view name,
absl::string_view value);
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
-
private:
// Returns number of evictions required to enter |name| & |value|.
size_t EvictionCountForEntry(absl::string_view name,
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc
index 7b925680d4b..bce24fa610d 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc
@@ -7,7 +7,6 @@
#include <utility>
#include "common/platform/api/quiche_logging.h"
-#include "spdy/platform/api/spdy_estimate_memory_usage.h"
namespace spdy {
@@ -97,8 +96,4 @@ void HpackOutputStream::BoundedTakeString(size_t max_size,
}
}
-size_t HpackOutputStream::EstimateMemoryUsage() const {
- return SpdyEstimateMemoryUsage(buffer_);
-}
-
} // namespace spdy
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h
index 59e3bb33a42..ec657b54064 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h
@@ -61,9 +61,6 @@ class QUICHE_EXPORT_PRIVATE HpackOutputStream {
// Size in bytes of stream's internal buffer.
size_t size() const { return buffer_.size(); }
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
-
private:
// The internal bit buffer.
std::string buffer_;
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.cc
index 7594943b43e..e5a403d7162 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.cc
@@ -8,7 +8,6 @@
#include "common/platform/api/quiche_logging.h"
#include "spdy/core/hpack/hpack_constants.h"
#include "spdy/core/hpack/hpack_entry.h"
-#include "spdy/platform/api/spdy_estimate_memory_usage.h"
namespace spdy {
@@ -48,10 +47,4 @@ bool HpackStaticTable::IsInitialized() const {
return !static_entries_.empty();
}
-size_t HpackStaticTable::EstimateMemoryUsage() const {
- return SpdyEstimateMemoryUsage(static_entries_) +
- SpdyEstimateMemoryUsage(static_index_) +
- SpdyEstimateMemoryUsage(static_name_index_);
-}
-
} // namespace spdy
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h
index 346332e4134..307fc1cc990 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h
@@ -43,9 +43,6 @@ class QUICHE_EXPORT_PRIVATE HpackStaticTable {
return static_name_index_;
}
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
-
private:
HpackHeaderTable::StaticEntryTable static_entries_;
// The following two members have string_views that point to strings stored in
diff --git a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc
index b20d0032aff..e2e311dda07 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc
@@ -30,8 +30,6 @@
#include "spdy/core/spdy_header_block.h"
#include "spdy/core/spdy_headers_handler_interface.h"
#include "spdy/core/spdy_protocol.h"
-#include "spdy/platform/api/spdy_estimate_memory_usage.h"
-#include "spdy/platform/api/spdy_string_utils.h"
using ::spdy::ExtensionVisitorInterface;
using ::spdy::HpackDecoderAdapter;
@@ -40,7 +38,6 @@ using ::spdy::ParseErrorCode;
using ::spdy::ParseFrameType;
using ::spdy::SpdyAltSvcWireFormat;
using ::spdy::SpdyErrorCode;
-using ::spdy::SpdyEstimateMemoryUsage;
using ::spdy::SpdyFramerDebugVisitorInterface;
using ::spdy::SpdyFramerVisitorInterface;
using ::spdy::SpdyFrameType;
@@ -249,6 +246,8 @@ const char* Http2DecoderAdapter::SpdyFramerErrorToString(
return "HPACK_FRAGMENT_TOO_LONG";
case SPDY_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT:
return "HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT";
+ case SPDY_STOP_PROCESSING:
+ return "STOP_PROCESSING";
case LAST_ERROR:
return "UNKNOWN_ERROR";
}
@@ -320,11 +319,9 @@ bool Http2DecoderAdapter::probable_http_response() const {
return latched_probable_http_response_;
}
-size_t Http2DecoderAdapter::EstimateMemoryUsage() const {
- // Skip |frame_decoder_|, |frame_header_| and |hpack_first_frame_header_| as
- // they don't allocate.
- return SpdyEstimateMemoryUsage(alt_svc_origin_) +
- SpdyEstimateMemoryUsage(alt_svc_value_);
+void Http2DecoderAdapter::StopProcessing() {
+ SetSpdyErrorAndNotify(SpdyFramerError::SPDY_STOP_PROCESSING,
+ "Ignoring further events on this connection.");
}
// ===========================================================================
@@ -784,12 +781,12 @@ void Http2DecoderAdapter::OnFrameSizeError(const Http2FrameHeader& header) {
QUICHE_DVLOG(1) << "OnFrameSizeError: " << header;
size_t recv_limit = recv_frame_size_limit_;
if (header.payload_length > recv_limit) {
- SetSpdyErrorAndNotify(SpdyFramerError::SPDY_OVERSIZED_PAYLOAD, "");
- return;
- }
- if (header.type != Http2FrameType::DATA &&
- header.payload_length > recv_limit) {
- SetSpdyErrorAndNotify(SpdyFramerError::SPDY_CONTROL_PAYLOAD_TOO_LARGE, "");
+ if (header.type == Http2FrameType::DATA) {
+ SetSpdyErrorAndNotify(SpdyFramerError::SPDY_OVERSIZED_PAYLOAD, "");
+ } else {
+ SetSpdyErrorAndNotify(SpdyFramerError::SPDY_CONTROL_PAYLOAD_TOO_LARGE,
+ "");
+ }
return;
}
switch (header.type) {
diff --git a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h
index f05152b6468..81f12572c35 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h
@@ -95,6 +95,10 @@ class QUICHE_EXPORT_PRIVATE Http2DecoderAdapter
SPDY_HPACK_FRAGMENT_TOO_LONG,
SPDY_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT,
+ // Set if the visitor no longer wishes to receive events for this
+ // connection.
+ SPDY_STOP_PROCESSING,
+
LAST_ERROR, // Must be the last entry in the enum.
};
@@ -153,13 +157,17 @@ class QUICHE_EXPORT_PRIVATE Http2DecoderAdapter
// has responded with an HTTP/1.1 (or earlier) response.
bool probable_http_response() const;
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
-
spdy::HpackDecoderAdapter* GetHpackDecoder();
+ const spdy::HpackDecoderAdapter* GetHpackDecoder() const {
+ return hpack_decoder_.get();
+ }
bool HasError() const;
+ // A visitor may call this method to indicate it no longer wishes to receive
+ // events for this connection.
+ void StopProcessing();
+
private:
bool OnFrameHeader(const Http2FrameHeader& header) override;
void OnDataStart(const Http2FrameHeader& header) override;
diff --git a/chromium/net/third_party/quiche/src/spdy/core/http2_header_block_hpack_listener.h b/chromium/net/third_party/quiche/src/spdy/core/http2_header_block_hpack_listener.h
new file mode 100644
index 00000000000..19352e54058
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/spdy/core/http2_header_block_hpack_listener.h
@@ -0,0 +1,47 @@
+#ifndef QUICHE_SPDY_CORE_HTTP2_HEADER_BLOCK_HPACK_LISTENER_H_
+#define QUICHE_SPDY_CORE_HTTP2_HEADER_BLOCK_HPACK_LISTENER_H_
+
+#include "absl/strings/string_view.h"
+#include "http2/hpack/decoder/hpack_decoder_listener.h"
+#include "common/platform/api/quiche_logging.h"
+#include "spdy/core/spdy_header_block.h"
+
+namespace spdy {
+
+// This class simply gathers the key-value pairs emitted by an HpackDecoder in
+// a SpdyHeaderBlock.
+class Http2HeaderBlockHpackListener : public http2::HpackDecoderListener {
+ public:
+ Http2HeaderBlockHpackListener() {}
+
+ void OnHeaderListStart() override {
+ header_block_.clear();
+ hpack_error_ = false;
+ }
+
+ void OnHeader(const std::string& name, const std::string& value) override {
+ header_block_.AppendValueOrAddHeader(name, value);
+ }
+
+ void OnHeaderListEnd() override {}
+
+ void OnHeaderErrorDetected(absl::string_view error_message) override {
+ QUICHE_VLOG(1) << error_message;
+ hpack_error_ = true;
+ }
+
+ SpdyHeaderBlock release_header_block() {
+ SpdyHeaderBlock block = std::move(header_block_);
+ header_block_ = {};
+ return block;
+ }
+ bool hpack_error() const { return hpack_error_; }
+
+ private:
+ SpdyHeaderBlock header_block_;
+ bool hpack_error_ = false;
+};
+
+} // namespace spdy
+
+#endif // QUICHE_SPDY_CORE_HTTP2_HEADER_BLOCK_HPACK_LISTENER_H_
diff --git a/chromium/net/third_party/quiche/src/spdy/core/metadata_extension.cc b/chromium/net/third_party/quiche/src/spdy/core/metadata_extension.cc
new file mode 100644
index 00000000000..12f6af14843
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/spdy/core/metadata_extension.cc
@@ -0,0 +1,195 @@
+#include "spdy/core/metadata_extension.h"
+
+#include <list>
+#include <string>
+
+#include "absl/memory/memory.h"
+#include "absl/strings/str_cat.h"
+#include "http2/decoder/decode_buffer.h"
+#include "http2/hpack/decoder/hpack_decoder.h"
+#include "common/platform/api/quiche_bug_tracker.h"
+#include "common/platform/api/quiche_logging.h"
+#include "spdy/core/hpack/hpack_encoder.h"
+#include "spdy/core/http2_header_block_hpack_listener.h"
+
+namespace spdy {
+
+// Non-standard constants related to METADATA frames.
+const SpdySettingsId MetadataVisitor::kMetadataExtensionId = 0x4d44;
+const uint8_t MetadataVisitor::kMetadataFrameType = 0x4d;
+const uint8_t MetadataVisitor::kEndMetadataFlag = 0x4;
+
+namespace {
+
+const size_t kMaxMetadataBlockSize = 1 << 20; // 1 MB
+
+// This class uses an HpackEncoder to serialize a METADATA block as a series of
+// METADATA frames.
+class MetadataFrameSequence : public MetadataSerializer::FrameSequence {
+ public:
+ MetadataFrameSequence(SpdyStreamId stream_id, spdy::SpdyHeaderBlock payload)
+ : stream_id_(stream_id), payload_(std::move(payload)) {
+ // Metadata should not use HPACK compression.
+ encoder_.DisableCompression();
+ HpackEncoder::Representations r;
+ for (const auto& kv_pair : payload_) {
+ r.push_back(kv_pair);
+ }
+ progressive_encoder_ = encoder_.EncodeRepresentations(r);
+ }
+
+ // Copies are not allowed.
+ MetadataFrameSequence(const MetadataFrameSequence& other) = delete;
+ MetadataFrameSequence& operator=(const MetadataFrameSequence& other) = delete;
+
+ std::unique_ptr<spdy::SpdyFrameIR> Next() override;
+
+ private:
+ SpdyStreamId stream_id_;
+ SpdyHeaderBlock payload_;
+ HpackEncoder encoder_;
+ std::unique_ptr<HpackEncoder::ProgressiveEncoder> progressive_encoder_;
+};
+
+std::unique_ptr<spdy::SpdyFrameIR> MetadataFrameSequence::Next() {
+ if (!progressive_encoder_->HasNext()) {
+ return nullptr;
+ }
+ std::string payload;
+ // METADATA frames obey the HTTP/2 maximum frame size.
+ progressive_encoder_->Next(spdy::kHttp2DefaultFramePayloadLimit, &payload);
+ const bool end_metadata = (!progressive_encoder_->HasNext());
+ const uint8_t flags = end_metadata ? MetadataVisitor::kEndMetadataFlag : 0;
+ return absl::make_unique<spdy::SpdyUnknownIR>(
+ stream_id_, MetadataVisitor::kMetadataFrameType, flags,
+ std::move(payload));
+}
+
+} // anonymous namespace
+
+struct MetadataVisitor::MetadataPayloadState {
+ MetadataPayloadState(size_t remaining, bool end)
+ : bytes_remaining(remaining), end_metadata(end) {}
+ std::list<std::string> buffer;
+ size_t bytes_remaining;
+ bool end_metadata;
+};
+
+MetadataVisitor::MetadataVisitor(OnCompletePayload on_payload,
+ OnMetadataSupport on_support)
+ : on_payload_(std::move(on_payload)),
+ on_support_(std::move(on_support)),
+ peer_supports_metadata_(MetadataSupportState::UNSPECIFIED) {}
+
+MetadataVisitor::~MetadataVisitor() {}
+
+void MetadataVisitor::OnSetting(SpdySettingsId id, uint32_t value) {
+ QUICHE_VLOG(1) << "MetadataVisitor::OnSetting(" << id << ", " << value << ")";
+ if (id == kMetadataExtensionId) {
+ if (value == 0) {
+ const MetadataSupportState previous_state = peer_supports_metadata_;
+ peer_supports_metadata_ = MetadataSupportState::NOT_SUPPORTED;
+ if (previous_state == MetadataSupportState::UNSPECIFIED ||
+ previous_state == MetadataSupportState::SUPPORTED) {
+ on_support_(false);
+ }
+ } else if (value == 1) {
+ const MetadataSupportState previous_state = peer_supports_metadata_;
+ peer_supports_metadata_ = MetadataSupportState::SUPPORTED;
+ if (previous_state == MetadataSupportState::UNSPECIFIED ||
+ previous_state == MetadataSupportState::NOT_SUPPORTED) {
+ on_support_(true);
+ }
+ } else {
+ LOG_EVERY_N_SEC(WARNING, 1)
+ << "Unrecognized value for setting " << id << ": " << value;
+ }
+ }
+}
+
+bool MetadataVisitor::OnFrameHeader(SpdyStreamId stream_id, size_t length,
+ uint8_t type, uint8_t flags) {
+ QUICHE_VLOG(1) << "OnFrameHeader(stream_id=" << stream_id
+ << ", length=" << length << ", type=" << static_cast<int>(type)
+ << ", flags=" << static_cast<int>(flags);
+ // TODO(birenroy): Consider disabling METADATA handling until our setting
+ // advertising METADATA support has been acked.
+ if (type != kMetadataFrameType) {
+ return false;
+ }
+ auto it = metadata_map_.find(stream_id);
+ if (it == metadata_map_.end()) {
+ auto state = absl::make_unique<MetadataPayloadState>(
+ length, flags & kEndMetadataFlag);
+ auto result = metadata_map_.insert(std::make_pair(stream_id,
+ std::move(state)));
+ QUICHE_BUG_IF(bug_if_2781_1, !result.second) << "Map insertion failed.";
+ it = result.first;
+ } else {
+ QUICHE_BUG_IF(bug_22051_1, it->second->end_metadata)
+ << "Inconsistent metadata payload state!";
+ QUICHE_BUG_IF(bug_if_2781_2, it->second->bytes_remaining > 0)
+ << "Incomplete metadata block!";
+ }
+
+ if (it->second == nullptr) {
+ QUICHE_BUG(bug_2781_3) << "Null metadata payload state!";
+ return false;
+ }
+ current_stream_ = stream_id;
+ it->second->bytes_remaining = length;
+ it->second->end_metadata = (flags & kEndMetadataFlag);
+ return true;
+}
+
+void MetadataVisitor::OnFramePayload(const char* data, size_t len) {
+ QUICHE_VLOG(1) << "OnFramePayload(stream_id=" << current_stream_
+ << ", len=" << len << ")";
+ auto it = metadata_map_.find(current_stream_);
+ if (it == metadata_map_.end() || it->second == nullptr) {
+ QUICHE_BUG(bug_2781_4) << "Invalid order of operations on MetadataVisitor.";
+ } else {
+ MetadataPayloadState* state = it->second.get(); // For readability.
+ state->buffer.push_back(std::string(data, len));
+ if (len < state->bytes_remaining) {
+ state->bytes_remaining -= len;
+ } else {
+ QUICHE_BUG_IF(bug_22051_2, len > state->bytes_remaining)
+ << "Metadata payload overflow! len: " << len
+ << " bytes_remaining: " << state->bytes_remaining;
+ state->bytes_remaining = 0;
+ if (state->end_metadata) {
+ // The whole process of decoding the HPACK-encoded metadata block,
+ // below, is more cumbersome than it ought to be.
+ spdy::Http2HeaderBlockHpackListener listener;
+ http2::HpackDecoder decoder(&listener, kMaxMetadataBlockSize);
+
+ // If any operations fail, the decode process should be aborted.
+ bool success = decoder.StartDecodingBlock();
+ for (const std::string& slice : state->buffer) {
+ if (!success) {
+ break;
+ }
+ http2::DecodeBuffer buffer(slice.data(), slice.size());
+ success = success && decoder.DecodeFragment(&buffer);
+ }
+ success =
+ success && decoder.EndDecodingBlock() && !listener.hpack_error();
+ if (success) {
+ on_payload_(current_stream_, listener.release_header_block());
+ }
+ // TODO(birenroy): add varz counting metadata decode successes/failures.
+ metadata_map_.erase(it);
+ }
+ }
+ }
+}
+
+std::unique_ptr<MetadataSerializer::FrameSequence>
+MetadataSerializer::FrameSequenceForPayload(SpdyStreamId stream_id,
+ MetadataPayload payload) {
+ return absl::make_unique<MetadataFrameSequence>(stream_id,
+ std::move(payload));
+}
+
+} // namespace spdy
diff --git a/chromium/net/third_party/quiche/src/spdy/core/metadata_extension.h b/chromium/net/third_party/quiche/src/spdy/core/metadata_extension.h
new file mode 100644
index 00000000000..043d6f0ebcd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/spdy/core/metadata_extension.h
@@ -0,0 +1,116 @@
+#ifndef QUICHE_SPDY_CORE_METADATA_EXTENSION_H_
+#define QUICHE_SPDY_CORE_METADATA_EXTENSION_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/container/flat_hash_map.h"
+#include "spdy/core/http2_frame_decoder_adapter.h"
+#include "spdy/core/spdy_header_block.h"
+#include "spdy/core/spdy_protocol.h"
+#include "spdy/core/zero_copy_output_buffer.h"
+
+namespace spdy {
+
+// An implementation of the ExtensionVisitorInterface that can parse
+// METADATA frames. METADATA is a non-standard HTTP/2 extension developed and
+// used internally at Google. A peer advertises support for METADATA by sending
+// a setting with a setting ID of kMetadataExtensionId and a value of 1.
+//
+// Metadata is represented as a HPACK header block with literal encoding.
+class MetadataVisitor : public spdy::ExtensionVisitorInterface {
+ public:
+ using MetadataPayload = spdy::SpdyHeaderBlock;
+
+ static_assert(!std::is_copy_constructible<MetadataPayload>::value,
+ "MetadataPayload should be a move-only type!");
+
+ using OnMetadataSupport = std::function<void(bool)>;
+ using OnCompletePayload =
+ std::function<void(spdy::SpdyStreamId, MetadataPayload)>;
+
+ // The HTTP/2 SETTINGS ID that is used to indicate support for METADATA
+ // frames.
+ static const spdy::SpdySettingsId kMetadataExtensionId;
+
+ // The 8-bit frame type code for a METADATA frame.
+ static const uint8_t kMetadataFrameType;
+
+ // The flag that indicates the end of a logical metadata block. Due to frame
+ // size limits, a single metadata block may be emitted as several HTTP/2
+ // frames.
+ static const uint8_t kEndMetadataFlag;
+
+ // |on_payload| is invoked whenever a complete metadata payload is received.
+ // |on_support| is invoked whenever the peer's advertised support for metadata
+ // changes.
+ MetadataVisitor(OnCompletePayload on_payload, OnMetadataSupport on_support);
+ ~MetadataVisitor() override;
+
+ MetadataVisitor(const MetadataVisitor&) = delete;
+ MetadataVisitor& operator=(const MetadataVisitor&) = delete;
+
+ // Interprets the non-standard setting indicating support for METADATA.
+ void OnSetting(spdy::SpdySettingsId id, uint32_t value) override;
+
+ // Returns true iff |type| indicates a METADATA frame.
+ bool OnFrameHeader(spdy::SpdyStreamId stream_id, size_t length, uint8_t type,
+ uint8_t flags) override;
+
+ // Consumes a METADATA frame payload. Invokes the registered callback when a
+ // complete payload has been received.
+ void OnFramePayload(const char* data, size_t len) override;
+
+ // Returns true if the peer has advertised support for METADATA via the
+ // appropriate setting.
+ bool PeerSupportsMetadata() const {
+ return peer_supports_metadata_ == MetadataSupportState::SUPPORTED;
+ }
+
+ private:
+ enum class MetadataSupportState : uint8_t {
+ UNSPECIFIED,
+ SUPPORTED,
+ NOT_SUPPORTED,
+ };
+
+ struct MetadataPayloadState;
+
+ using StreamMetadataMap =
+ absl::flat_hash_map<spdy::SpdyStreamId,
+ std::unique_ptr<MetadataPayloadState>>;
+
+ OnCompletePayload on_payload_;
+ OnMetadataSupport on_support_;
+ StreamMetadataMap metadata_map_;
+ spdy::SpdyStreamId current_stream_;
+ MetadataSupportState peer_supports_metadata_;
+};
+
+// A class that serializes metadata blocks as sequences of frames.
+class MetadataSerializer {
+ public:
+ using MetadataPayload = spdy::SpdyHeaderBlock;
+
+ class FrameSequence {
+ public:
+ virtual ~FrameSequence() {}
+
+ // Returns nullptr once the sequence has been exhausted.
+ virtual std::unique_ptr<spdy::SpdyFrameIR> Next() = 0;
+ };
+
+ MetadataSerializer() {}
+
+ MetadataSerializer(const MetadataSerializer&) = delete;
+ MetadataSerializer& operator=(const MetadataSerializer&) = delete;
+
+ // Returns nullptr on failure.
+ std::unique_ptr<FrameSequence> FrameSequenceForPayload(
+ spdy::SpdyStreamId stream_id, MetadataPayload payload);
+};
+
+} // namespace spdy
+
+#endif // QUICHE_SPDY_CORE_METADATA_EXTENSION_H_
diff --git a/chromium/net/third_party/quiche/src/spdy/core/metadata_extension_test.cc b/chromium/net/third_party/quiche/src/spdy/core/metadata_extension_test.cc
new file mode 100644
index 00000000000..07a5ee1113f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/spdy/core/metadata_extension_test.cc
@@ -0,0 +1,226 @@
+#include "spdy/core/metadata_extension.h"
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/functional/bind_front.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "common/platform/api/quiche_test.h"
+#include "spdy/core/array_output_buffer.h"
+#include "spdy/core/mock_spdy_framer_visitor.h"
+#include "spdy/core/spdy_framer.h"
+#include "spdy/core/spdy_header_block.h"
+#include "spdy/core/spdy_no_op_visitor.h"
+#include "spdy/core/spdy_protocol.h"
+
+namespace spdy {
+namespace test {
+namespace {
+
+using ::absl::bind_front;
+using ::spdy::SpdyFramer;
+using ::spdy::SpdyHeaderBlock;
+using ::spdy::test::MockSpdyFramerVisitor;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+
+const size_t kBufferSize = 64 * 1024;
+char kBuffer[kBufferSize];
+
+class MetadataExtensionTest : public QuicheTest {
+ protected:
+ MetadataExtensionTest() : test_buffer_(kBuffer, kBufferSize) {}
+
+ void SetUp() override {
+ extension_ = absl::make_unique<MetadataVisitor>(
+ bind_front(&MetadataExtensionTest::OnCompletePayload, this),
+ bind_front(&MetadataExtensionTest::OnMetadataSupport, this));
+ }
+
+ void OnCompletePayload(spdy::SpdyStreamId stream_id,
+ MetadataVisitor::MetadataPayload payload) {
+ ++received_count_;
+ received_payload_map_.insert(std::make_pair(stream_id, std::move(payload)));
+ }
+
+ void OnMetadataSupport(bool peer_supports_metadata) {
+ EXPECT_EQ(peer_supports_metadata, extension_->PeerSupportsMetadata());
+ received_metadata_support_.push_back(peer_supports_metadata);
+ }
+
+ MetadataSerializer::MetadataPayload PayloadForData(absl::string_view data) {
+ SpdyHeaderBlock block;
+ block["example-payload"] = data;
+ return block;
+ }
+
+ std::unique_ptr<MetadataVisitor> extension_;
+ absl::flat_hash_map<spdy::SpdyStreamId, SpdyHeaderBlock>
+ received_payload_map_;
+ std::vector<bool> received_metadata_support_;
+ size_t received_count_ = 0;
+ spdy::ArrayOutputBuffer test_buffer_;
+};
+
+// This test verifies that the MetadataVisitor is initialized to a state where
+// it believes the peer does not support metadata.
+TEST_F(MetadataExtensionTest, MetadataNotSupported) {
+ EXPECT_FALSE(extension_->PeerSupportsMetadata());
+ EXPECT_THAT(received_metadata_support_, IsEmpty());
+}
+
+// This test verifies that upon receiving a specific setting, the extension
+// realizes that the peer supports metadata.
+TEST_F(MetadataExtensionTest, MetadataSupported) {
+ EXPECT_FALSE(extension_->PeerSupportsMetadata());
+ // 3 is not an appropriate value for the metadata extension key.
+ extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 3);
+ EXPECT_FALSE(extension_->PeerSupportsMetadata());
+ extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
+ ASSERT_TRUE(extension_->PeerSupportsMetadata());
+ extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 0);
+ EXPECT_FALSE(extension_->PeerSupportsMetadata());
+ EXPECT_THAT(received_metadata_support_, ElementsAre(true, false));
+}
+
+TEST_F(MetadataExtensionTest, MetadataIgnoredWithoutExtension) {
+ const char kData[] = "some payload";
+ SpdyHeaderBlock payload = PayloadForData(kData);
+
+ extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
+ ASSERT_TRUE(extension_->PeerSupportsMetadata());
+
+ MetadataSerializer serializer;
+ auto sequence = serializer.FrameSequenceForPayload(3, std::move(payload));
+ ASSERT_TRUE(sequence != nullptr);
+
+ http2::Http2DecoderAdapter deframer;
+ ::testing::StrictMock<MockSpdyFramerVisitor> visitor;
+ deframer.set_visitor(&visitor);
+
+ // The Return(true) should not be necessary. http://b/36023792
+ EXPECT_CALL(visitor, OnUnknownFrame(3, MetadataVisitor::kMetadataFrameType))
+ .WillOnce(::testing::Return(true));
+
+ SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+ auto frame = sequence->Next();
+ ASSERT_TRUE(frame != nullptr);
+ while (frame != nullptr) {
+ const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_);
+ ASSERT_GT(frame_size, 0);
+ ASSERT_FALSE(deframer.HasError());
+ ASSERT_EQ(frame_size, test_buffer_.Size());
+ EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size));
+ test_buffer_.Reset();
+ frame = sequence->Next();
+ }
+ EXPECT_FALSE(deframer.HasError());
+ EXPECT_THAT(received_metadata_support_, ElementsAre(true));
+}
+
+// This test verifies that the METADATA frame emitted by a MetadataExtension
+// can be parsed by another SpdyFramer with a MetadataVisitor.
+TEST_F(MetadataExtensionTest, MetadataPayloadEndToEnd) {
+ SpdyHeaderBlock block1;
+ block1["foo"] = "Some metadata value.";
+ SpdyHeaderBlock block2;
+ block2["bar"] =
+ "The color taupe truly represents a triumph of the human spirit over "
+ "adversity.";
+ block2["baz"] =
+ "Or perhaps it represents abject surrender to the implacable and "
+ "incomprehensible forces of the universe.";
+ const absl::string_view binary_payload{"binary\0payload", 14};
+ block2["qux"] = binary_payload;
+ EXPECT_EQ(binary_payload, block2["qux"]);
+ for (const SpdyHeaderBlock& payload_block :
+ {std::move(block1), std::move(block2)}) {
+ extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
+ ASSERT_TRUE(extension_->PeerSupportsMetadata());
+
+ MetadataSerializer serializer;
+ auto sequence =
+ serializer.FrameSequenceForPayload(3, payload_block.Clone());
+ ASSERT_TRUE(sequence != nullptr);
+
+ http2::Http2DecoderAdapter deframer;
+ ::spdy::SpdyNoOpVisitor visitor;
+ deframer.set_visitor(&visitor);
+ deframer.set_extension_visitor(extension_.get());
+ SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+ auto frame = sequence->Next();
+ ASSERT_TRUE(frame != nullptr);
+ while (frame != nullptr) {
+ const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_);
+ ASSERT_GT(frame_size, 0);
+ ASSERT_FALSE(deframer.HasError());
+ ASSERT_EQ(frame_size, test_buffer_.Size());
+ EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size));
+ test_buffer_.Reset();
+ frame = sequence->Next();
+ }
+ EXPECT_EQ(1, received_count_);
+ auto it = received_payload_map_.find(3);
+ ASSERT_TRUE(it != received_payload_map_.end());
+ EXPECT_EQ(payload_block, it->second);
+
+ received_count_ = 0;
+ received_payload_map_.clear();
+ }
+}
+
+// This test verifies that METADATA frames for two different streams can be
+// interleaved and still successfully parsed by another SpdyFramer with a
+// MetadataVisitor.
+TEST_F(MetadataExtensionTest, MetadataPayloadInterleaved) {
+ const std::string kData1 = std::string(65 * 1024, 'a');
+ const std::string kData2 = std::string(65 * 1024, 'b');
+ const SpdyHeaderBlock payload1 = PayloadForData(kData1);
+ const SpdyHeaderBlock payload2 = PayloadForData(kData2);
+
+ extension_->OnSetting(MetadataVisitor::kMetadataExtensionId, 1);
+ ASSERT_TRUE(extension_->PeerSupportsMetadata());
+
+ MetadataSerializer serializer;
+ auto sequence1 = serializer.FrameSequenceForPayload(3, payload1.Clone());
+ ASSERT_TRUE(sequence1 != nullptr);
+
+ auto sequence2 = serializer.FrameSequenceForPayload(5, payload2.Clone());
+ ASSERT_TRUE(sequence2 != nullptr);
+
+ http2::Http2DecoderAdapter deframer;
+ ::spdy::SpdyNoOpVisitor visitor;
+ deframer.set_visitor(&visitor);
+ deframer.set_extension_visitor(extension_.get());
+
+ SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
+ auto frame1 = sequence1->Next();
+ ASSERT_TRUE(frame1 != nullptr);
+ auto frame2 = sequence2->Next();
+ ASSERT_TRUE(frame2 != nullptr);
+ while (frame1 != nullptr || frame2 != nullptr) {
+ for (auto frame : {frame1.get(), frame2.get()}) {
+ if (frame != nullptr) {
+ const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_);
+ ASSERT_GT(frame_size, 0);
+ ASSERT_FALSE(deframer.HasError());
+ ASSERT_EQ(frame_size, test_buffer_.Size());
+ EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size));
+ test_buffer_.Reset();
+ }
+ }
+ frame1 = sequence1->Next();
+ frame2 = sequence2->Next();
+ }
+ EXPECT_EQ(2, received_count_);
+ auto it = received_payload_map_.find(3);
+ ASSERT_TRUE(it != received_payload_map_.end());
+ EXPECT_EQ(payload1, it->second);
+
+ it = received_payload_map_.find(5);
+ ASSERT_TRUE(it != received_payload_map_.end());
+ EXPECT_EQ(payload2, it->second);
+}
+
+} // anonymous namespace
+} // namespace test
+} // namespace spdy
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc
index 94218f697da..f1cca0da3b1 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc
@@ -11,7 +11,6 @@
#include "absl/strings/str_cat.h"
#include "common/platform/api/quiche_logging.h"
-#include "spdy/platform/api/spdy_string_utils.h"
namespace spdy {
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc
index d6f0f889876..1b6eb8a6a9f 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc
@@ -18,8 +18,6 @@
#include "spdy/core/spdy_bitmasks.h"
#include "spdy/core/spdy_frame_builder.h"
#include "spdy/core/spdy_frame_reader.h"
-#include "spdy/platform/api/spdy_estimate_memory_usage.h"
-#include "spdy/platform/api/spdy_string_utils.h"
namespace spdy {
@@ -303,9 +301,8 @@ size_t SpdyFramer::SpdyFrameIterator::NextFrame(ZeroCopyOutputBuffer* output) {
const size_t size_without_block =
is_first_frame_ ? GetFrameSizeSansBlock() : kContinuationFrameMinimumSize;
- auto encoding = std::make_unique<std::string>();
- encoder_->Next(kHttp2MaxControlFrameSendSize - size_without_block,
- encoding.get());
+ std::string encoding;
+ encoder_->Next(kHttp2MaxControlFrameSendSize - size_without_block, &encoding);
has_next_frame_ = encoder_->HasNext();
if (framer_->debug_visitor_ != nullptr) {
@@ -316,14 +313,14 @@ size_t SpdyFramer::SpdyFrameIterator::NextFrame(ZeroCopyOutputBuffer* output) {
framer_->debug_visitor_->OnSendCompressedFrame(
frame_ir.stream_id(),
is_first_frame_ ? frame_ir.frame_type() : SpdyFrameType::CONTINUATION,
- header_list_size, size_without_block + encoding->size());
+ header_list_size, size_without_block + encoding.size());
}
const size_t free_bytes_before = output->BytesFree();
bool ok = false;
if (is_first_frame_) {
is_first_frame_ = false;
- ok = SerializeGivenEncoding(*encoding, output);
+ ok = SerializeGivenEncoding(encoding, output);
} else {
SpdyContinuationIR continuation_ir(frame_ir.stream_id());
continuation_ir.take_encoding(std::move(encoding));
@@ -1380,8 +1377,4 @@ size_t SpdyFramer::header_encoder_table_size() const {
}
}
-size_t SpdyFramer::EstimateMemoryUsage() const {
- return SpdyEstimateMemoryUsage(hpack_encoder_);
-}
-
} // namespace spdy
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h
index 4e3dc35f1d3..6f7a5dc9bd6 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h
@@ -236,8 +236,9 @@ class QUICHE_EXPORT_PRIVATE SpdyFramer {
// Get (and lazily initialize) the HPACK encoder state.
HpackEncoder* GetHpackEncoder();
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
+ // Gets the HPACK encoder state. Returns nullptr if the encoder has not been
+ // initialized.
+ const HpackEncoder* GetHpackEncoder() const { return hpack_encoder_.get(); }
protected:
friend class test::SpdyFramerPeer;
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc
index c4b5979a874..138c4873b79 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc
@@ -16,6 +16,7 @@
#include "absl/base/macros.h"
#include "common/platform/api/quiche_logging.h"
#include "common/platform/api/quiche_test.h"
+#include "common/quiche_text_utils.h"
#include "spdy/core/array_output_buffer.h"
#include "spdy/core/mock_spdy_framer_visitor.h"
#include "spdy/core/recording_headers_handler.h"
@@ -24,7 +25,6 @@
#include "spdy/core/spdy_frame_reader.h"
#include "spdy/core/spdy_protocol.h"
#include "spdy/core/spdy_test_utils.h"
-#include "spdy/platform/api/spdy_string_utils.h"
using ::http2::Http2DecoderAdapter;
using ::testing::_;
@@ -287,7 +287,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
QUICHE_VLOG(1) << "OnStreamFrameData(" << stream_id << ", data, " << len
<< ", "
<< ") data:\n"
- << SpdyHexDump(absl::string_view(data, len));
+ << quiche::QuicheTextUtils::HexDump(
+ absl::string_view(data, len));
EXPECT_EQ(header_stream_id_, stream_id);
data_bytes_ += len;
@@ -1040,8 +1041,7 @@ TEST_P(SpdyFramerTest, ContinuationWithStreamIdZero) {
deframer_.set_visitor(&visitor);
SpdyContinuationIR continuation(/* stream_id = */ 0);
- auto some_nonsense_encoding =
- std::make_unique<std::string>("some nonsense encoding");
+ std::string some_nonsense_encoding = "some nonsense encoding";
continuation.take_encoding(std::move(some_nonsense_encoding));
continuation.set_end_headers(true);
SpdySerializedFrame frame(framer_.SerializeContinuation(continuation));
@@ -1230,6 +1230,92 @@ TEST_P(SpdyFramerTest, Basic) {
EXPECT_EQ(4, visitor.data_frame_count_);
}
+// Verifies that the decoder stops delivering events after a user error.
+TEST_P(SpdyFramerTest, BasicWithError) {
+ // Send HEADERS frames with PRIORITY and END_HEADERS set.
+ // frame-format off
+ const unsigned char kH2Input[] = {
+ 0x00, 0x00, 0x01, // Length: 1
+ 0x01, // Type: HEADERS
+ 0x04, // Flags: END_HEADERS
+ 0x00, 0x00, 0x00, 0x01, // Stream: 1
+ 0x8c, // :status: 200
+
+ 0x00, 0x00, 0x0c, // Length: 12
+ 0x00, // Type: DATA
+ 0x00, // Flags: none
+ 0x00, 0x00, 0x00, 0x01, // Stream: 1
+ 0xde, 0xad, 0xbe, 0xef, // Payload
+ 0xde, 0xad, 0xbe, 0xef, //
+ 0xde, 0xad, 0xbe, 0xef, //
+
+ 0x00, 0x00, 0x06, // Length: 5
+ 0x01, // Type: HEADERS
+ 0x24, // Flags: END_HEADERS|PRIORITY
+ 0x00, 0x00, 0x00, 0x03, // Stream: 3
+ 0x00, 0x00, 0x00, 0x00, // Parent: 0
+ 0x82, // Weight: 131
+ 0x8c, // :status: 200
+
+ 0x00, 0x00, 0x08, // Length: 8
+ 0x00, // Type: DATA
+ 0x00, // Flags: none
+ 0x00, 0x00, 0x00, 0x03, // Stream: 3
+ 0xde, 0xad, 0xbe, 0xef, // Payload
+ 0xde, 0xad, 0xbe, 0xef, //
+
+ 0x00, 0x00, 0x04, // Length: 4
+ 0x00, // Type: DATA
+ 0x00, // Flags: none
+ 0x00, 0x00, 0x00, 0x01, // Stream: 1
+ 0xde, 0xad, 0xbe, 0xef, // Payload
+
+ 0x00, 0x00, 0x04, // Length: 4
+ 0x03, // Type: RST_STREAM
+ 0x00, // Flags: none
+ 0x00, 0x00, 0x00, 0x01, // Stream: 1
+ 0x00, 0x00, 0x00, 0x08, // Error: CANCEL
+
+ 0x00, 0x00, 0x00, // Length: 0
+ 0x00, // Type: DATA
+ 0x00, // Flags: none
+ 0x00, 0x00, 0x00, 0x03, // Stream: 3
+
+ 0x00, 0x00, 0x04, // Length: 4
+ 0x03, // Type: RST_STREAM
+ 0x00, // Flags: none
+ 0x00, 0x00, 0x00, 0x03, // Stream: 3
+ 0x00, 0x00, 0x00, 0x08, // Error: CANCEL
+ };
+ // frame-format on
+
+ testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+
+ deframer_.set_visitor(&visitor);
+
+ testing::InSequence s;
+ EXPECT_CALL(visitor, OnHeaders(1, false, 0, 0, false, false, true));
+ EXPECT_CALL(visitor, OnHeaderFrameStart(1));
+ EXPECT_CALL(visitor, OnHeaderFrameEnd(1));
+ EXPECT_CALL(visitor, OnDataFrameHeader(1, 12, false));
+ EXPECT_CALL(visitor, OnStreamFrameData(1, _, 12));
+ EXPECT_CALL(visitor, OnHeaders(3, true, 131, 0, false, false, true));
+ EXPECT_CALL(visitor, OnHeaderFrameStart(3));
+ EXPECT_CALL(visitor, OnHeaderFrameEnd(3));
+ EXPECT_CALL(visitor, OnDataFrameHeader(3, 8, false))
+ .WillOnce(
+ testing::InvokeWithoutArgs([this]() { deframer_.StopProcessing(); }));
+ // Remaining frames are not processed due to the error.
+ EXPECT_CALL(
+ visitor,
+ OnError(http2::Http2DecoderAdapter::SpdyFramerError::SPDY_STOP_PROCESSING,
+ "Ignoring further events on this connection."));
+
+ size_t processed = deframer_.ProcessInput(
+ reinterpret_cast<const char*>(kH2Input), sizeof(kH2Input));
+ EXPECT_LT(processed, sizeof(kH2Input));
+}
+
// Test that the FIN flag on a data frame signifies EOF.
TEST_P(SpdyFramerTest, FinOnDataFrame) {
// Send HEADERS frames with END_HEADERS set.
@@ -2315,10 +2401,10 @@ TEST_P(SpdyFramerTest, CreateContinuationUncompressed) {
Http2HeaderBlock header_block;
header_block["bar"] = "foo";
header_block["foo"] = "bar";
- auto buffer = std::make_unique<std::string>();
+ std::string buffer;
HpackEncoder encoder;
encoder.DisableCompression();
- encoder.EncodeHeaderSet(header_block, buffer.get());
+ encoder.EncodeHeaderSet(header_block, &buffer);
SpdyContinuationIR continuation(/* stream_id = */ 42);
continuation.take_encoding(std::move(buffer));
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc
index b5790470e02..2190c6bbdad 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc
@@ -11,7 +11,6 @@
#include "absl/strings/str_cat.h"
#include "common/platform/api/quiche_logging.h"
-#include "spdy/platform/api/spdy_estimate_memory_usage.h"
namespace spdy {
namespace {
@@ -299,12 +298,6 @@ void Http2HeaderBlock::AppendValueOrAddHeader(const absl::string_view key,
iter->second.Append(storage_.Write(value));
}
-size_t Http2HeaderBlock::EstimateMemoryUsage() const {
- // TODO(xunjieli): https://crbug.com/669108. Also include |map_| when EMU()
- // supports linked_hash_map.
- return SpdyEstimateMemoryUsage(storage_);
-}
-
void Http2HeaderBlock::AppendHeader(const absl::string_view key,
const absl::string_view value) {
auto backed_key = WriteKey(key);
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h
index 46584191461..c3c5bad7205 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h
@@ -20,8 +20,8 @@
#include "absl/strings/string_view.h"
#include "common/platform/api/quiche_export.h"
#include "common/platform/api/quiche_logging.h"
+#include "common/quiche_linked_hash_map.h"
#include "spdy/core/spdy_header_storage.h"
-#include "spdy/platform/api/spdy_containers.h"
namespace spdy {
@@ -108,10 +108,10 @@ class QUICHE_EXPORT_PRIVATE Http2HeaderBlock {
}
};
- typedef SpdyLinkedHashMap<absl::string_view,
- HeaderValue,
- StringPieceCaseHash,
- StringPieceCaseEqual>
+ typedef quiche::QuicheLinkedHashMap<absl::string_view,
+ HeaderValue,
+ StringPieceCaseHash,
+ StringPieceCaseEqual>
MapType;
public:
@@ -201,6 +201,7 @@ class QUICHE_EXPORT_PRIVATE Http2HeaderBlock {
const_iterator find(absl::string_view key) const {
return wrap_const_iterator(map_.find(key));
}
+ bool contains(absl::string_view key) const { return find(key) != end(); }
void erase(absl::string_view key);
// Clears both our MapType member and the memory used to hold headers.
@@ -262,9 +263,6 @@ class QUICHE_EXPORT_PRIVATE Http2HeaderBlock {
// Allows either lookup or mutation of the value associated with a key.
ABSL_MUST_USE_RESULT ValueProxy operator[](const absl::string_view key);
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const;
-
size_t TotalBytesUsed() const { return key_size_ + value_size_; }
private:
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block_test.cc
index 2435ea9c043..0767dc9e875 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block_test.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block_test.cc
@@ -33,6 +33,7 @@ TEST(Http2HeaderBlockTest, EmptyBlock) {
EXPECT_TRUE(block.empty());
EXPECT_EQ(0u, block.size());
EXPECT_EQ(block.end(), block.find("foo"));
+ EXPECT_FALSE(block.contains("foo"));
EXPECT_TRUE(block.end() == block.begin());
// Should have no effect.
@@ -83,6 +84,7 @@ TEST(Http2HeaderBlockTest, AddHeaders) {
std::string qux("qux");
EXPECT_EQ("qux2", block[qux]);
ASSERT_NE(block.end(), block.find("key"));
+ ASSERT_TRUE(block.contains("key"));
EXPECT_EQ(Pair("key", "value"), *block.find("key"));
block.erase("key");
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_storage.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_storage.h
index 24c64eeae87..54267db51a4 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_storage.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_storage.h
@@ -43,8 +43,6 @@ class QUICHE_EXPORT_PRIVATE SpdyHeaderStorage {
size_t bytes_allocated() const { return arena_.status().bytes_allocated(); }
- size_t EstimateMemoryUsage() const { return bytes_allocated(); }
-
private:
SpdySimpleArena arena_;
};
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_intrusive_list.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_intrusive_list.h
index 2d9f749f7fa..3af20538578 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_intrusive_list.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_intrusive_list.h
@@ -267,9 +267,10 @@ template <typename T, typename ListID = void> class SpdyIntrusiveList {
public:
typedef std::iterator<std::bidirectional_iterator_tag, QualifiedT> base;
- iterator_impl() : link_(nullptr) {}
+ iterator_impl() = default;
iterator_impl(QualifiedLinkT* link) : link_(link) {}
- iterator_impl(const iterator_impl& x) : link_(x.link_) {}
+ iterator_impl(const iterator_impl& x) = default;
+ iterator_impl& operator=(const iterator_impl& x) = default;
// Allow converting and comparing across iterators where the pointer
// assignment and comparisons (respectively) are allowed.
@@ -310,7 +311,7 @@ template <typename T, typename ListID = void> class SpdyIntrusiveList {
// Ensure iterators can access other iterators node directly.
template <typename U, typename V> friend class iterator_impl;
- QualifiedLinkT* link_;
+ QualifiedLinkT* link_ = nullptr;
};
// This bare link acts as the sentinel node.
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc
index 4bde56a29d7..12a716e486c 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc
@@ -446,7 +446,6 @@ size_t SpdyGoAwayIR::size() const {
SpdyContinuationIR::SpdyContinuationIR(SpdyStreamId stream_id)
: SpdyFrameIR(stream_id), end_headers_(false) {
- encoding_ = std::make_unique<std::string>();
}
SpdyContinuationIR::~SpdyContinuationIR() = default;
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h
index 16d6b9a7ecd..eafb845883a 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h
@@ -260,7 +260,7 @@ QUICHE_EXPORT_PRIVATE bool IsValidHTTP2FrameStreamId(
SpdyFrameType frame_type_field);
// Serialize |frame_type| to string for logging/debugging.
-const char* FrameTypeToString(SpdyFrameType frame_type);
+QUICHE_EXPORT_PRIVATE const char* FrameTypeToString(SpdyFrameType frame_type);
// If |wire_setting_id| is the on-the-wire representation of a defined SETTINGS
// parameter, parse it to |*setting_id| and return true.
@@ -328,7 +328,7 @@ const int32_t kInitialStreamWindowSize = 64 * 1024 - 1;
// Initial window size for a session in bytes.
const int32_t kInitialSessionWindowSize = 64 * 1024 - 1;
// The NPN string for HTTP2, "h2".
-extern const char* const kHttp2Npn;
+QUICHE_EXPORT_PRIVATE extern const char* const kHttp2Npn;
// An estimate size of the HPACK overhead for each header field. 1 bytes for
// indexed literal, 1 bytes for key literal and length encoding, and 2 bytes for
// value literal and length encoding.
@@ -351,7 +351,7 @@ QUICHE_EXPORT_PRIVATE size_t GetNumberRequiredContinuationFrames(size_t size);
// exclusive bit}. Templated to allow for use by QUIC code; SPDY and HTTP/2
// code should use the concrete type instantiation SpdyStreamPrecedence.
template <typename StreamIdType>
-class StreamPrecedence {
+class QUICHE_EXPORT_PRIVATE StreamPrecedence {
public:
// Constructs instance that is a SPDY 3.x priority. Clamps priority value to
// the valid range [0, 7].
@@ -427,7 +427,7 @@ class StreamPrecedence {
}
private:
- struct Http2StreamDependency {
+ struct QUICHE_EXPORT_PRIVATE Http2StreamDependency {
StreamIdType parent_id;
int weight;
bool is_exclusive;
@@ -827,14 +827,12 @@ class QUICHE_EXPORT_PRIVATE SpdyContinuationIR : public SpdyFrameIR {
bool end_headers() const { return end_headers_; }
void set_end_headers(bool end_headers) { end_headers_ = end_headers; }
- const std::string& encoding() const { return *encoding_; }
- void take_encoding(std::unique_ptr<std::string> encoding) {
- encoding_ = std::move(encoding);
- }
+ const std::string& encoding() const { return encoding_; }
+ void take_encoding(std::string encoding) { encoding_ = std::move(encoding); }
size_t size() const override;
private:
- std::unique_ptr<std::string> encoding_;
+ std::string encoding_;
bool end_headers_;
};
@@ -920,7 +918,7 @@ class QUICHE_EXPORT_PRIVATE SpdyPriorityUpdateIR : public SpdyFrameIR {
std::string priority_field_value_;
};
-struct AcceptChOriginValuePair {
+struct QUICHE_EXPORT_PRIVATE AcceptChOriginValuePair {
std::string origin;
std::string value;
bool operator==(const AcceptChOriginValuePair& rhs) const {
@@ -1061,9 +1059,6 @@ class QUICHE_EXPORT_PRIVATE SpdySerializedFrame {
return buffer;
}
- // Returns the estimate of dynamically allocated memory in bytes.
- size_t EstimateMemoryUsage() const { return owns_buffer_ ? size_ : 0; }
-
protected:
char* frame_;
diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_containers.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_containers.h
deleted file mode 100644
index 2adf3f4b87a..00000000000
--- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_containers.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2018 The Chromium 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 QUICHE_SPDY_PLATFORM_API_SPDY_CONTAINERS_H_
-#define QUICHE_SPDY_PLATFORM_API_SPDY_CONTAINERS_H_
-
-#include "net/spdy/platform/impl/spdy_containers_impl.h"
-
-namespace spdy {
-
-// A map which offers insertion-ordered iteration.
-template <typename Key, typename Value, typename Hash, typename Eq>
-using SpdyLinkedHashMap = SpdyLinkedHashMapImpl<Key, Value, Hash, Eq>;
-
-} // namespace spdy
-
-#endif // QUICHE_SPDY_PLATFORM_API_SPDY_CONTAINERS_H_
diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h
deleted file mode 100644
index 4e22d1eabb0..00000000000
--- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The Chromium 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 QUICHE_SPDY_PLATFORM_API_SPDY_ESTIMATE_MEMORY_USAGE_H_
-#define QUICHE_SPDY_PLATFORM_API_SPDY_ESTIMATE_MEMORY_USAGE_H_
-
-#include <cstddef>
-
-#include "quiche_platform_impl/quiche_estimate_memory_usage_impl.h"
-
-namespace spdy {
-
-template <class T>
-size_t SpdyEstimateMemoryUsage(const T& object) {
- return quiche::QuicheEstimateMemoryUsageImpl(object);
-}
-
-} // namespace spdy
-
-#endif // QUICHE_SPDY_PLATFORM_API_SPDY_ESTIMATE_MEMORY_USAGE_H_
diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h
deleted file mode 100644
index 83660bab06f..00000000000
--- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The Chromium 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 QUICHE_SPDY_PLATFORM_API_SPDY_STRING_UTILS_H_
-#define QUICHE_SPDY_PLATFORM_API_SPDY_STRING_UTILS_H_
-
-#include <string>
-
-#include "absl/strings/string_view.h"
-#include "net/spdy/platform/impl/spdy_string_utils_impl.h"
-
-namespace spdy {
-
-inline std::string SpdyHexDump(absl::string_view data) {
- return SpdyHexDumpImpl(data);
-}
-
-} // namespace spdy
-
-#endif // QUICHE_SPDY_PLATFORM_API_SPDY_STRING_UTILS_H_